Intellij - Dealing with call hierarchies and anonymous classes

Long story short, I'm working on an Intellij plugin (Intellij 14, CentOS) that will extend the call hierarchy functionality. It will basically search for all methods that contain a certain criteria, compute the call hierarchy for all of them, and then export that to a JSON file with the inclusion of some additional data that you won't find in the standard call hierarchy. I have this working for the most part. However, I've hit an irritating snag that seems to be a limitation with Intellij itself.

Let's say that the project has a simple delegate interface with one method, dubbed "IMyInterface". This interface has maybe ten concrete implementations throughout the project. However, there are cases where an anonymous implementation is created and passed along the call chain. Intellij can't seem to properly identify it, and instead dumps ALL implementations (along with the correct anonymous one) into the call hierarchy! This makes it look like all of them are being called, which screws up what I'm trying to do.

Q&D code example if that wasn't clear:

interface IMyInterface<T> {
    public T doStuff();
}
public Thingie doSomeThings(){
    return someClass.useThis(new IMyInterface<Thingie>(){ ... });
}

Is there any way to correct this or, at the very least, somehow detect when it happens as I'm computing the hierarchy so I can catch it and try some extra legwork to correct the issue? I'm using the following code to get the hierarchy:

CalleeMethodsTreeStructure callees = 
    new CalleeMethodsTreeStructure(project, psiMethod, scope);
Object[] children = callees.getChildElements(callees.getBaseDescriptor());
for (Object obj : children){
    CallHierarchyNodeDescriptor c = (CallHierarchyNodeDescriptor) obj;
    PsiMethod m = (PsiMethod) c.getEnclosingElement();
    //Process method, recurse if necessary
}
1 comment
Comment actions Permalink

After doing some digging, I've found more odd behavior. Let's say I have InterfaceA. InterfaceB and InterfaceC both implement InterfaceA. Each of them have a single implementing class. Now, let's say I do this in code:

InterfaceB b = getBObj();
InterfaceC c = getCObj();
b.doStuff();
c.doStuff();

One would think that Intellij would show the call hierarchy with InterfaceB.doStuff() and InterfaceC.doStuff(). Instead, it shows ONE invocation of InterfaceA.doStuff()! This makes no sense. It's choosing the most broad possible type when the more specific type is RIGHT THERE IN THE CODE. And it's showing a single invocation of doStuff() when we have two distinct objects, each with distinct types, performing that method.

I'm not sure why Intellij isn't making these connections. Maybe it's a performance compromise. In any case, it feels like I'll have to do a lot of reimplementation to get the sort of call hierarchy I need, and the documentation of Intellij's API is not exactly stellar. It doesn't really exist, actually.

0

Please sign in to leave a comment.