Analysis of call graphs ranges from easy to ugly:
- Virtual calls (easy?): Do you show just the statically-known class or include all derived classes?
Let them choose. If it's an interface, then you indicate as such (icons), if I know X is going to be a Q then I'll want to be able to select Q.F() instead of B.F(). I should be able to easily change my choice later though, in case I was wrong or need to trace the flow through two or more different sets of derived methods. If we're doing this in a "view" based format, you could have separate chunks of the view for each virtual method I'm tracking.
- Callbacks (easy? hard?): What's the ideal representation? Sequence diagrams? Something else?
Probably sequence graphs if you're familiar with them. Alternatively: Callstack.
- Functors/delegates/lambdas/etc: No idea how you'd analyze or represent these nicely.
Same idea as Callbacks.
How do you render the call graph? I wouldn't think that anything less than a Graphviz-style layout would be sufficient, but I've done call graphs with Graphviz, and real-world graphs tend to become completely unreadable very quickly. The density of calls is usually just too high to visualize cleanly.
The real problem I see with this is that you can never eliminate the shit you don't care about, and at the same time bookmark the stuff you DO care about. Ideally I would want to be able to, in a method, right click on a function and say "add to view." At this point I can now see the method in my view alongside all the other methods. If this method is called by (either directly or through a potential derived instance) then I would like to see that relationship, either due to the implicit ordering of the method with relation to its callee, or by having the ability to show and hide a relation graph that might be a side part of the editor window.
What might be really badass is if you could trace data flow in the graph. For example, let's say we have a call from method A to method B, where one of the arguments is passed straight through without change. It might be nice to indicate that in the graph layout:
| Namespace | ========> | Namespace2 |
| Class | | Class |
| Method | | Method |
| Arg1 | /---> | Arg1 |
| Arg2 | ---/
This is doable currently, just needs a bit of static analysis. You can do this in the latest C# CTP for 6.0 and roslyn.
I like the tag-and-view idea. I was thinking that in C#, you could easily adapt the #region/#endregion directives to function as tagging constructs:
#region tags: spells, attacks
// ...whatever code you want to put here...
#endregion
You might want to automatically assign a GUID to each region as well, so that you could cherry-pick regions instead of being forced to get every region with a specific set of tags.
#region guid: {1BD2771D-D38F-4AB1-8076-8A10D5EBDE51} tags: spells, attacks
// ...whatever code you want to put here...
#endregion
Your view files would then just list guid and tag expressions:
guid: {1BD2771D-D38F-4AB1-8076-8A10D5EBDE51}
tags: spells && attacks
(In my example, that region matches the guid AND the tags, but this would obviously not be necessary)
Hmm, you could use regions for this yes. This would result in the data not being available post-compile time though. Using attributes is another option...
[Tag("fireball","spells","attacks")]
[Tag("fireball, spells, attacks")]
[Tag("fireball"), Tag("spells"), Tag("attacks")]
This would result in the data being available after compilation for use by external automation tools...