Skip to content

Report unexpected mock generation failures - #6605

Merged
thomhurst merged 2 commits into
mainfrom
agent/mock-generation-failure-diagnostic
Aug 13, 2026
Merged

Report unexpected mock generation failures#6605
thomhurst merged 2 commits into
mainfrom
agent/mock-generation-failure-diagnostic

Conversation

@thomhurst

@thomhurst thomhurst commented Aug 13, 2026

Copy link
Copy Markdown
Owner

Summary

  • report TM009 when unexpected mock generation failures occur
  • attach the diagnostic to the request location with target type and exception details
  • continue generating unrelated mocks after one target fails

Why

Generator exceptions can otherwise leave users without actionable context. This makes failures visible and navigable while isolating them to the affected mock.

Validation

  • TUnit.Mocks.SourceGenerator.Tests: 303 passed across net8.0, net9.0, and net10.0
  • Roslyn compatibility builds: 4.4, 4.7, and 4.14
  • no .received.txt snapshot files

Relates to #6604

Summary by CodeRabbit

  • Bug Fixes
    • Mock generation failures now produce a clear diagnostic with the affected type and exception details.
    • A failure generating one mock no longer prevents other valid mocks from being generated.
    • Duplicate mock requests are handled consistently, and name collisions include source-location information.

@coderabbitai

coderabbitai Bot commented Aug 13, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 44afd929-9904-4d1d-92b8-aa3c2199f1e5

📥 Commits

Reviewing files that changed from the base of the PR and between 60360b4 and 5050a27.

📒 Files selected for processing (5)
  • src/TUnit.Mocks.SourceGenerator/Discovery/GeneratedNameCollisionDetector.cs
  • src/TUnit.Mocks.SourceGenerator/Discovery/MockTypeDiscovery.cs
  • src/TUnit.Mocks.SourceGenerator/MockGenerator.cs
  • src/TUnit.Mocks.SourceGenerator/Models/MockGenerationRequest.cs
  • tests/TUnit.Mocks.SourceGenerator.Tests/MockGeneratorDiagnosticTests.cs
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/TUnit.Mocks.SourceGenerator/Models/MockGenerationRequest.cs
  • tests/TUnit.Mocks.SourceGenerator.Tests/MockGeneratorDiagnosticTests.cs
  • src/TUnit.Mocks.SourceGenerator/MockGenerator.cs

📝 Walkthrough

Walkthrough

The source generator now preserves mock request locations, deduplicates requests, injects source emission, and reports unexpected generation failures through TM009 diagnostics. Tests verify failure isolation and continued generation of other mocks.

Changes

Mock generation diagnostics

Layer / File(s) Summary
Request location pipeline
src/TUnit.Mocks.SourceGenerator/Models/MockGenerationRequest.cs, src/TUnit.Mocks.SourceGenerator/Discovery/MockTypeDiscovery.cs, src/TUnit.Mocks.SourceGenerator/Discovery/GeneratedNameCollisionDetector.cs, src/TUnit.Mocks.SourceGenerator/MockGenerator.cs
Discovery creates immutable requests with attribute locations. Duplicate models retain the first request. Collision annotations preserve request metadata.
Guarded source emission
src/TUnit.Mocks.SourceGenerator/Diagnostics.cs, src/TUnit.Mocks.SourceGenerator/MockGenerator.cs
An injectable emitter supports guarded generation. Non-cancellation exceptions produce TM009 diagnostics with exception details and the matching request location. Existing emission paths remain available.
Diagnostic regression coverage
tests/TUnit.Mocks.SourceGenerator.Tests/MockGeneratorDiagnosticTests.cs, tests/TUnit.Mocks.SourceGenerator.Tests/SnapshotTestBase.cs
Tests inject a failure for IBroken, validate TM009 severity, message, location, and partial output, and confirm that other mocks continue to generate. The test helper accepts an optional generator.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Mergeability Score: ⚪ Minimal · up to 5050a

The change adds actionable diagnostics for unexpected mock-generation failures while allowing unrelated mocks to continue generating; no actionable merge-blocking risk remains after normal checks and review.

Sequence Diagram(s)

sequenceDiagram
  participant MockGenerator
  participant MockGenerationRequest
  participant SourceEmitter
  participant SourceProductionContext

  MockGenerator->>MockGenerationRequest: Create request with source location
  MockGenerator->>SourceEmitter: Emit mock sources
  SourceEmitter-->>MockGenerator: Throw generation exception
  MockGenerator->>SourceProductionContext: Report TM009 at request location
  MockGenerator->>SourceEmitter: Emit remaining mock sources
Loading

Possibly related PRs

  • thomhurst/TUnit#6606: Both PRs update MockTypeDiscovery.TransformGenerateMockAttribute to process multiple GenerateMock attributes.

Poem

A rabbit records each mock’s place,
TM009 marks the failure trace.
One mock breaks; the next runs through.
Healthy sources still come through.
Hop, hop—generation stays true.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: reporting unexpected mock generation failures.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch agent/mock-generation-failure-diagnostic

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

@greptile-apps

greptile-apps Bot commented Aug 13, 2026

Copy link
Copy Markdown

Greptile Summary

