How do I find all classes that derive from specified class or interface efficiently in Incremental Generator? #71402
-
I have several tasks that require my incremental generator to know if a class is derived from a certain class, or, in another case, an interface. I've tried a number of approaches, but I can't see how to do this efficiently, so maybe I'm missing some idea, or I should go about the whole task differently? Requirements:So, let's talk about my requirements first. I have two main tasks I need to solve right now:
I think that both cases can be solved more efficiently by requiring the end-users to mark all derived classes with attributes, but this seems inelegant to me, and I don't want to change the existing codebase. So, I'm looking for a way to quickly check if a type derives from another type or interface. Naive approach:At the first glance, the solution is clear: I can walk inheritance tree using Details: UpdatablesCodeGenerator.md (don't mind the Build method - it makes use of my own templating engine, which isn't related to the question, and please excuse the amount of debugging code - I'm in process of learning incremental source generators). Fast, but broken approach:It is very unfortunate that SemanticModel only contains backward links to parent types - otherwise, I could easily query a class, or an interface for a list of its descendants. So let's build inheritance graph ourselves! The important thing here is to avoid rebuilding it entirely when only a small portion of code changes. This is the pipeline I came up with:
BuildTree generates a tree, by creating a Here, the first problem comes: I desperately want to cache the tree, and only update parts of it that actually change, but Anyway, I checked this approach on my project, and it works - the first tree rebuild takes about 3 seconds, and small changes to it take about 40-100ms. Fast! The second problem is that this approach is fundamentally broken: it tracks changes only to syntax, by comparing Details: InheritanceTreeBasedGenerator.md Slightly better, but much slower approach:I can fix the second problem if our Transform step would take semantic meaning of symbols into account. I can do this by making it return Instead, I can iterate over type declaration's Details: InheritanceTreeBasedGeneratorSym.md The question:So. It seems to me that the task - finding all descendants of a type - should be a relatively common one, yet I can't find any solutions to it on the net, aside from naive ones, which don't work well on large codebases. What am I missing? Is there a way to do this efficiently, or should I bite the bullet and just mark every class of interest with an attribute (maybe with an Analyzer that checks if a class derived from a marked class is not marked and adds a diagnostic, to avoid errors)? |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
There is no way to do this efficiently
Yes. Do this.
Yes. Do this. |
Beta Was this translation helpful? Give feedback.
-
It's also intentional. We have no magical easy to do better here either. Any sort of edit could entirely change the inheritance tree, and we wouldn't know without doing all the analysis work for each type. So SemanticModel (and symbols), contain the operations that can be done cheaply. Reverse transitive maps will be expensive and should be avoided as part of any design. |
Beta Was this translation helpful? Give feedback.
There is no way to do this efficiently
Yes. Do this.
Yes. Do this.