Skip to content

fix: replace string-based detection with symbol-based detection#1030

Merged
rjmurillo merged 28 commits intomainfrom
fix/981-remove-string-detection
Mar 9, 2026
Merged

fix: replace string-based detection with symbol-based detection#1030
rjmurillo merged 28 commits intomainfrom
fix/981-remove-string-detection

Conversation

@rjmurillo
Copy link
Copy Markdown
Owner

@rjmurillo rjmurillo commented Mar 7, 2026

Summary

  • Remove string-based IsActionDelegate/IsEventHandlerDelegate fallbacks in EventSyntaxExtensions. All delegate type checks now require KnownSymbols and use symbol comparison via NamedTypeSymbolExtensions.
  • Replace string.Equals(targetMethod.Name, "Of") in LinqToMocksExpressionShouldBeValidAnalyzer with targetMethod.IsInstanceOf(knownSymbols.MockOf) for proper symbol-based detection.
  • Hoist ReferencedAssemblyNames string scan in SetupShouldNotIncludeAsyncResultAnalyzer from per-node Analyze into RegisterCompilationStartAction, performing the version check once per compilation instead of per syntax node.
  • All three analyzers now follow the established pattern: RegisterCompilationStartAction + IsMockReferenced() guard + closure-captured MoqKnownSymbols.
  • Remove duplicate WithKnownSymbols test variants that became identical after consolidation.

Test plan

  • All 2894 existing tests pass
  • Build succeeds with zero warnings and zero errors
  • EventSyntaxExtensions tests updated to use KnownSymbols parameter
  • Duplicate test methods removed to resolve S4144 warnings
  • Verified no remaining string-based type detection in analyzer source files

Closes #981

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Improved event signature validation for Raises operations in mock setups
    • Enhanced detection of Moq API method patterns
    • Refined Mock.Of invocation validation logic
  • Refactor

    • Consolidated and streamlined event validation utilities and logic
  • Tests

    • Expanded test coverage for event handling scenarios
    • Added comprehensive tests for Task/ValueTask Result properties
    • Increased validation coverage for raises-on-returns chain operations

Copilot AI review requested due to automatic review settings March 7, 2026 22:17
@chatgpt-codex-connector
Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 7, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 62a27b31-6e85-404f-886c-7d8dec309ad0

📥 Commits

Reviewing files that changed from the base of the PR and between 1e301b0 and 373a1e8.

📒 Files selected for processing (4)
  • src/Common/EventSyntaxExtensions.cs
  • src/Common/SemanticModelExtensions.cs
  • tests/Moq.Analyzers.Test/Common/EventSyntaxExtensionsTests.cs
  • tests/Moq.Analyzers.Test/Common/SemanticModelExtensionsTests.cs

📝 Walkthrough

Walkthrough

This PR refactors Moq analyzer symbol detection to replace string-based name matching with symbol-based type checking. It consolidates event argument validation logic, removes redundant invocation detection methods, and strengthens test coverage with data-driven scenarios targeting validation across multiple analyzer paths.

Changes

