Skip to content

test: pin diagnostic-location safety across incremental updates#144

Merged
ANcpLua merged 2 commits into
mainfrom
test/incremental-diagnostic-location-regression
Jun 14, 2026
Merged

test: pin diagnostic-location safety across incremental updates#144
ANcpLua merged 2 commits into
mainfrom
test/incremental-diagnostic-location-regression

Conversation

@ANcpLua

@ANcpLua ANcpLua commented Jun 13, 2026

Copy link
Copy Markdown
Owner

Was

Regression test inspired by dotnet/roslyn#82113 (fixes roslyn#82032) — "Validate generator diagnostics after incremental updates".

That Roslyn fix is host-side: it re-validates every generator diagnostic's Location against the current compilation on every run, so a span captured against a longer tree can't survive a shrinking edit and crash. We can't apply that patch (we don't own GeneratorDriver), but we are already on the safe side of the bug: our diagnostics flow as value-equatable DiagnosticInfo/LocationInfo, which reconstruct an external-file location (Location.Create(path, span, lineSpan)SourceTree == null) at report time. The driver never range-validates external locations, so a shrink can neither crash the generator nor emit a stale span.

This PR pins that guarantee.

The test

Shrinking_Edit_Keeps_Diagnostic_Location_In_Range_Without_Crashing drives CSharpGeneratorDriver across a shrinking ReplaceSyntaxTree edit (long union root → tiny one, both reporting AL0301) and asserts on the re-reported diagnostic:

  • Precondition — the original span really would be out of range for the shrunk tree (otherwise the edit wouldn't exercise the bug class).
  • Location.IsInSource == false — it's an external location the driver never range-validates (this is the assertion that fails if LocationInfo.ToLocation() ever regresses to a source-tree-bound location).
  • Location.SourceSpan.End <= newTree.Length — the location stays in range after the edit.
  • The only generator diagnostic after the edit is AL0301 — no CS8785/crash diagnostic.

Bonus

  • Extracted a public GeneratorTestHelper.CreateCompilation(params string[]) primitive (reused by RunGenerator) so tests can drive the driver directly for incremental scenarios.
  • Declared the now-direct Microsoft.CodeAnalysis.CSharp dependency on the DU test project (the test constructs CSharpGeneratorDriver/CSharpSyntaxTree directly rather than relying on transitive flow).

Verification

  • dotnet build -c Debug0 warnings, 0 errors
  • dotnet test (DU project) → 9/9 passed

🤖 Generated with Claude Code

Mirrors the bug class fixed by dotnet/roslyn#82113 (issue #82032), from the
consumer side. A generator diagnostic whose location was captured against a
longer tree must not survive a shrinking incremental edit as an out-of-range
span. Our DiagnosticInfo/LocationInfo reconstruct an external-file location
(never source-tree-bound), so the driver never range-validates it and a shrink
can neither crash the generator nor report a stale span.

The test drives CSharpGeneratorDriver across a shrinking ReplaceSyntaxTree edit
and asserts the re-reported AL0301 location is external (IsInSource == false)
and in range of the new tree, with no generator-failure diagnostic. It fails if
LocationInfo.ToLocation() ever regresses to a source-tree-bound location.

Bonus: extract a public GeneratorTestHelper.CreateCompilation primitive (reused
by RunGenerator) so tests can drive the driver directly for incremental
scenarios, and declare the now-direct Microsoft.CodeAnalysis.CSharp dependency
on the DU test project.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2c8d2cee-be7c-4b69-b86a-25c7e9ad9d61

📥 Commits

Reviewing files that changed from the base of the PR and between 0f9c4d5 and 7210da2.

📒 Files selected for processing (1)
  • .github/workflows/nuget-publish.yml
📜 Recent review details
🧰 Additional context used
📓 Path-based instructions (2)
**/.github/workflows/**/*.{yml,yaml}

📄 CodeRabbit inference engine (Custom checks)

Fail when CI workflow/build/release files remove an existing validation job, remove concurrency from push/PR workflows, add --admin/force-push/bypass commands, increase token permissions beyond contents:read without inline rationale, expose secrets in command arguments/logs, change publish triggers to run outside tags or approved environments, or allow a previously required failing check to be ignored

Files:

  • .github/workflows/nuget-publish.yml
.github/**

⚙️ CodeRabbit configuration file

GitHub Actions workflows. Review for: action version pinning (use SHA not tags for third-party actions), proper secret handling (no secrets in logs, use GITHUB_TOKEN where possible), unnecessary workflow triggers, and job dependency correctness. Flag missing concurrency groups on push-triggered workflows. Ensure matrix strategies cover the supported .NET TFMs.

Files:

  • .github/workflows/nuget-publish.yml
🔇 Additional comments (1)
.github/workflows/nuget-publish.yml (1)

89-93: LGTM!


Summary by CodeRabbit

  • Tests

    • Added a regression test to ensure discriminated-union generator diagnostic spans remain valid after incremental edits that shrink the syntax tree.
  • Refactor

    • Improved generator test infrastructure by introducing a reusable compilation-creation helper for consistent, flexible generator-driven testing.
  • Chores

    • Updated CI to run dotnet test (Release) after building in the Linux/Windows build jobs, before packaging and publishing.

Walkthrough

Compilation creation is extracted into a reusable public helper to enable direct incremental generator driver invocation. A regression test then validates that AL0301 diagnostics remain valid after syntax-tree shrinking without crashing or reporting out-of-range locations. CI is gated on test passage.

Changes

Incremental Diagnostic Location Fix

Layer / File(s) Summary
Compilation helper extraction
src/ANcpLua.Roslyn.Utilities.Testing/GeneratorHelpers/GeneratorTestHelper.cs
New public CreateCompilation(params string[] sources) builds CSharpCompilation with standard references and dynamic library output; RunGenerator<TGenerator> refactored to call it instead of inlining.
Incremental diagnostic location regression test
tests/ANcpLua.Roslyn.Utilities.DiscriminatedUnion.Tests/ANcpLua.Roslyn.Utilities.DiscriminatedUnion.Tests.csproj, tests/ANcpLua.Roslyn.Utilities.DiscriminatedUnion.Tests/IncrementalDiagnosticLocationTests.cs
Add Microsoft.CodeAnalysis.CSharp dependency and implement IncrementalDiagnosticLocationTests with Shrinking_Edit_Keeps_Diagnostic_Location_In_Range_Without_Crashing method, validating AL0301 diagnostic survives tree replacement without source-location binding or range violations.
CI test execution gating
.github/workflows/nuget-publish.yml
Add dotnet test (Release, --no-build) to the build job after compilation, blocking artifact upload until tests pass.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

area:infra

🚥 Pre-merge checks | ✅ 8
✅ Passed checks (8 passed)
Check name Status Explanation
Title check ✅ Passed Title follows conventional commit format with 'test' prefix, clear scope omission is acceptable for cross-cutting test changes, and is 63 characters.
Description check ✅ Passed Description thoroughly documents regression test motivation, implementation assertions, extracted primitives, and verification results aligned with the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Otel Instrumentation Required ✅ Passed No injectable service classes added; PR only adds static test utilities and xUnit test classes—instrumentation not applicable.
No Unbounded Mcp Responses ✅ Passed No MCP tool definitions under src/qyl.mcp/ are modified in this PR; check applies only when such modifications exist.
Duckdb Backpressure On Write Paths ✅ Passed PR adds no new DuckDB write paths; changes are limited to C# code generation testing infrastructure (diagnostic location regression test, test helper extraction, CI test execution).
Cancellationtoken Threading ✅ Passed No new public async methods were added in src/**/*.cs; all new public methods (CreateCompilation, updated RunGenerator) are synchronous.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@tests/ANcpLua.Roslyn.Utilities.DiscriminatedUnion.Tests/IncrementalDiagnosticLocationTests.cs`:
- Around line 63-67: The test currently inspects generator-only diagnostics via
driver.GetRunResult().Diagnostics which won't surface compiler-level failures
like CS8785; change the test to call RunGeneratorsAndUpdateCompilation to obtain
the updated outputCompilation and assert that outputCompilation.GetDiagnostics()
(or the compilation diagnostics collection returned) contains no CS8785, and
additionally replace the manual CSharpGeneratorDriver plumbing with the
project's Test<TGenerator> harness (or adjust to the canonical test runner) so
the test follows repo conventions; update references to
driver.RunGenerators(...) and driver.GetRunResult() accordingly and remove the
old generator-only assertion.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f3e65723-7e31-47d2-b756-f74fd4f93e3f

📥 Commits

Reviewing files that changed from the base of the PR and between 2e01f93 and 0f9c4d5.

📒 Files selected for processing (3)
  • src/ANcpLua.Roslyn.Utilities.Testing/GeneratorHelpers/GeneratorTestHelper.cs
  • tests/ANcpLua.Roslyn.Utilities.DiscriminatedUnion.Tests/ANcpLua.Roslyn.Utilities.DiscriminatedUnion.Tests.csproj
  • tests/ANcpLua.Roslyn.Utilities.DiscriminatedUnion.Tests/IncrementalDiagnosticLocationTests.cs
📜 Review details
🧰 Additional context used
📓 Path-based instructions (8)
**/*.Tests.csproj

📄 CodeRabbit inference engine (global.json)

Use Microsoft.Testing.Platform as the test runner

Files:

  • tests/ANcpLua.Roslyn.Utilities.DiscriminatedUnion.Tests/ANcpLua.Roslyn.Utilities.DiscriminatedUnion.Tests.csproj
**

⚙️ CodeRabbit configuration file

**: # ANcpLua.Roslyn.Utilities

Minimal navigation for Claude/Codex agents. Keep policy in AGENTS.md; keep findings in issues, PRs, or tests.

Project Index

  1. AOT reflection generator
  2. AOT reflection attributes
  3. Discriminated union generator
  4. Extensible enum mirror generator
  5. Core Roslyn utilities
  6. Polyfills package
  7. Source-only package
  8. Testing utilities
  9. AOT testing utilities

Nearby Repos

  • ANcpLua.NET.Sdk: shared SDK/version truth for the ANcpLua repos.
  • ANcpLua.Analyzers: analyzer consumer of the source-only utilities.
  • ANcpLua.Agents: successor location for agent workflow/test helpers; do not describe this repo as the MAF runtime home.

**:

999.9.9


10.0.203
latestMinor

<PropertyGroup Label="Roslyn">
    <RoslynVersion>5.3.0</RoslynVersion>
    <RoslynAnalyzersVersion>5.3.0</RoslynAnalyzersVersion>
</PropertyGroup>

<!-- ═══════════════════════════════════════════════════════════════════════
     ROSLYN ANALYZER TESTING
     Used by: Roslyn.Utilities.Testing, Analyzers.Tests
     ════════════════════════════════════════════════════════════════════...

Files:

  • tests/ANcpLua.Roslyn.Utilities.DiscriminatedUnion.Tests/ANcpLua.Roslyn.Utilities.DiscriminatedUnion.Tests.csproj
  • src/ANcpLua.Roslyn.Utilities.Testing/GeneratorHelpers/GeneratorTestHelper.cs
  • tests/ANcpLua.Roslyn.Utilities.DiscriminatedUnion.Tests/IncrementalDiagnosticLocationTests.cs
**/*.{ts,tsx,js,jsx,cs,py}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Skip files whose first ~3 lines contain // Ported from <upstream>, // Generated, or // Auto-generated — surface a one-line note instead of line-level findings

Files:

  • src/ANcpLua.Roslyn.Utilities.Testing/GeneratorHelpers/GeneratorTestHelper.cs
  • tests/ANcpLua.Roslyn.Utilities.DiscriminatedUnion.Tests/IncrementalDiagnosticLocationTests.cs
**/*.cs

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

.NET code: enable nullable reference types, use central package management via Directory.Packages.props, and treat Version.props as the single owner of versions — never edit <Version> lines directly

**/*.cs: Scan added/modified C# files for .Result, .Wait(), or .GetAwaiter().GetResult() and fail if any are found
Scan added/modified C# files for DateTime.Now or DateTime.UtcNow and fail if any usage found — use TimeProvider.System (or polyfill in netstandard2.0) instead
Scan added/modified C# files for : ISourceGenerator patterns and fail if a new ISourceGenerator is added — must use IIncrementalGenerator instead
Scan added/modified C# files for ! (null-forgiving operator) and fail if any ! is added without an inline comment

**/*.cs: Use DiagnosticFlow<T> for railway-oriented error accumulation in incremental generator pipelines
Use EquatableArray<T> and value-equatable collections to ensure incremental generator caching actually hits
Use IndentedStringBuilder with BeginBlock()/EndBlock() scopes instead of hardcoded indentation strings for code emission
Use Match.Type(), Match.Method(), and fluent symbol matching instead of string comparisons for symbol analysis
Embed utilities via ANcpLua.Roslyn.Utilities.Sources package as internal source code in source generators instead of loading NuGet DLLs at runtime
Use ValueStringBuilder for stack-allocated string building in analyzer hot paths to reduce allocations
Use closure-free GetOrInsert<TContext>(key, context, factory) for dictionary inserts in analyzer hot paths instead of lambda closures
Use GeneratorCachingReport to compare two generator runs and detect cache hits instead of hand-rolled cache assertions
Use Test<TGenerator> fluent runner for generator tests instead of hand-rolled CSharpGeneratorDriver plumbing
Use ForAttributeWithMetadataName to filter syntax before expensive semantic work in incremental generators
Compile-time reflection takes precedence o...

Files:

  • src/ANcpLua.Roslyn.Utilities.Testing/GeneratorHelpers/GeneratorTestHelper.cs
  • tests/ANcpLua.Roslyn.Utilities.DiscriminatedUnion.Tests/IncrementalDiagnosticLocationTests.cs
**/*.{cs,ts,tsx,py}

📄 CodeRabbit inference engine (Custom checks)

Fail C#/TS/Python changes that introduce sync-over-async, unobserved fire-and-forget work, missing CancellationToken/AbortSignal propagation on public/internal async boundaries, sleeps for synchronization, or resource disposal paths that can drop in-flight work

Files:

  • src/ANcpLua.Roslyn.Utilities.Testing/GeneratorHelpers/GeneratorTestHelper.cs
  • tests/ANcpLua.Roslyn.Utilities.DiscriminatedUnion.Tests/IncrementalDiagnosticLocationTests.cs
src/**/*.cs

⚙️ CodeRabbit configuration file

src/**/*.cs: C# 14 / .NET 10 codebase. Review for: idiomatic modern C#, proper async/await (no sync-over-async, no fire-and-forget without justification), correct IDisposable/IAsyncDisposable, null safety (NRTs enabled), and adherence to existing patterns. Flag new public API surface that lacks XML doc comments. Check DI lifetime correctness (scoped vs singleton vs transient).
ARCHITECTURAL INVARIANTS — flag violations as blocking: 1. Every new injectable service must register OpenTelemetry instrumentation
(ActivitySource or Meter).
2. Every new DuckDB write path must handle backpressure (bounded channel or semaphore). 3. No hardcoded connection strings, paths, or magic strings — use IOptions or
IConfiguration.
4. No new dependencies on Sentry-specific types in core/ — Sentry is a comparison
target, not an identity.
5. CancellationToken must be threaded through all async public methods. 6. No sync-over-async (.Result, .GetAwaiter().GetResult()) outside of
well-documented infrastructure code.

Files:

  • src/ANcpLua.Roslyn.Utilities.Testing/GeneratorHelpers/GeneratorTestHelper.cs
src/ANcpLua.Roslyn.Utilities.Testing/**

⚙️ CodeRabbit configuration file

src/ANcpLua.Roslyn.Utilities.Testing/**: # ANcpLua.Roslyn.Utilities.Testing

Testing support package for Roslyn utilities and related generators.

Files:

  • src/ANcpLua.Roslyn.Utilities.Testing/GeneratorHelpers/GeneratorTestHelper.cs
tests/**/*.cs

⚙️ CodeRabbit configuration file

xUnit.v3 test projects using xunit.v3.mtp-v2 with AwesomeAssertions and NSubstitute. Follow Arrange-Act-Assert pattern. Use descriptive test names: MethodName_Scenario_ExpectedResult. Test async methods with async Task, not async void. Flag hardcoded test data that should come from the seeded DuckDB file (the single source of truth for demo/test data). Prefer [Theory] with [InlineData] or [MemberData] over duplicated [Fact] methods testing variations of the same behavior. Flag tests that depend on test execution order.

Files:

  • tests/ANcpLua.Roslyn.Utilities.DiscriminatedUnion.Tests/IncrementalDiagnosticLocationTests.cs
🔇 Additional comments (3)
src/ANcpLua.Roslyn.Utilities.Testing/GeneratorHelpers/GeneratorTestHelper.cs (1)

39-39: LGTM!

Also applies to: 49-59

tests/ANcpLua.Roslyn.Utilities.DiscriminatedUnion.Tests/ANcpLua.Roslyn.Utilities.DiscriminatedUnion.Tests.csproj (1)

19-20: LGTM!

tests/ANcpLua.Roslyn.Utilities.DiscriminatedUnion.Tests/IncrementalDiagnosticLocationTests.cs (1)

45-47: Don’t force Test<TGenerator> here: this test needs ReplaceSyntaxTree between runs.
Test<TGenerator>/GeneratorTestEngine.RunTwiceAsync only reruns on a cloned compilation with the same syntax trees (no hook to swap in the “shrunk” tree), so the direct CSharpGeneratorDriver flow is appropriate for validating the shrinking-edit diagnostic span.

			> Likely an incorrect or invalid review comment.

…pile

The build job compiled the test projects but never ran them, so a runtime
regression could pass CI and auto-publish to nuget.org. Add a Test step after
Build on both ubuntu + windows. publish `needs: build`, so a failing test now
fails the job and blocks the release — closing the gap without touching the
fleet-managed branch protection. CrefRegression.Tests stays a compile-only
guard covered by Build (it has no test-platform reference).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@ANcpLua
ANcpLua merged commit ff05f07 into main Jun 14, 2026
7 checks passed
@ANcpLua
ANcpLua deleted the test/incremental-diagnostic-location-regression branch June 14, 2026 23:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant