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!
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