refactor: eliminate DRY violations across analyzer pairs#1044
refactor: eliminate DRY violations across analyzer pairs#1044
Conversation
Extract duplicated logic from three analyzer groups: 1. Overridable member check: Extract IsOverridableOrAllowedMockMember to ISymbolExtensions, replacing identical private methods in SetupShouldBeUsedOnlyForOverridableMembersAnalyzer, SetupSequenceShouldBeUsedOnlyForOverridableMembersAnalyzer, and VerifyShouldBeUsedOnlyForOverridableMembersAnalyzer. 2. MockBehavior reporting: Add TryReportMockBehaviorDiagnostic and TryHandleMissingMockBehaviorParameter overloads with messageArgs to MockBehaviorDiagnosticAnalyzerBase, plus shared GetMockedTypeName. Removes duplicate methods from SetExplicitMockBehaviorAnalyzer and SetStrictMockBehaviorAnalyzer. 3. Event argument validation: Replace RaiseEventArgumentsShouldMatchEventSignatureAnalyzer's private ValidateArgumentTypesWithEventName with existing shared EventSyntaxExtensions.ValidateEventArgumentTypes. No behavior changes. All 2901 tests pass. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
|
Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits. |
|
Warning Rate limit exceeded
⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Repository UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (7)
📝 WalkthroughWalkthroughCentralizes mock-behavior diagnostic/reporting and mocked-type extraction in the analyzer base, converts event-argument validators into context extension methods, and consolidates overridability checks into a shared ISymbol extension; callers and duplicate helpers were removed or updated accordingly. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
|
|
Overall Grade Focus Area: Hygiene |
Security Reliability Complexity Hygiene |
Feedback
- Inconsistent public API parameter ordering
- The parameter-order mistake in the extension indicates a wider lack of API conventions; callers can accidentally pass swapped arguments. Enforce a canonical parameter order for event extensions and add compile-time checks to prevent regressions.
- Missing static analysis for API shape
- This slipped because no analyzer enforces signature rules across files; minor ordering bugs survive routine edits. Introduce Roslyn rules to validate extension 'this' placement and parameter ordering during the build.
- Tests don't assert public API contracts
- Behavioural tests miss API-shape regressions because no reflection-based assertions exist for signatures. Add unit tests that assert extension method parameter order and fail loudly when the public contract deviates.
Code Review Summary
| Analyzer | Status | Updated (UTC) | Details |
|---|---|---|---|
| C# | Mar 8, 2026 6:31p.m. | Review ↗ |
src/Analyzers/RaiseEventArgumentsShouldMatchEventSignatureAnalyzer.cs
Outdated
Show resolved
Hide resolved
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/Analyzers/SetStrictMockBehaviorAnalyzer.cs (1)
34-34: 🧹 Nitpick | 🔵 TrivialConsider aligning variable naming with SetExplicitMockBehaviorAnalyzer.
This file uses
mockedTypeNamewhileSetExplicitMockBehaviorAnalyzerusestypeName. Consider unifying to one naming convention across both analyzers for consistency.mockedTypeNameis more descriptive; alternatively, both could usetypeNamefor brevity.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Analyzers/SetStrictMockBehaviorAnalyzer.cs` at line 34, The variable name mockedTypeName in SetStrictMockBehaviorAnalyzer should be renamed to match the convention used in SetExplicitMockBehaviorAnalyzer (typeName) for consistency; update the declaration and every usage of mockedTypeName within SetStrictMockBehaviorAnalyzer (including references in GetMockedTypeName calls, diagnostic messages, and any symbolic comparisons) to typeName, and run tests/compile to ensure no references remain to mockedTypeName; alternatively, if you prefer the more descriptive name, change typeName in SetExplicitMockBehaviorAnalyzer to mockedTypeName in the same consistent way across both analyzers.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/Analyzers/MockBehaviorDiagnosticAnalyzerBase.cs`:
- Around line 17-18: Update the XML doc comments in
MockBehaviorDiagnosticAnalyzerBase so type references use <see cref="..."/>
instead of plain text: change the <param name="operation"> and <param
name="target"> descriptions to reference their types with <see cref="..."/>
(e.g., <see cref="IOperation"/> or the actual symbol type and <see
cref="IMethodSymbol"/> or the actual method symbol type) and ensure the tags
reference the exact symbol names used in the code (operation, target) for proper
IntelliSense linking.
In `@src/Common/ISymbolExtensions.cs`:
- Around line 279-300: The switch in
ISymbolExtensions.IsOverridableOrAllowedMockMember currently ignores
IEventSymbol so virtual events (surfaced by
MoqVerificationHelpers.TryGetMockedMemberSymbol for SetupAdd/SetupRemove) are
treated as non-overridable; update IsOverridableOrAllowedMockMember to handle
IEventSymbol (call eventSymbol.IsOverridable()) in the switch alongside
IPropertySymbol/IMethodSymbol and leave other cases false, and add a regression
unit test that exercises SetupAdd/SetupRemove on a virtual event to ensure the
analyzer no longer reports a false positive.
---
Outside diff comments:
In `@src/Analyzers/SetStrictMockBehaviorAnalyzer.cs`:
- Line 34: The variable name mockedTypeName in SetStrictMockBehaviorAnalyzer
should be renamed to match the convention used in
SetExplicitMockBehaviorAnalyzer (typeName) for consistency; update the
declaration and every usage of mockedTypeName within
SetStrictMockBehaviorAnalyzer (including references in GetMockedTypeName calls,
diagnostic messages, and any symbolic comparisons) to typeName, and run
tests/compile to ensure no references remain to mockedTypeName; alternatively,
if you prefer the more descriptive name, change typeName in
SetExplicitMockBehaviorAnalyzer to mockedTypeName in the same consistent way
across both analyzers.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 862198cb-5e27-4007-abdd-5c6f23e7c5e0
📒 Files selected for processing (10)
src/Analyzers/MockBehaviorDiagnosticAnalyzerBase.cssrc/Analyzers/RaiseEventArgumentsShouldMatchEventSignatureAnalyzer.cssrc/Analyzers/RaisesEventArgumentsShouldMatchEventSignatureAnalyzer.cssrc/Analyzers/SetExplicitMockBehaviorAnalyzer.cssrc/Analyzers/SetStrictMockBehaviorAnalyzer.cssrc/Analyzers/SetupSequenceShouldBeUsedOnlyForOverridableMembersAnalyzer.cssrc/Analyzers/SetupShouldBeUsedOnlyForOverridableMembersAnalyzer.cssrc/Analyzers/VerifyShouldBeUsedOnlyForOverridableMembersAnalyzer.cssrc/Common/EventSyntaxExtensions.cssrc/Common/ISymbolExtensions.cs
Virtual events (e.g., virtual event EventHandler Changed) were falling through to the default false case in the switch expression, causing false positives when using SetupAdd/SetupRemove. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Resolve merge conflict in MockBehaviorDiagnosticAnalyzerBase.cs: - Keep private protected abstract for AnalyzeCore (from main) - Retain PR helper methods: TryReportMockBehaviorDiagnostic (both overloads), TryHandleMissingMockBehaviorParameter, and GetMockedTypeName Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
b40f084 to
1d5024d
Compare
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/Analyzers/MockBehaviorDiagnosticAnalyzerBase.cs (1)
45-54:⚠️ Potential issue | 🔴 CriticalFix member ordering to resolve SA1202 build failure.
The build is failing due to SA1202:
internalmembers (starting at line 65) are placed after theprivate protectedmemberAnalyzeCoreat line 54. StyleCop requires accessibility ordering:public→internal→private protected→private.🔧 Proposed fix: Reorder members
Move
AnalyzeCorebelow allinternalmethods. The correct order should be:
public override void Initialize(public)internal static string GetMockedTypeName(internal static)internal bool TryReportMockBehaviorDiagnosticoverloads (internal)internal bool TryHandleMissingMockBehaviorParameteroverloads (internal)private protected abstract void AnalyzeCore(private protected)private void RegisterCompilationStartAction(private)private void AnalyzeObjectCreation(private)private void AnalyzeInvocation(private)/// <inheritdoc /> public override void Initialize(AnalysisContext context) { context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); context.EnableConcurrentExecution(); context.RegisterCompilationStartAction(RegisterCompilationStartAction); } - private protected abstract void AnalyzeCore(OperationAnalysisContext context, IMethodSymbol target, ImmutableArray<IArgumentOperation> arguments, MoqKnownSymbols knownSymbols); - /// <summary> /// Attempts to report a diagnostic for a MockBehavior parameter issue. /// </summary> // ... TryReportMockBehaviorDiagnostic methods ... // ... TryHandleMissingMockBehaviorParameter methods ... + private protected abstract void AnalyzeCore(OperationAnalysisContext context, IMethodSymbol target, ImmutableArray<IArgumentOperation> arguments, MoqKnownSymbols knownSymbols); + private void RegisterCompilationStartAction(CompilationStartAnalysisContext context)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/Analyzers/MockBehaviorDiagnosticAnalyzerBase.cs` around lines 45 - 54, SA1202 is triggered because the private protected member AnalyzeCore is declared before internal members; move the declaration of AnalyzeCore (private protected abstract void AnalyzeCore(OperationAnalysisContext, IMethodSymbol, ImmutableArray<IArgumentOperation>, MoqKnownSymbols)) so it appears after the internal members (internal static string GetMockedTypeName, the internal TryReportMockBehaviorDiagnostic overloads, and the internal TryHandleMissingMockBehaviorParameter overloads) and before the private methods (RegisterCompilationStartAction, AnalyzeObjectCreation, AnalyzeInvocation) to restore the required accessibility ordering.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@src/Analyzers/MockBehaviorDiagnosticAnalyzerBase.cs`:
- Around line 45-54: SA1202 is triggered because the private protected member
AnalyzeCore is declared before internal members; move the declaration of
AnalyzeCore (private protected abstract void
AnalyzeCore(OperationAnalysisContext, IMethodSymbol,
ImmutableArray<IArgumentOperation>, MoqKnownSymbols)) so it appears after the
internal members (internal static string GetMockedTypeName, the internal
TryReportMockBehaviorDiagnostic overloads, and the internal
TryHandleMissingMockBehaviorParameter overloads) and before the private methods
(RegisterCompilationStartAction, AnalyzeObjectCreation, AnalyzeInvocation) to
restore the required accessibility ordering.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 9416b927-9061-49bb-a27d-2d7026b9fbe0
📒 Files selected for processing (6)
src/Analyzers/MockBehaviorDiagnosticAnalyzerBase.cssrc/Analyzers/RaisesEventArgumentsShouldMatchEventSignatureAnalyzer.cssrc/Analyzers/SetExplicitMockBehaviorAnalyzer.cssrc/Analyzers/SetStrictMockBehaviorAnalyzer.cssrc/Analyzers/SetupSequenceShouldBeUsedOnlyForOverridableMembersAnalyzer.cssrc/Analyzers/VerifyShouldBeUsedOnlyForOverridableMembersAnalyzer.cs
…alyzerBase Resolve merge conflict in ISymbolExtensions.cs by keeping main's file split (general vs Moq-specific) and adding IsOverridableOrAllowedMockMember to ISymbolExtensions.Moq.cs. Reorder members in MockBehaviorDiagnosticAnalyzerBase to satisfy SA1202: public > internal > private protected > private. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…raction Consolidate overload pairs that differ only by params object[] messageArgs into single methods with params. Remove duplicated TryGetEventNameFromLambdaSelector in RaisesEventArgumentsShouldMatchEventSignatureAnalyzer in favor of the shared SemanticModelExtensions method. Inline trivial Internal wrapper methods in EventSyntaxExtensions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Collapse overload pairs into single methods with optional/params parameters: - MockBehaviorDiagnosticAnalyzerBase: merge TryReportMockBehaviorDiagnostic and TryHandleMissingMockBehaviorParameter overloads using params - EventSyntaxExtensions: merge ValidateEventArgumentTypes, GetEventParameterTypes, and TryGetEventMethodArguments overloads using optional parameters - EventSyntaxExtensions: inline single-caller internal methods (GetEventParameterTypesInternal, TryGetEventMethodArgumentsInternal) - SetupSequenceShouldBeUsedOnlyForOverridableMembersAnalyzer: consolidate guard clauses, remove verbose numbered comments, drop unused SuppressMessage import Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nosticAnalyzerBase - Make GetMockedTypeName virtual so SetStrictMockBehaviorAnalyzer can restore its original behavior (target.TypeArguments, "Unknown" fallback) - Remove IEventSymbol case from IsOverridableOrAllowedMockMember to match original methods that only handled IPropertySymbol/IMethodSymbol - Replace params object[] overloads with typed string mockedTypeName parameter since both callers always pass a single string Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Coverage summary from CodacySee diff coverage on Codacy
Coverage variation details
Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: Diff coverage details
Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: See your quality gate settings Change summary preferences |
## Summary Resolves #980. Extracts duplicated logic from three analyzer groups without changing behavior. - **Overridable member check**: `IsOverridableOrAllowedMockMember` extracted to `ISymbolExtensions`, replacing identical private methods (`IsPropertyOrMethod`, `IsOverridableOrTaskResultMember`, `IsAllowedMockMember`) in `SetupShouldBeUsedOnlyForOverridableMembersAnalyzer`, `SetupSequenceShouldBeUsedOnlyForOverridableMembersAnalyzer`, and `VerifyShouldBeUsedOnlyForOverridableMembersAnalyzer` - **MockBehavior reporting**: Added `TryReportMockBehaviorDiagnostic` and `TryHandleMissingMockBehaviorParameter` overloads with `messageArgs` plus shared `GetMockedTypeName` to `MockBehaviorDiagnosticAnalyzerBase`, removing duplicate methods from `SetExplicitMockBehaviorAnalyzer` and `SetStrictMockBehaviorAnalyzer` - **Event argument validation**: Replaced `RaiseEventArgumentsShouldMatchEventSignatureAnalyzer.ValidateArgumentTypesWithEventName` with existing `EventSyntaxExtensions.ValidateEventArgumentTypes` Net result: 122 lines added, 285 lines removed (163 lines reduced). ## Test plan - [x] All 2901 existing tests pass with zero failures - [x] Build succeeds with zero warnings - [x] No diagnostic IDs, messages, or categories changed - [x] No analyzer behavior changed, only structure 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Consolidated mock-behavior diagnostic reporting to a single path with support for formatted messages and overload-aware reporting. * Replaced many inlined helpers with shared reporting/handling flows for strict/explicit mock analyses. * Switched event-argument validation to context-based extension calls for consistency. * Centralized overridable/allowed-member checks into a single symbol extension used by setup, verify, and sequence analyzers. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Richard Murillo <rjmurillo@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary Resolves #980. Extracts duplicated logic from three analyzer groups without changing behavior. - **Overridable member check**: `IsOverridableOrAllowedMockMember` extracted to `ISymbolExtensions`, replacing identical private methods (`IsPropertyOrMethod`, `IsOverridableOrTaskResultMember`, `IsAllowedMockMember`) in `SetupShouldBeUsedOnlyForOverridableMembersAnalyzer`, `SetupSequenceShouldBeUsedOnlyForOverridableMembersAnalyzer`, and `VerifyShouldBeUsedOnlyForOverridableMembersAnalyzer` - **MockBehavior reporting**: Added `TryReportMockBehaviorDiagnostic` and `TryHandleMissingMockBehaviorParameter` overloads with `messageArgs` plus shared `GetMockedTypeName` to `MockBehaviorDiagnosticAnalyzerBase`, removing duplicate methods from `SetExplicitMockBehaviorAnalyzer` and `SetStrictMockBehaviorAnalyzer` - **Event argument validation**: Replaced `RaiseEventArgumentsShouldMatchEventSignatureAnalyzer.ValidateArgumentTypesWithEventName` with existing `EventSyntaxExtensions.ValidateEventArgumentTypes` Net result: 122 lines added, 285 lines removed (163 lines reduced). ## Test plan - [x] All 2901 existing tests pass with zero failures - [x] Build succeeds with zero warnings - [x] No diagnostic IDs, messages, or categories changed - [x] No analyzer behavior changed, only structure 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Consolidated mock-behavior diagnostic reporting to a single path with support for formatted messages and overload-aware reporting. * Replaced many inlined helpers with shared reporting/handling flows for strict/explicit mock analyses. * Switched event-argument validation to context-based extension calls for consistency. * Centralized overridable/allowed-member checks into a single symbol extension used by setup, verify, and sequence analyzers. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Richard Murillo <rjmurillo@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
## Summary Resolves #980. Extracts duplicated logic from three analyzer groups without changing behavior. - **Overridable member check**: `IsOverridableOrAllowedMockMember` extracted to `ISymbolExtensions`, replacing identical private methods (`IsPropertyOrMethod`, `IsOverridableOrTaskResultMember`, `IsAllowedMockMember`) in `SetupShouldBeUsedOnlyForOverridableMembersAnalyzer`, `SetupSequenceShouldBeUsedOnlyForOverridableMembersAnalyzer`, and `VerifyShouldBeUsedOnlyForOverridableMembersAnalyzer` - **MockBehavior reporting**: Added `TryReportMockBehaviorDiagnostic` and `TryHandleMissingMockBehaviorParameter` overloads with `messageArgs` plus shared `GetMockedTypeName` to `MockBehaviorDiagnosticAnalyzerBase`, removing duplicate methods from `SetExplicitMockBehaviorAnalyzer` and `SetStrictMockBehaviorAnalyzer` - **Event argument validation**: Replaced `RaiseEventArgumentsShouldMatchEventSignatureAnalyzer.ValidateArgumentTypesWithEventName` with existing `EventSyntaxExtensions.ValidateEventArgumentTypes` Net result: 122 lines added, 285 lines removed (163 lines reduced). ## Test plan - [x] All 2901 existing tests pass with zero failures - [x] Build succeeds with zero warnings - [x] No diagnostic IDs, messages, or categories changed - [x] No analyzer behavior changed, only structure 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Refactor** * Consolidated mock-behavior diagnostic reporting to a single path with support for formatted messages and overload-aware reporting. * Replaced many inlined helpers with shared reporting/handling flows for strict/explicit mock analyses. * Switched event-argument validation to context-based extension calls for consistency. * Centralized overridable/allowed-member checks into a single symbol extension used by setup, verify, and sequence analyzers. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Richard Murillo <rjmurillo@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Summary
Resolves #980. Extracts duplicated logic from three analyzer groups without changing behavior.
IsOverridableOrAllowedMockMemberextracted toISymbolExtensions, replacing identical private methods (IsPropertyOrMethod,IsOverridableOrTaskResultMember,IsAllowedMockMember) inSetupShouldBeUsedOnlyForOverridableMembersAnalyzer,SetupSequenceShouldBeUsedOnlyForOverridableMembersAnalyzer, andVerifyShouldBeUsedOnlyForOverridableMembersAnalyzerTryReportMockBehaviorDiagnosticandTryHandleMissingMockBehaviorParameteroverloads withmessageArgsplus sharedGetMockedTypeNametoMockBehaviorDiagnosticAnalyzerBase, removing duplicate methods fromSetExplicitMockBehaviorAnalyzerandSetStrictMockBehaviorAnalyzerRaiseEventArgumentsShouldMatchEventSignatureAnalyzer.ValidateArgumentTypesWithEventNamewith existingEventSyntaxExtensions.ValidateEventArgumentTypesNet result: 122 lines added, 285 lines removed (163 lines reduced).
Test plan
🤖 Generated with Claude Code
Summary by CodeRabbit