Cohort / File(s) Summary
Mock.Of Validation
src/Analyzers/LinqToMocksExpressionShouldBeValidAnalyzer.cs
Replaces string-based method name check with symbol-based instance check using knownSymbols.MockOf; validates target method via type identity instead of name equality.
Raises/Raise Analyzers
src/Analyzers/RaisesEventArgumentsShouldMatchEventSignatureAnalyzer.cs, src/Analyzers/RaiseEventArgumentsShouldMatchEventSignatureAnalyzer.cs
Removes private TryGetRaisesMethodArguments / TryGetRaiseMethodArguments helpers; delegates to new EventSyntaxExtensions.TryGetEventMethodArgumentsFromLambdaSelector and GetEventNameFromSelector for event extraction and naming.
Setup Analyzer Formatting
src/Analyzers/SetupShouldNotIncludeAsyncResultAnalyzer.cs
Minor formatting wrapping of Moq assembly lookup line; no semantic change to control flow.
Event Syntax Extension Consolidation
src/Common/EventSyntaxExtensions.cs
Major refactor: centralizes diagnostic creation via CreateEventDiagnostic, removes string-based IsActionDelegate and IsEventHandlerDelegate, adds new helpers (TryGetEventMethodArgumentsFromLambdaSelector, GetEventNameFromSelector), updates method signatures to require KnownSymbols instead of falling back to string matching.
Invocation Expression Cleanup
src/Common/InvocationExpressionSyntaxExtensions.cs
Removes IsRaisesMethodCall symbol-based detection method; logic migrated to SemanticModelExtensions.IsRaisesInvocation.
Semantic Model Extension Refactoring
src/Common/SemanticModelExtensions.cs
Introduces IsMoqFluentInvocation generic helper with fast-path name checking and fallback to symbol predicate; refactors IsRaisesInvocation and IsCallbackOrReturnInvocation to delegate to this helper with specific symbol predicates.
Symbol Extension Refinement
src/Common/ISymbolExtensions.Moq.cs
Refactors IsTaskOrValueResultProperty to early-exit for non-properties; simplifies IsGenericResultProperty signature to accept IPropertySymbol directly and check property name before generic containment.
Event Syntax Extensions Tests
tests/Moq.Analyzers.Test/Common/EventSyntaxExtensionsTests.cs
Introduces data-driven test matrix via multiple IEnumerable<object[]> providers; replaces direct unit tests with theory-based tests for GetEventParameterTypes, diagnostic creation, and event argument validation across various delegate shapes.
Invocation Expression Tests Cleanup
tests/Moq.Analyzers.Test/Common/InvocationExpressionSyntaxExtensionsTests.cs
Removes tests for IsRaisesMethodCall (3 test cases deleted); method relocated and tested in semantic model extension tests.
Raises Method Tests Updates
tests/Moq.Analyzers.Test/IsRaisesMethodTests.cs
Updates test documentation; removes inline test-purpose comments; no functional test changes.
Task/ValueTask Result Property Tests
tests/Moq.Analyzers.Test/Common/IsTaskOrValueResultPropertyTests.cs
New comprehensive test class covering positive and negative scenarios for IsTaskOrValueResultProperty and IsGenericResultProperty across Task, ValueTask, and custom delegate types.
Semantic Model Extension Tests
tests/Moq.Analyzers.Test/Common/SemanticModelExtensionsTests.cs
Refactors IsRaisesInvocation test flow with new IsRaisesInvocationForMemberAccessAsync helper; adds 6 new test cases validating Raises detection against non-Raises, non-Moq, and chained invocations.
Raises Analyzer Integration Tests
tests/Moq.Analyzers.Test/RaisesEventArgumentsShouldMatchEventSignatureAnalyzerTests.cs
Introduces ValidRaisesOnReturnsChainTestData and InvalidRaisesOnReturnsChainTestData providers; adds 5 new test methods validating analyzer behavior on valid/invalid Raises-on-Returns-chain and user-defined Raises method scenarios; extends test interface with additional members.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • MattKotsenas
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.89% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the primary change: replacing string-based detection with symbol-based detection across three analyzers and extensions.
Description check ✅ Passed The description covers the key changes and test results but lacks explicit 'Fixes' issue reference and 'Type of Change' checkbox selections.
Linked Issues check ✅ Passed All requirements from issue #981 are met: string-based detection in EventSyntaxExtensions, LinqToMocksExpressionShouldBeValidAnalyzer, and SetupShouldNotIncludeAsyncResultAnalyzer have been replaced with symbol-based detection.
Out of Scope Changes check ✅ Passed All changes align with issue #981 objectives: refactoring three analyzers and extensions to use symbol-based detection, with supporting test additions and package baseline updates.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/981-remove-string-detection

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

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the robustness and performance of several Moq analyzers by transitioning from brittle string-based type and method detection to more accurate and reliable symbol-based comparisons. It centralizes the initialization of MoqKnownSymbols and Moq version checks to occur once per compilation, rather than repeatedly for each analyzed syntax node, thereby improving analysis efficiency. Additionally, the changes streamline the EventSyntaxExtensions by removing redundant methods and enforcing the use of KnownSymbols for delegate type identification, leading to a cleaner and more maintainable codebase.

