What's wrong
GeneratorBase projects each AdditionalText into new MetadataFile(...) and then Collects the results (GeneratorBase.cs:60-66). MetadataFile (MetadataFile.cs:18) is a class with no Equals or GetHashCode. Roslyn compares incremental step outputs with default equality, so the check is effectively reference identity. A new AdditionalText with byte-identical contents therefore always produces a "modified" step, and the whole Generate runs again: every file is deserialized and every source re-emitted.
In an IDE a new AdditionalText instance is created in several ordinary cases, such as an edit followed by an undo, or a save with no content change. The incremental pipeline does not save any work in those cases.
GeneratorHarness.ReusesCachedOutputOnRerun does not catch this. It reuses the same AdditionalText instances and changes only the compilation.
Repro
- Create a driver with
trackIncrementalGeneratorSteps: true and the test project's ThingsGenerator, and run it once.
- Call
driver.ReplaceAdditionalText(t1, new MemText(samePath, sameText)).
- Run it again against the same compilation.
- Observed: the tracked output step's reason is
Modified.
- Expected:
Cached or Unchanged.
Suggested fix
- Implement
IEquatable<MetadataFile> using ordinal comparison of FileName, the path and Text (or SourceText.ContentEquals), with a matching GetHashCode.
- Extend
GeneratorHarness with a check that swaps in equal-content AdditionalTexts and asserts the outputs are cached, so a regression is caught.
What's wrong
GeneratorBaseprojects each AdditionalText intonew MetadataFile(...)and thenCollects the results (GeneratorBase.cs:60-66).MetadataFile(MetadataFile.cs:18) is a class with noEqualsorGetHashCode. Roslyn compares incremental step outputs with default equality, so the check is effectively reference identity. A newAdditionalTextwith byte-identical contents therefore always produces a "modified" step, and the wholeGenerateruns again: every file is deserialized and every source re-emitted.In an IDE a new
AdditionalTextinstance is created in several ordinary cases, such as an edit followed by an undo, or a save with no content change. The incremental pipeline does not save any work in those cases.GeneratorHarness.ReusesCachedOutputOnRerundoes not catch this. It reuses the same AdditionalText instances and changes only the compilation.Repro
trackIncrementalGeneratorSteps: trueand the test project'sThingsGenerator, and run it once.driver.ReplaceAdditionalText(t1, new MemText(samePath, sameText)).Modified.CachedorUnchanged.Suggested fix
IEquatable<MetadataFile>using ordinal comparison ofFileName, the path andText(orSourceText.ContentEquals), with a matchingGetHashCode.GeneratorHarnesswith a check that swaps in equal-content AdditionalTexts and asserts the outputs are cached, so a regression is caught.