Repository navigation
Conversation
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
teneko
force-pushed
the
feat/explicit-matcher-role
branch
from
October 9, 2026 06:36
46d1bf2 to
89e3843
Compare
A type became a matcher implicitly as soon as any target listed it in Matchers. The generator then skipped the type's own [GenerateOverloads] methods as targets, so referencing a type elsewhere removed its own overloads. The analyzer, which analyzes each type on its own, treated an unreferenced matcher as a direct target and reported false generation diagnostics (two MOG006 in the acceptance project). The matcher role is now explicit and local to the type: - New [OverloadMatcher] marker (class, struct, interface). A marked type is a matcher and never a target, whether or not anything references it (TypeModel.IsMatcher replaces the global set of referenced matcher types in the plan builder). - An unmarked type in Matchers is not used as a matcher and keeps its own role. The analyzer reports MOG020 (Error) at every typeof reference to it. - [GenerateMethodOverloads] on a matcher type, or Matchers on one of its methods, is ignored and reported as MOG021 (Error). Other [GenerateOverloads] attributes of that matcher method still apply. Formal checks of matcher attributes now run once, in the matcher type itself (new ValidateMatcherMethods step), independent of any target. Targets skip invalid matcher attributes silently instead of reporting diagnostics that the analyzer discarded anyway. MOG020 and MOG021 are declaration checks done directly in the analyzer, so generator inputs carry no new diagnostics or locations. All matcher types in the acceptance project are marked. Analyzer diagnostics on the acceptance sources: the two false MOG006 at Class_4_Matcher and Bug_Class_1_ICommandRuntimeFactoryMatchers are gone, the other 37 are unchanged, generated overloads are unchanged. BREAKING CHANGE: types listed in Matchers must be marked with [OverloadMatcher]; unmarked types produce MOG020 and are ignored. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Acceptance cases for the matcher role: - Class_46: an unmarked type in Matchers is ignored and keeps its own role, so its own overload is generated; MOG020 at both typeof references (type and method level). - Class_47: [GenerateMethodOverloads] on a matcher type (MOG021). - Class_48: one matcher per path of the matcher validation step (MOG001, MOG008, MOG011, MOG013, MOG014) and Matchers on a matcher method (MOG021). The error kinds themselves are already covered at targets. A target still gets the overload from the valid attribute of the MOG021 matcher. - Class_49: an unused matcher in the shape of Class_4_Matcher produces no overloads and no MOG006. Matchers that expect diagnostics are abstract classes, because the acceptance infrastructure maps diagnostics only inside classes. The diagnostics comparison works on IDs per class and cannot count, so UnmarkedMatcherTests checks on the acceptance output that MOG020 is reported at both typeof references of Class_46. OverloadMatcher usages on class, struct and interface feed the attribute usage test, and the attributes-only test checks that the attribute is emitted. Mutation checks: disabling the validation step fails the five validation cases of Class_48; treating matcher methods as targets fails Class_49 (among others); skipping a matcher method because of its Matchers attribute fails Class_48. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Describe [OverloadMatcher], the matcher type rules, MOG020 and MOG021 in the README (including the breaking change) and in docs/generator.md, where formal diagnostics on matcher methods are now reported once in the matcher type. docs/acceptance-criterias.md explains where MOG020 is declared, why matchers in acceptance cases are abstract classes, and which test counts MOG020. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
teneko
force-pushed
the
feat/explicit-matcher-role
branch
from
October 9, 2026 06:44
89e3843 to
ba3aded
Compare
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.
Problem
A type became a matcher implicitly, only because some target listed it in
Matchers = [typeof(T)]. The role was decided somewhere else, which caused two problems:IUserServicewith[GenerateOverloads(nameof(name))] void Update(int id, string? name);getsUpdate(this IUserService, int). As soon as another class adds[GenerateOverloads(Matchers = [typeof(IUserService)])]to its own method, theIUserServiceoverload disappears (OverloadPlanBuilderskipped every type that was referenced as a matcher anywhere).MOG006in the acceptance project (Class_4_Matcher.Matcher_2,Bug_Class_1_ICommandRuntimeFactoryMatchers.Matcher). fix: report analyzer diagnostics per type at in-source locations #4 documents this as a known limitation.Change
The matcher role is now explicit and local to the type:
[OverloadMatcher]Matcherstypeofreference[GenerateMethodOverloads]on a matcher type, orMatcherson one of its methods[GenerateOverloads]of the matcher method still apply[GenerateOverloads]New
[OverloadMatcher]attribute:Class | Struct | Interface, noAllowMultiple, embedded like the other attributes.Implementation
TypeModel.IsMatchercomes from the marker (Parser.IsMatcherType).OverloadPlanBuilderskips methods of matcher types via this flag instead of the global set of referenced matcher types.Parser.ExtractMatcherTypesignores unmarked types.TargetFactoryignoresMatcherson matcher types.ValidateMatcherMethodsstep reports MOG001, MOG007–MOG011, MOG013–MOG016, MOG018 and MOG019 once, in the matcher type's own analysis, independent of any target. The matcher path of a target now skips invalid matcher attributes silently; before, it reported diagnostics at matcher locations that the analyzer discarded. Generation diagnostics (MOG003–MOG006) are reported only at targets.Breaking change
Types listed in
Matchersmust be marked with[OverloadMatcher]. Unmarked types produce MOG020 and are ignored. Documented in the README.Minor: a matcher method whose
[GenerateOverloads]combinesMatcherswith window anchors orExcludeAnywas skipped entirely before (MOG012/MOG017, never visible at the matcher). Now only that attribute is ignored (MOG021).Diagnostics before/after (acceptance sources, in-memory analyzer dump)
All 35 matcher types are marked. The suggested
grepforMatchers = [...]missedSupplyParameterType_Class_12, which spans lines; MOG020 flagged its two matchers.Class_46, MOG021 inClass_47(new cases, suppressed as expected)Generated overloads of all existing acceptance cases are unchanged.
Tests
Tests follow the acceptance-criteria approach (
docs/testing.md):Class_46: an unmarked type inMatchersis ignored and keeps its own role, so its own overload is generated. MOG020 is reported at bothtypeofreferences (type and method level).Class_47:[GenerateMethodOverloads]on a matcher type (MOG021).Class_48: one matcher per path of the matcher validation step (invalid window MOG001, redundant anchors MOG008, parameterless MOG011, invalid bucket MOG013, invalid SupplyParameterType MOG014) andMatcherson a matcher method (MOG021). The error kinds themselves are already covered at targets. A target still gets the overload from the valid attribute of the MOG021 matcher.Class_49: an unused matcher in the shape ofClass_4_Matcherproduces no overloads and no MOG006 (the original false positive).UnmarkedMatcherTestschecks on the acceptance output that MOG020 is reported twice inClass_46. The diagnostics comparison works on IDs per class and cannot count.OverloadMatcherusages on class, struct and interface forAttributeUsageTests.AttributesOnlyTestschecks that the attribute is emitted. The diagnostics snapshot gains MOG020 and MOG021.DiagnosticSuppressionTestsmarks its in-memory matcher.ValidateMatcherMethodsfails the five validation cases ofClass_48Class_49(among others)Matchersattribute failsClass_48Verification
dotnet test --nologo: 131 passed. Thefeat!commit alone: 120 passed.dotnet build ref/...csproj -c Release -nodeReuse:false --no-incrementalwithTenekonMethodOverloadsSourceGeneratorAttributesOnly=falseand=true: 0 errors, 0 MOG warnings. The CS8625 warnings are unchanged (59 in the full build).SupplyParameterTypeerrors on matcher types. The acceptance project has no case for type-levelSupplyParameterTypeerrors at all (pre-existing). No tests were added only for coverage.Out of scope
🤖 Generated with Claude Code