Highlights

  • Symbol-based Detection: Removed string-based IsActionDelegate/IsEventHandlerDelegate fallbacks in EventSyntaxExtensions, ensuring all delegate type checks now require KnownSymbols and use symbol comparison.
  • Refactored Mock.Of Detection: Replaced string.Equals(targetMethod.Name, "Of") with targetMethod.IsInstanceOf(knownSymbols.MockOf) in LinqToMocksExpressionShouldBeValidAnalyzer for proper symbol-based detection.
  • Performance Optimization: Hoisted the ReferencedAssemblyNames string scan in SetupShouldNotIncludeAsyncResultAnalyzer from per-node Analyze into RegisterCompilationStartAction, performing the Moq version check once per compilation.
  • Standardized Analyzer Pattern: All three affected analyzers now follow a consistent pattern: RegisterCompilationStartAction + IsMockReferenced() guard + closure-captured MoqKnownSymbols.
  • Test Consolidation: Removed duplicate WithKnownSymbols test variants that became identical after the consolidation of logic.
Changelog
  • src/Analyzers/LinqToMocksExpressionShouldBeValidAnalyzer.cs
    • Moved analyzer registration to RegisterCompilationStartAction to initialize MoqKnownSymbols once per compilation.
    • Updated AnalyzeInvocation to receive MoqKnownSymbols as a parameter.
    • Replaced string comparison for "Of" method name with symbol-based IsInstanceOf check.
  • src/Analyzers/RaisesEventArgumentsShouldMatchEventSignatureAnalyzer.cs
    • Refactored analyzer initialization to RegisterCompilationStartAction for MoqKnownSymbols setup.
    • Modified Analyze method to accept MoqKnownSymbols.
    • Updated TryGetRaisesMethodArguments to require KnownSymbols parameter.
  • src/Analyzers/SetupShouldNotIncludeAsyncResultAnalyzer.cs
    • Migrated Moq version check and MoqKnownSymbols instantiation to RegisterCompilationStartAction for compilation-level execution.
    • Adjusted Analyze method signature to include MoqKnownSymbols.
  • src/Common/EventSyntaxExtensions.cs
    • Removed redundant ValidateEventArgumentTypes overloads.
    • Eliminated string-based GetEventParameterTypes and TryGetEventMethodArguments overloads.
    • Made KnownSymbols a mandatory parameter for GetEventParameterTypesInternal, TryGetEventMethodArgumentsInternal, TryGetActionDelegateParameters, and TryGetEventHandlerDelegateParameters.
    • Removed IsActionDelegate and IsEventHandlerDelegate methods, relying solely on symbol-based checks.
  • tests/Moq.Analyzers.Test/Common/EventSyntaxExtensionsTests.cs
    • Updated all calls to GetEventParameterTypes and TryGetEventMethodArguments to pass KnownSymbols.
    • Removed several test methods that became redundant due to the consolidation of GetEventParameterTypes logic.
    • Removed the GetEventFieldType helper method.
Activity
  • All 2894 existing tests passed.
  • Build succeeded with zero warnings and zero errors.
  • EventSyntaxExtensions tests were updated to use KnownSymbols parameter.
  • Duplicate test methods were removed to resolve S4144 warnings.
  • Verified no remaining string-based type detection in analyzer source files.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@deepsource-io
Copy link
Copy Markdown

deepsource-io bot commented Mar 7, 2026

DeepSource Code Review

We reviewed changes in 4a7db6c...373a1e8 on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.

See full review on DeepSource ↗

PR Report Card

Overall Grade  

Focus Area: Hygiene
Security  

Reliability  

Complexity  

Hygiene  

Feedback

  • Inconsistent style enforcement across projects
    • Naming, redundant-type and parameter-order violations keep reappearing because coding rules aren't applied uniformly; codify formatting and naming in EditorConfig and promote Roslyn analyzers to enforced rules so fixes run automatically.
  • Test code lives outside automated fixes
    • The bulk of violations is concentrated in test folders, showing formatters/analyzers or auto-fixes aren't targeting tests; apply the same analyzer and formatting configuration to test projects and enable bulk code-fixes there.
  • No automated guardrails for public-API shape
    • Misordered parameters and naming slips indicate no analyzer catching API surface conventions; implement and enable analyzers that detect and offer code-fixes for parameter ordering and Task-naming to prevent recurrence.

Code Review Summary

Analyzer Status Updated (UTC) Details
C# Mar 9, 2026 1:17a.m. Review ↗

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request is a great improvement, refactoring several analyzers to use the more performant and robust RegisterCompilationStartAction pattern. Replacing string-based type detection with symbol-based detection significantly improves the reliability of the analyzers. The code is cleaner and more efficient. I have one minor suggestion regarding test coverage.