The PR adds TM009 diagnostics for unexpected per-target mock-generation exceptions while preserving generation for unrelated targets.

  • Carries request locations through discovery and deduplication.
  • Catches non-cancellation generation exceptions and reports target, exception type, and message.
  • Preserves generated-name collision handling while associating models with their originating requests.

Confidence Score: 5/5

The PR appears safe to merge because no blocking failure remains.

No blocking failure remains.

Important Files Changed

Filename Overview
src/TUnit.Mocks.SourceGenerator/MockGenerator.cs Introduces request-aware deduplication and per-target exception isolation with TM009 reporting.
src/TUnit.Mocks.SourceGenerator/Models/MockGenerationRequest.cs Adds an incremental-safe request model that stores primitive source-location data.
src/TUnit.Mocks.SourceGenerator/Discovery/MockTypeDiscovery.cs Associates assembly-attribute discovery results with the matching attribute location.
src/TUnit.Mocks.SourceGenerator/Discovery/GeneratedNameCollisionDetector.cs Preserves collision annotation while keeping each model paired with its request location.
tests/TUnit.Mocks.SourceGenerator.Tests/MockGeneratorDiagnosticTests.cs Adds coverage for TM009 content, location selection, and continued unrelated generation.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Discover mock request] --> B[Pair model with source location]
  B --> C[Deduplicate requests]
  C --> D[Detect generated-name collisions]
  D --> E{Emit target sources}
  E -->|Success| F[Generated mock artifacts]
  E -->|Unexpected exception| G[Report TM009 at request location]
  G --> H[Continue processing unrelated targets]
Loading

Reviews (2): Last reviewed commit: "fix(mocks): preserve diagnostic requests" | Re-trigger Greptile

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 60360b4093

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/TUnit.Mocks.SourceGenerator/Models/MockGenerationRequest.cs Outdated
@claude

claude Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Code review

Reviewed 60360b4 (Report unexpected mock generation failures).

Summary: Wraps each mock target's source emission in a try/catch that reports a new TM009 diagnostic instead of letting one bad target take down the whole generator run, and threads a primitive, incremental-cache-safe MockSourceLocation alongside each MockTypeModel so the diagnostic can point back at the call site. Good instinct on the location type — keeping Location/SyntaxNode out of pipeline state (

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
namespace TUnit.Mocks.SourceGenerator.Models;
/// <summary>
/// Pairs an equatable mock model with the source request that triggered it. Location data stays
/// primitive so incremental state never retains a syntax tree.
/// </summary>
internal sealed record MockGenerationRequest(MockTypeModel Model, MockSourceLocation SourceLocation);
internal readonly record struct MockSourceLocation(
string FilePath,
int SpanStart,
int SpanLength,
) avoids retaining a syntax tree in the incremental cache, which is exactly the kind of thing that quietly kills caching in incremental generators. The injectable _emitSources seam is a clean way to unit test the catch path without a friction-heavy integration harness.

Two design points worth considering, neither blocking:

  1. Parallel-list coupling between models/locations and GeneratedNameCollisionDetector.Annotate (

    }
    private static ImmutableArray<MockGenerationRequest> CreateRequests(
    ImmutableArray<MockTypeModel> models,
    Location location)
    {
    if (models.IsDefaultOrEmpty)
    {
    ). AddDistinctRequests builds models and locations as two same-length lists, then re-zips them by index after Annotate runs. This only works because Annotate happens to preserve order and count 1:1 — an invariant it documents but nothing enforces at the type level. If Annotate (or a future refactor of it) ever filtered, sorted, or deduplicated further, this would silently pair the wrong location with the wrong model instead of failing loudly. Since MockGenerationRequest already exists as exactly the (Model, Location) pair type, it'd be more robust to have Annotate take/return List<MockGenerationRequest> (or at least IReadOnlyList<MockGenerationRequest>) directly and use request with { Model = request.Model with { CollidesWith = ... } } internally, rather than splitting into parallel arrays and reassembling by position.

  2. GetAttributeLocation assumes the model came from Attributes[0] (

    }
    private static void GenerateSingleTypeMock(SourceProductionContext spc, MockTypeModel model)
    ). TransformGenerateMockAttribute loops context.Attributes and returns the model for the first attribute that passes validation, but GetAttributeLocation always takes Attributes[0]'s location regardless of which entry actually produced the model. For the common case (one [assembly: GenerateMock(typeof(T))] per attribute list) these are the same thing, so this won't show up in practice today. But GenerateMockAttribute is AllowMultiple = true, and if a user ever writes [assembly: GenerateMock(typeof(Sealed)), GenerateMock(typeof(Good))] in one list, the diagnostic would report Attributes[0]'s (the skipped one's) location instead of the attribute that actually triggered the model. Given the location is now threaded through specifically for diagnostic precision, it'd be worth having TransformGenerateMockAttribute report back which AttributeData/index it matched so the location follows the same attribute the model came from.

Nothing here blocks the PR — the exception-isolation behavior itself (catch per target, continue with the rest) is correct and the new regression test confirms IHealthyMock still generates after IBroken fails.

@thomhurst
thomhurst enabled auto-merge (squash) August 13, 2026 10:48
@thomhurst
thomhurst merged commit ff85fc6 into main Aug 13, 2026
14 checks passed
@thomhurst
thomhurst deleted the agent/mock-generation-failure-diagnostic branch August 13, 2026 11:01
This was referenced Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant