Fix incremental source generator caching with proper equality#114
Merged
jonathanpeppers merged 3 commits intoMay 3, 2026
Merged
Conversation
GenerationInfo and NetworkRequest were plain classes without IEquatable<T>, causing the incremental pipeline to always regenerate on every build since ReferenceEquals was used for cache comparison (always false for new objects). Changes: - Implement IEquatable<T> on GenerationInfo and NetworkRequest - Replace HashSet<string> with sorted ImmutableArray<string> for stable value equality on FallbackTypes/FallbackTypesWithComparer - Replace NetworkRequest[] with ImmutableArray<NetworkRequest> - Add RunGeneratorTwice helper to SourceGeneratorDriver for incremental tests - Add 4 tests verifying cache hits on same/unrelated changes and cache misses when attributes actually change Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace ad-hoc hash * 397 ^ value patterns with a proper HashCode polyfill based on the xxHash32 implementation from dotnet/runtime. This provides better hash distribution via HashCode.Combine and the Add/ToHashCode pattern. The polyfill is internal to the generator assembly, avoiding the need to redistribute a NuGet dependency (Microsoft.Bcl.HashCode) in the analyzer package. Uses a fixed seed since randomization is unnecessary for incremental generator caching. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Improves Roslyn incremental source generator caching so unchanged inputs don’t trigger full regeneration, by giving the generator’s pipeline model types stable value equality and adding tests that verify incremental cache behavior via tracked step reasons.
Changes:
- Implement
IEquatable<T>/GetHashCode()for generator pipeline model types and switch collections toImmutableArray(with deterministic ordering where needed). - Add an internal
HashCode(xxHash32-based) polyfill fornetstandard2.0. - Add incremental caching tests and a helper to run the generator twice with
trackIncrementalGeneratorSteps: true.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| SortingNetworks.Generators/HashCode.cs | Adds an internal HashCode polyfill used for stable hashing in generator model types. |
| SortingNetworks.Generators/SortingNetworkGenerator.cs | Adds structural equality to pipeline model types and switches to ImmutableArray for cache-friendly comparisons. |
| SortingNetworks.Tests/SourceGeneratorDriver.cs | Adds RunGeneratorTwice helper to enable incremental-step reason assertions. |
| SortingNetworks.Tests/GeneratorTests.cs | Adds 4 tests validating Cached vs Modified incremental behavior. |
…e unnecessary pragma - Tests now query TrackedOutputSteps["SourceOutput"] specifically instead of flattening all steps, preventing false positives from vacuous Assert.All and brittleness if new tracked steps are added later. - Assert.NotEmpty added before Assert.All to guard against vacuous passes. - Removed unnecessary #pragma warning disable CS0809 from HashCode polyfill. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The incremental source generator was effectively non-incremental -- it regenerated all output on every build even when nothing relevant changed. The root cause was that the pipeline model types (
GenerationInfo,NetworkRequest) lacked value equality, so the Roslyn incremental cache always missed (falling back toReferenceEquals, which is alwaysfalsefor newly constructed objects).Approach
Implement
IEquatable<T>on both model types and switch to collection types with structural equality:NetworkRequest[]->ImmutableArray<NetworkRequest>(value equality built-in)HashSet<string>-> sortedImmutableArray<string>(deterministic ordering for stable comparison)GetHashCode()uses an internal xxHash32-basedHashCodepolyfill ported fromdotnet/runtime, sinceSystem.HashCodeis not available onnetstandard2.0and addingMicrosoft.Bcl.HashCodewould require bundling the DLL in the analyzer packageTesting
Added 4 incremental caching tests that use
trackIncrementalGeneratorSteps: trueandIncrementalStepRunReasonto verify:CachedCachedModifiedModifiedAll 459 tests pass.
Files changed
SortingNetworks.Generators/HashCode.cs-- New internal xxHash32 polyfill from dotnet/runtime (BSD 2-Clause, MIT licensed)SortingNetworks.Generators/SortingNetworkGenerator.cs--IEquatable<T>on model types,ImmutableArraycollections,HashCodeusageSortingNetworks.Tests/SourceGeneratorDriver.cs--RunGeneratorTwicehelper for incremental cache testingSortingNetworks.Tests/GeneratorTests.cs-- 4 new incremental caching tests