jqassistant

How to detect / resolve super method invocations with jqassistant


I am using jqassistant 1.8.0 to detect super method calls in a class hierarchy. It seems some method invocations within a class hierarchy are missing, even after applying the classpath:Resolve concept. Minimal set-up consist of three classes:

public class SuperClass {

  void superMethod() {
  }

}

public class SubClass1 extends SuperClass {

  void subMethod1() {
    super.superMethod();
  }

}

public class SubClass2 extends SuperClass {

  void subMethod2() {
    superMethod();
  }
}

Both subMethod1 and subMethod2 are calling the same method of SuperClass, but only the one with the explicit "super." invocation has the :INVOKES relationship.

MATCH
  (who)-[:INVOKES]->(m)<-[:DECLARES]-(:Type {fqn: 'SuperClass'})
Return who

In the database two nodes with a signature "void superMethod()" exist, one declared by the SuperClass and one declared by SubClass2. It seems there is some step missing that links the two identical methods.

Is there another built-in concept (apart from classpath:Resolve) resolving this or is this not covered by the java-plugin? Thanks!


Solution

  • There's a slight difference between calling super.superMethod() and superMethod():

    The first instructs the JVM to use the method from the super class, the second relies on resolving the method at runtime, there might be an implementation in SubClass2 (virtual invocation):

    For this case the created graph contains an invocation to a method placeholder in SubClass2 (only having the signature property):

    (method)-[:INVOKES]->(:Method{signature:"void superMethod()"})<-[:DECLARES]-(:Type{name:"SubClass2"}) 
    

    There's a concept java:MethodOverrides that should create an OVERRIDES relation to the super class method but sadly it does not work in this case (up to jQA 1.8.0). There's already a fix applied which will come with jQA 1.9.0:

                MATCH
                  (type:Type)-[:DECLARES]->(method:Method),
                  (superType:Type)-[:DECLARES]->(superMethod:Method),
                  path=shortestPath((type)-[:EXTENDS|IMPLEMENTS*]->(superType))
                WHERE
                  method.signature = superMethod.signature
                  and superMethod.visibility <> "private"
                WITH
                  type, method, superType, superMethod, length(path) as depth
                ORDER BY
                  depth asc
                WITH
                  method, head(collect(superMethod)) as overriddenMethod
                MERGE
                  (method)-[:OVERRIDES]->(overriddenMethod)
                RETURN
                  count(*) as OverriddenMethods
    

    Using this one you should be able to exeute the following query:

    MATCH
      (:Type{name:"SuperClass"})-[:DECLARES]->(m:Method{name:"superMethod"}),
      (who)-[:INVOKES]->(:Method)-[:OVERRIDES*0..1]->(m) // catching direct or virtual invocations
    RETURN
      who