This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.
Example: public class Caller { void method1() { new Derived1().foo(); } void method2() { new Derived2().foo(); } } abstract class Base { void foo() { bar(); } abstract void bar(); } class Derived1 extends Base { @Override void bar() { } } class Derived2 extends Base { @Override void bar() { } } Invoking Call Hierarchy on Derived1#bar() results in a tree with both Caller#method1() and Caller#method2() (via Base#foo()), although it is trivially clear that only method1() can result in calling Derived1#bar(). Apparently Call Hierarchy doesn't use its knowledge of the type of 'this' for subsequent method invocations on the current instance.
Note that this can be generalized to any method arguments (as 'this' can be considered to be an implicit method argument). For example: public class Caller { void method1() { foo(new Derived1()); } void method2() { foo(new Derived2()); } void foo(Base base) { base.bar(); } } abstract class Base { abstract void bar(); } class Derived1 extends Base { @Override void bar() { } } class Derived2 extends Base { @Override void bar() { } } When tracing calls to Derived1#bar(), it is clear that the method argument of Caller#foo(Base) is really a Derived1 object, and therefore only method1() is a caller, but not method2().
The problem is actually worse than I thought. Consider the following example: public class Caller { void method1() { new Derived1().bar(); } void method2() { new Derived2().bar(); } } abstract class Base { abstract void bar(); } class Derived1 extends Base { void bar() { new Derived2().bar(); } } class Derived2 extends Base { void baz() { } void bar() { baz(); } } Invoking Call hierarchy on Derived2#baz() results in the following hierarchy: baz() :: Derived2 `- bar() :: Derived2 |- method1() :: Caller |- method2() :: Caller `- bar() :: Derived1 Besides Derived2#bar() being calle by method1() is not true, there is no indication that Derived1#bar() (last line) is being called by method1(). Apparently Call Hierarchy is mixing up Derived1#bar() and Derived2#bar() because they both implement the same abstract method. Commenting out the abstract bar() method in Base (or removing the Base class alltogether) results in the correct call hierarchy: baz() :: Derived2 `- bar() :: Derived2 |- method2() :: Caller `- bar() :: Derived1 `- method1() :: Caller In this example, the result should not depend on whether Base#bar() exists or not, because none of the calls are made via Base.
Note that while new Derived1().foo() can be analyzed so it definitely does not cll Derived2#foo, a call through another member method is most probably out of scope of the call hierarchy
(In reply to Svata Dedic from comment #3) > Note that while new Derived1().foo() can be analyzed so it definitely does > not cll Derived2#foo, a call through another member method is most probably > out of scope of the call hierarchy What I would expect is that Call Hierarchy tracks the most-derived type of the method arguments (including 'this') that it encounters when tracing a call path. Example: class Base { void callee() { } } class Derived1 extends Base { void callee() { } } class Derived2 extends Base { void callee() { } } class Caller { void caller1(Derived1 d) { caller(d); } void caller2(Derived2 d) { caller(d); } void intermediate(Base b) { b.callee(); } } Consider the following two call paths: Caller#caller1 -> Caller#intermediate -> Derived1#callee Caller#caller2 -> Caller#intermediate -> Derived2#callee When tracing these paths (from caller to callee or vice versa), one has to track that the argument b of Caller#intermediate(Base) is known to be a Derived1 for one path and a Derived2 for the other path. That is, the two nodes for Caller#intermediate have to be annotated with the more specific type of b that is known along each path. If that information is not tracked, then the two paths will be conflated into one graph: Caller#caller1 -. ,-> Derived1#callee \ / }-> Caller#intermediate -{ / \ Caller#caller2 -’ `-> Derived2#callee ...yielding the following two additional paths although these can never happen: Caller#caller1 -> Caller#intermediate -> Derived2#callee Caller#caller2 -> Caller#intermediate -> Derived1#callee Implementing that logic for a chain of more than one intermediate call should not be more difficult than implementing it for a single intermediate call.