@rjmurillo rjmurillo added analyzers Change that impacts an analyzer behavior releasable labels Mar 7, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR strengthens analyzer correctness and consistency by removing remaining string-based fallbacks and standardizing on symbol-based detection patterns (with per-compilation MoqKnownSymbols), while also reducing per-node overhead in one analyzer.

Changes:

  • Removed string-based Action/EventHandler delegate detection fallbacks in EventSyntaxExtensions and required KnownSymbols for delegate checks.
  • Updated LinqToMocksExpressionShouldBeValidAnalyzer to detect Mock.Of via symbol identity (IsInstanceOf(knownSymbols.MockOf)) and moved to the compilation-start pattern with IsMockReferenced() guard.
  • Hoisted Moq version scanning in SetupShouldNotIncludeAsyncResultAnalyzer to RegisterCompilationStartAction to avoid repeated per-node scanning, and updated tests accordingly (including removing now-duplicate variants).

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tests/Moq.Analyzers.Test/Common/EventSyntaxExtensionsTests.cs Updates tests to pass KnownSymbols explicitly and removes duplicate test variants after API consolidation.
src/Common/EventSyntaxExtensions.cs Removes string-based fallbacks and makes delegate-type checks require KnownSymbols for symbol-based comparisons.
src/Analyzers/SetupShouldNotIncludeAsyncResultAnalyzer.cs Moves Moq reference/version checks to compilation start and closure-captures MoqKnownSymbols for node actions.
src/Analyzers/RaisesEventArgumentsShouldMatchEventSignatureAnalyzer.cs Switches to compilation-start pattern and threads knownSymbols through event-argument extraction.
src/Analyzers/LinqToMocksExpressionShouldBeValidAnalyzer.cs Replaces method-name string check with symbol-based Mock.Of detection and adopts compilation-start registration.

@codacy-production
Copy link
Copy Markdown

codacy-production bot commented Mar 7, 2026

Coverage summary from Codacy

See diff coverage on Codacy

Coverage variation Diff coverage
+0.06% (target: -1.00%) 93.33% (target: 95.00%)
Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (4a7db6c) 2508 2247 89.59%
Head commit (373a1e8) 2484 (-24) 2227 (-20) 89.65% (+0.06%)

Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: <coverage of head commit> - <coverage of common ancestor commit>

