Skip to content

Commit aafd6eb

Browse files
authored
Add cookbook section for avoiding inheritance (dotnet#79276)
* Add cookbook section for avoiding inheritance Adds a section to the incremental generators cookbook on why users should avoid inheritance, that goes over a few possible scenarios and how they can hurt IDE responsiveness. * Update ToC * Add a small note on preferring FAWMN.
1 parent 7283fc6 commit aafd6eb

File tree

1 file changed

+23
-0
lines changed

1 file changed

+23
-0
lines changed

docs/features/incremental-generators.cookbook.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ of scope in the final design of the shipping feature.
2121
- [Pipeline model design](#pipeline-model-design)
2222
- [Use `ForAttributeWithMetadataName`](#use-forattributewithmetadataname)
2323
- [Use an indented text writer, not `SyntaxNode`s, for generation](#use-an-indented-text-writer-not-syntaxnodes-for-generation)
24+
- [Put `Microsoft.CodeAnalysis.EmbeddedAttribute` on generated marker types](#put-microsoftcodeanalysisembeddedattribute-on-generated-marker-types)
25+
- [Do not scan for types that indirectly implement interfaces, indirectly inherit from types, or are indirectly marked by an attribute from an interface or base type](#do-not-scan-for-types-that-indirectly-implement-interfaces-indirectly-inherit-from-types-or-are-indirectly-marked-by-an-attribute-from-an-interface-or-base-type)
2426
- [Designs](#designs)
2527
- [Generated class](#generated-class)
2628
- [Additional file transformation](#additional-file-transformation)
@@ -135,6 +137,27 @@ project, it will not include that type in lookup results. To ensure that `Micros
135137
Another option is to provide an assembly in your nuget package that defines your marker attributes, but this can be more difficult to author. We recommend the
136138
`EmbeddedAttribute` approach, unless you need to support versions of Roslyn lower than 4.14.
137139

140+
### Do not scan for types that indirectly implement interfaces, indirectly inherit from types, or are indirectly marked by an attribute from an interface or base type
141+
142+
Using an interface/base type marker can be a very tempting and natural fit for generators. However, scanning for these types of markers is _very_ expensive, and cannot
143+
be done incrementally. Doing so can have an outsized impact on IDE and command-line performance, even for fairly small consuming users. These scenarios are:
144+
145+
* A user implements an interface on `BaseModelType`, and then the generator looks all derived types from `BaseModelType`. Because the generator cannot know ahead of time
146+
what `BaseModelType` actually is, it means that the generator has to fetch `AllInterfaces` on every single type in the compilation so it can scan for the marker
147+
interface. This will end up occurring either on every keystroke or every file save, depending on what mode the user is running generators in; either one is disastrous
148+
for IDE performance, even when trying to optimize by scoping down the scanning to only types with a base list.
149+
* A user inherits from a generator-defined `BaseSerializerType`, and the generator looks for anything that inherits from that type, either directly or indirectly. Similar
150+
to the above scenario, the generator will need to scan all types with a base type in the entire compilation for the inherited `BaseSerializerType`, which will heavily
151+
impact IDE performance.
152+
* A generator looks among all base types/implemented interfaces for a type that is attributed with a generator's marker attribute. This is effectively either scenario 1
153+
or 2, just with a different search criteria.
154+
* A generator leaves its marker attribute unsealed, and expects users to be able to derive their own attributes from that marker, as a source of parameter customization.
155+
This has a couple of problems: first, every attributed type needs to be checked to see if the attribute inherits from the marker attribute. While not as performance
156+
impacting as the first three scenarios, this isn't great for performance. Second, and more importantly, there is no good way to retrieve any customizations from the
157+
inherited attribute. These attributes are not instantiated by the source generator, so any parameters passed to the `base()` constructor call or values that are assigned
158+
to any properties of the base attribute are not visible to the generator. Prefer using FAWMN-driven development here, and using an analyzer to inform the user if they
159+
need to inherit from some base class for your generator to work correctly.
160+
138161
## Designs
139162

140163
This section is broken down by user scenarios, with general solutions listed first, and more specific examples later on.

0 commit comments

Comments
 (0)