Should Call Hierarchy display compiler-generated member calls?

[A quick reminder, Call Hierarchy is a new IDE feature in VS 2010]

In the comments to the previous post, a reader is asking:

But why are the query operator method calls ignored by the Call Hierarchy?

var r = from i in new[] { 1, 2, 3 }

       where i > 1

       select i;

The code snippet above calls Enumerable.Where and Enumerable.Select, and I reacon they should go into Call Hierarchy, which is not the case of the current beta. Any hint on this?

This is actually a good question. We thought about this issue in the feature design meetings and the reasoning behind the decision not to show the LINQ methods was as follows. The foreach statement also calls GetEnumerator and MoveNext, lock calls Monitor.TryEnter (or something similar??), not to mention other places where compiler-generated code calls members (e.g. the yield return state machine, lambdas, etc). The question is simple: where do we stop? In other words, how deep do we go down the abstraction ladder?

This also applies to things like calling += on events, which actually calls Delegate.Combine, calling a delegate calls the Invoke method, etc. etc. We decided that we will only show a member call in Call Hierarchy if the compiler actually sees the member’s symbol in the source code. Find All References also follows this pattern, e.g. if you look for Point, you will have two references in Point p = new Point(); and only one reference in var p = new Point(); – since the symbol doesn’t show up in your source code, we don’t mention it. This might be misleading actually, also when you’re looking for calls to a method, we won’t show you places where you create a delegate that points to this method (or method group).

Do you think this reasoning is OK or would you like us to change the feature’s behavior? If yes, how exactly should it behave? Also keep in mind that changing this behavior at this point will be pretty costly (i.e. I will have to retest everything!). Not even to mention that our dev will have to change the implementation :)


Comments (3)

  1. raven says:

    Oops, I had a typo on "reckon"…

    IMHO, if this feature were implemented before the days of extension methods, it’s perfectly reasonable to hide compiler-generated details. But now you can add arbitrary methods to classes, say if someone added GetEnumerator to System.Object, then every object would be valid to fit in a foreach loop’s collection argument. And when that happens, the IDE doesn’t tell you anything more than what you’ve got on the surface of your source code.

    It may still be reasonable to keep the Call Hierarchy feature as it is in Beta 1, but if there could be any improvements within the editor itself, things might have been better. Say when you hover over the positional keyword "select" in a query expression, if it pops up a tooltip telling you which method it’s actually calling, it’s gonna be much more helpful.

  2. Kirill Osenkov says:

    Or we might go the Reflector way in the future and display member calls from IL instead of the source code.

  3. raven says:

    > Or we might go the Reflector way in the future and display member calls from IL instead of the source code.

    A parser makes guesses when it encounters syntax errors (but the parser used in the editor is different from the one used in the compiler, in the case of Visual C#, right? whatever). If somehow the parser used in the compiler has made all guesses correctly, and the code after resync does compile, then going the Reflector way is a great place to be.

    But what if the code still doesn’t compile after parser’s resync? Is it that Call Hierarchy works only when the source code actually compiles? If that’s the case, implementing it the Reflector way seem to be even easier than the way it is now.