Diff coverage details
Coverable lines Covered lines Diff coverage
Pull request (#1030) 90 84 93.33%

Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: <covered lines added or modified>/<coverable lines added or modified> * 100%

See your quality gate settings    Change summary preferences

coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 7, 2026
coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 8, 2026
coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 8, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/Common/SemanticModelExtensions.cs (1)

78-91: ⚠️ Potential issue | 🟠 Major

Add a cheap Raises/RaisesAsync syntax guard before GetSymbolInfo().

RaisesEventArgumentsShouldMatchEventSignatureAnalyzer now reaches this helper from a syntax-node action on every invocation. Without a simple identifier prefilter, every member-access call in a Moq-referencing compilation pays the semantic lookup cost even when it is obviously unrelated.

Suggested fix
         if (raisesInvocation.Expression is not MemberAccessExpressionSyntax raisesMethod)
         {
             return false;
         }

+        string methodName = raisesMethod.Name.Identifier.ValueText;
+        if (!string.Equals(methodName, "Raises", StringComparison.Ordinal)
+            && !string.Equals(methodName, "RaisesAsync", StringComparison.Ordinal))
+        {
+            return false;
+        }
+
         SymbolInfo symbolInfo = semanticModel.GetSymbolInfo(raisesMethod);
         return symbolInfo.CandidateReason switch
         {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Common/SemanticModelExtensions.cs` around lines 78 - 91, The
IsRaisesInvocation method currently calls semanticModel.GetSymbolInfo for every
member-access invocation; add a cheap syntax-level guard before that to bail out
early for anything whose member name is not "Raises" or "RaisesAsync".
Concretely, inspect raisesMethod.Name (handle both IdentifierNameSyntax and
GenericNameSyntax) and if the identifier text is neither "Raises" nor
"RaisesAsync" return false immediately; only then call
semanticModel.GetSymbolInfo and continue with the existing
CandidateReason/IsRaisesSymbol logic. This keeps the rest of the method
(including IsRaisesSymbol and knownSymbols usage) unchanged.
src/Common/EventSyntaxExtensions.cs (1)

17-23: ⚠️ Potential issue | 🟠 Major

Validation skips typeless literals, creating false negatives for non-nullable value-type event parameters.

Line 53 guards validation with if (argumentType != null && ...), which means when GetTypeInfo(...).Type is null—as it is for typeless literals like null and default—the entire validation block is skipped. This allows cases like Raises(..., null) to pass without a diagnostic even when the event parameter is a non-nullable value type (e.g., Action<int>).

Add explicit handling: either classify the expression against expectedType before skipping, or emit a diagnostic for typeless literals passed to non-nullable value-type parameters. Include a regression test covering this edge case (e.g., Raises(x => x.IntEvent += null, null) where IntEvent: Action<int>).

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/Moq.Analyzers.Test/IsRaisesMethodTests.cs`:
- Around line 3-6: The test summary overstates coverage: current
IsRaisesMethodTests only asserts snippets are diagnostic-free so a failing
IsRaisesInvocation could still go undetected; either narrow the class summary to
state it only verifies absence of diagnostics for symbol-based Raises patterns,
or add a positive test in IsRaisesMethodTests that intentionally triggers the
symbol-based detection path (a snippet calling Raises with the specific pattern
your analyzer detects) and assert the analyzer reports the expected
Raises-specific diagnostic (use the same test helpers/assertion methods your
suite uses to expect a diagnostic) so the detection path is actually exercised.

---

Outside diff comments:
In `@src/Common/SemanticModelExtensions.cs`:
- Around line 78-91: The IsRaisesInvocation method currently calls
semanticModel.GetSymbolInfo for every member-access invocation; add a cheap
syntax-level guard before that to bail out early for anything whose member name
is not "Raises" or "RaisesAsync". Concretely, inspect raisesMethod.Name (handle
both IdentifierNameSyntax and GenericNameSyntax) and if the identifier text is
neither "Raises" nor "RaisesAsync" return false immediately; only then call
semanticModel.GetSymbolInfo and continue with the existing
CandidateReason/IsRaisesSymbol logic. This keeps the rest of the method
(including IsRaisesSymbol and knownSymbols usage) unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 049a41ed-139f-4605-9b99-c8ca97b0db2b

📥 Commits

Reviewing files that changed from the base of the PR and between 942999c and bb6f2ab.

📒 Files selected for processing (10)
  • src/Analyzers/LinqToMocksExpressionShouldBeValidAnalyzer.cs
  • src/Analyzers/RaisesEventArgumentsShouldMatchEventSignatureAnalyzer.cs
  • src/Analyzers/SetupShouldNotIncludeAsyncResultAnalyzer.cs
  • src/Common/EventSyntaxExtensions.cs
  • src/Common/ISymbolExtensions.cs
  • src/Common/InvocationExpressionSyntaxExtensions.cs
  • src/Common/SemanticModelExtensions.cs
  • tests/Moq.Analyzers.Test/Common/EventSyntaxExtensionsTests.cs
  • tests/Moq.Analyzers.Test/Common/InvocationExpressionSyntaxExtensionsTests.cs
  • tests/Moq.Analyzers.Test/IsRaisesMethodTests.cs
💤 Files with no reviewable changes (2)
  • src/Common/InvocationExpressionSyntaxExtensions.cs
  • tests/Moq.Analyzers.Test/Common/InvocationExpressionSyntaxExtensionsTests.cs

@coderabbitai coderabbitai bot requested a review from MattKotsenas March 8, 2026 03:41
coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 8, 2026
rjmurillo and others added 4 commits March 8, 2026 11:04
Remove string-based fallbacks that bypassed symbol-based detection:

- EventSyntaxExtensions: Remove string-based IsActionDelegate and
  IsEventHandlerDelegate fallbacks. Require KnownSymbols parameter
  for all delegate type checks.
- LinqToMocksExpressionShouldBeValidAnalyzer: Replace string comparison
  for "Of" method name with IsInstanceOf(knownSymbols.MockOf).
- SetupShouldNotIncludeAsyncResultAnalyzer: Hoist assembly version
  check to RegisterCompilationStartAction with IsMockReferenced guard.

All three analyzers now use RegisterCompilationStartAction with
IsMockReferenced guard and closure-captured MoqKnownSymbols, matching
the established pattern.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Merge GetEventParameterTypes_ActionWithSingleTypeArg_ReturnsSingleType
and GetEventParameterTypes_EventHandlerGeneric_ReturnsSingleTypeArgument
into a single [Theory] with [InlineData] to eliminate 16 lines of
duplicated test scaffolding (qlty similar-code, mass=78).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
IsRaisesMethodCall and IsRaisesInvocation performed identical logic:
extract MemberAccessExpressionSyntax, call GetSymbolInfo, check
IsMoqRaisesMethod, and handle OverloadResolutionFailure. The analyzer
called both in sequence, which was a DRY violation.

Remove IsRaisesMethodCall and its tests. Use IsRaisesInvocation as the
single implementation for Raises detection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Action<int, string> is Action`2, which IsActionDelegate does not match
(it only matches Action and Action<T>). The test actually exercises the
custom delegate fallback path via TryGetCustomDelegateParameters.

Rename from GetEventParameterTypes_ActionDelegate_ReturnsTypeArguments
to GetEventParameterTypes_MultiArgActionDelegate_FallsBackToCustomDelegateInvoke.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cover all code paths in ValidateEventArgumentTypes via the analyzer
pipeline: too few args, too many args, wrong types, exact match,
EventHandler subclass, Action<T>, custom delegates, zero-param
delegates, many-param delegates, and CreateEventDiagnostic branching.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 8, 2026
- Remove ValidateArgumentTypesWithEventName from RaiseEventArgumentsShouldMatchEventSignatureAnalyzer;
  reuse EventSyntaxExtensions.ValidateEventArgumentTypes instead
- Add static modifier to lambda in TryGetRaiseMethodArguments for consistency
- Consolidate 10 identical Theory test methods into 2 in EventSyntaxExtensionsTests
- Merge 6 CreateEventDiagnostic Fact tests into 2 covering same assertions
- Consolidate 4 duplicate positive-case Fact tests into 2 Theory tests in
  IsTaskOrValueResultPropertyTests and remove duplicate IsGenericResultProperty tests
- Fix SA1512 violations (comment followed by blank line)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 8, 2026
Resolved 3 conflict files from main merge:
- RaiseEventArgumentsShouldMatchEventSignatureAnalyzer: use extension method syntax
- RaisesEventArgumentsShouldMatchEventSignatureAnalyzer: keep GetEventNameFromSelector helper
- EventSyntaxExtensions: consolidate overloads with nullable optional KnownSymbols,
  retain CreateEventDiagnostic helper and improved docs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Redundant null coalescing on non-nullable return value
    • Removed the redundant ?? "event" from line 81 since GetEventNameFromSelector already returns a non-nullable string with its own fallback to "event".

rjmurillo and others added 3 commits March 8, 2026 16:01
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract shared event name extraction and lambda-based argument
extraction into EventSyntaxExtensions. Both Raise and Raises
analyzers now call the shared methods instead of maintaining
identical private implementations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 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/Common/EventSyntaxExtensions.cs`:
- Around line 61-75: GetEventParameterTypes lacks special handling for plain
System.EventHandler, causing it to fall back to TryGetCustomDelegateParameters
and return both (object, EventArgs) so Moq Raise/Raises are misvalidated; add a
dedicated handler (e.g., TryGetNonGenericEventHandlerParameters) that detects
when namedType.ConstructedFrom equals knownSymbols.EventHandler0 and returns
only the EventArgs parameter (invokeMethod.Parameters[1].Type), call this
handler before TryGetCustomDelegateParameters in GetEventParameterTypes, and
make KnownSymbols usage non-nullable in that path so EventHandler and
EventHandler<T> are resolved correctly; update tests to expect a single
parameter for non-generic EventHandler and add analyzer tests for direct
Raise/Raises with plain EventHandler.

In `@tests/Moq.Analyzers.Test/Common/IsTaskOrValueResultPropertyTests.cs`:
- Around line 161-180: Two tests,
IsTaskOrValueResultProperty_CustomClassWithResultProperty_ReturnsFalse and
IsGenericResultProperty_ResultOnUnrelatedGenericType_ReturnsFalse, exercise the
same scenario (a user-defined generic type with a Result property) and should be
consolidated or made distinct; update the tests so only one covers the generic
user-type case (keeping the existing assertion that
ISymbol.IsTaskOrValueResultProperty returns false) and either remove the
duplicate test or change the second test to target a different scenario (e.g., a
non-generic type, a nested type, or a Result property with a Task/ValueTask
wrapper) while using the existing helpers GetPropertyFromMemberAccess and
MoqKnownSymbols to fetch the property for assertion.
- Around line 242-253: The helper GetPropertyFromMemberAccess currently casts
model.GetSymbolInfo(memberAccess).Symbol! to IPropertySymbol without checking
for null; add a defensive null check after retrieving the symbol (e.g., var
symbol = model.GetSymbolInfo(memberAccess).Symbol) and throw or Assert.Fail with
a clear message if symbol is null or not an IPropertySymbol, then cast to
IPropertySymbol (prop) so test failures show a clear diagnostic; update
references to memberAccess and prop accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 229461b6-d19f-4020-b3a5-f479ab1f02f7

📥 Commits

Reviewing files that changed from the base of the PR and between afae7f1 and 1e301b0.

📒 Files selected for processing (11)
  • src/Analyzers/RaiseEventArgumentsShouldMatchEventSignatureAnalyzer.cs
  • src/Analyzers/RaisesEventArgumentsShouldMatchEventSignatureAnalyzer.cs
  • src/Common/EventSyntaxExtensions.cs
  • src/Common/ISymbolExtensions.Moq.cs
  • tests/Moq.Analyzers.Test/Common/EventSyntaxExtensionsTests.cs
  • tests/Moq.Analyzers.Test/Common/IsTaskOrValueResultPropertyTests.cs
  • tests/Moq.Analyzers.Test/Common/SemanticModelExtensionsTests.cs
  • tests/Moq.Analyzers.Test/PackageTests.Baseline#contents.verified.txt
  • tests/Moq.Analyzers.Test/PackageTests.Baseline_main#contents.verified.txt
  • tests/Moq.Analyzers.Test/PackageTests.Baseline_symbols#contents.verified.txt
  • tests/Moq.Analyzers.Test/RaisesEventArgumentsShouldMatchEventSignatureAnalyzerTests.cs

coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 9, 2026
Source: extract IsMoqFluentInvocation shared helper from duplicate
IsCallbackOrReturnInvocation/IsRaisesInvocation methods. Replace LINQ
allocation with direct loop in TryGetCustomDelegateParameters.

Tests: extract IsRaisesInvocationForMemberAccessAsync helper to remove
repeated compilation boilerplate. Consolidate multi-param delegate tests
into Theory-based parameterization.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
rjmurillo and others added 3 commits March 8, 2026 18:04
…nullable dead code

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move static field initializations into a static constructor to satisfy
ECS1300. Suppress S3963 which conflicts with ECS1300.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@rjmurillo rjmurillo merged commit a974999 into main Mar 9, 2026
39 of 43 checks passed
@rjmurillo rjmurillo deleted the fix/981-remove-string-detection branch March 9, 2026 01:37
@rjmurillo rjmurillo added this to the vNext milestone Mar 9, 2026
rjmurillo added a commit that referenced this pull request Mar 9, 2026
The analyzer now correctly detects type mismatches in generic Callback<T>()
syntax after symbol-based detection was introduced in PR #1030. The previous
'known limitation' test expected no diagnostic, but the analyzer now correctly
produces Moq1100.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
rjmurillo added a commit that referenced this pull request Mar 9, 2026
Integrate symbol-based detection (#1030) and simple lambda support (#1042)
from main with error handling improvements (#1040) on this branch.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
rjmurillo added a commit that referenced this pull request Mar 9, 2026
…tch (#1057)

## Summary
- Renamed `GenericCallbackValidation_CurrentLimitation_IsDocumented` to
`GenericCallbackWithWrongType_ProducesDiagnostic`
- Updated the test to expect Moq1100 diagnostic on
`.Callback<int>(wrongTypeParam => { })` where the mocked method takes
`string`
- The "known limitation" no longer exists after PR #1030 introduced
symbol-based detection, which correctly resolves generic type arguments

## Test plan
- [x] Both `Net80WithOldMoq` and `Net80WithNewMoq` variants pass locally
(2/2 passed)
- [ ] CI passes on this branch

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Richard Murillo <rjmurillo@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

analyzers Change that impacts an analyzer behavior bug releasable

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: String-based detection fallbacks violate symbol-based detection mandate

3 participants