Skip to content

fix: filter non-lambda-rooted members in Moq1302 to eliminate false positives#1017

Merged
rjmurillo merged 5 commits intomainfrom
bug/1010-false-positive-moq1302
Mar 7, 2026
Merged

fix: filter non-lambda-rooted members in Moq1302 to eliminate false positives#1017
rjmurillo merged 5 commits intomainfrom
bug/1010-false-positive-moq1302

Conversation

@rjmurillo
Copy link
Copy Markdown
Owner

@rjmurillo rjmurillo commented Mar 7, 2026

Summary

Fixes #1010. The Moq1302 analyzer (LinqToMocksExpressionShouldBeValidAnalyzer) produced false positives when Mock.Of<T>() expressions contained static members, constants, or enum values in binary comparisons (e.g., r => r.Status == StatusCodes.Status200OK).

Root cause: The analyzer walked both sides of binary expressions without checking whether the member access originated from the lambda parameter. Static members (Instance == null), constants, and external instance properties were incorrectly flagged as invalid mock members.

Fix:

  • Add IsRootedInLambdaParameter extension method to IOperationExtensions that walks the receiver chain to verify a member access terminates in the lambda parameter
  • Apply the guard in AnalyzeMemberOperations only to leaf member operations (IMemberReferenceOperation, IInvocationOperation), allowing composite operations (IBinaryOperation for chained &&/||/==) to pass through for decomposition
  • Thread MoqKnownSymbols through the call chain to eliminate per-operation allocation
  • Route the default branch child operations through the guarded path to prevent bypass
  • Achieve 100% block code coverage on all changed lines

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature with breaking side effects)
  • Documentation update

Changes

File Change
src/Analyzers/LinqToMocksExpressionShouldBeValidAnalyzer.cs Add IsRootedInLambdaParameter guard (leaf ops only), thread MoqKnownSymbols, route default branch through guarded path, tighten AnalyzeMemberOperations parameter to non-nullable
src/Common/IOperationExtensions.cs Add IsRootedInLambdaParameter extension method with IMemberReferenceOperation consolidated receiver chain walk, while(true) loop structure
tests/.../LinqToMocksExpressionShouldBeValidAnalyzerTests.cs Add 16 boundary case tests, convert existing [Fact] to [Theory] with dual Moq version coverage

Test Plan

  • 16 new boundary case tests covering: static const, static property, enum value, reversed operands, chained &&, static method call, non-virtual member (true positive), field on lambda parameter (true positive), external instance property, ternary with static member, null-coalescing, conditional with mixed sources, chained property access, nested Mock.Of, explicit cast receiver chain, virtual instance method
  • All tests run against both Moq 4.8.2 and 4.18.4 via [Theory] with MoqReferenceAssemblyGroups
  • Existing ShouldNotAnalyzeNonMockOfInvocations converted from [Fact] to [Theory] for dual-version coverage
  • 100% block code coverage on all changed lines (verified via coverlet)
  • All tests pass locally

Known Limitations

IConditionalOperation (ternary expressions) inside Mock.Of lambdas are not fully analyzed. Non-virtual members inside ternary branches are not flagged (false negative). This is a deliberate trade-off to prevent false positives. Documented in the ShouldNotFlagConditionalWithMixedSources test.

IParenthesizedOperation is intentionally omitted from IsRootedInLambdaParameter. The C# compiler never emits it in IOperation trees (VB.NET only), and this analyzer targets C# exclusively via [DiagnosticAnalyzer(LanguageNames.CSharp)].

Security Impact

None. Changes are limited to compile-time Roslyn analyzer logic with no runtime attack surface.

Summary by CodeRabbit

  • Bug Fixes

    • Improved Linq to Mocks expression validation to correctly handle comparisons, chained properties, and conditional expressions involving external members and static values.
  • Tests

    • Expanded test coverage across multiple Moq versions with scenarios for ternaries, null-coalescing operators, nested comparisons, and complex expression patterns.

…ositives

Add IsRootedInLambdaParameter guard to prevent Moq1302 from flagging
static members, constants, enum values, and external instance members
in Mock.Of binary expressions. Thread MoqKnownSymbols through the call
chain to eliminate per-operation allocation. Route default branch child
operations through the guarded path.

Fixes #1010

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 7, 2026 04:39
@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.

@rjmurillo rjmurillo added bug analyzers Change that impacts an analyzer behavior releasable labels Mar 7, 2026
@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 addresses and resolves an issue where the Moq1302 analyzer incorrectly flagged static members, constants, and enum values within Mock.Of<T>() expressions as invalid mock members. The core of the solution involves introducing a mechanism to accurately determine if an operation's receiver chain is rooted in the lambda parameter, thereby filtering out value expressions that should not be analyzed. This enhancement significantly reduces false positives and improves the accuracy of the analyzer.

Highlights

  • False Positive Elimination: The Moq1302 analyzer no longer produces false positives when Mock.Of<T>() expressions contain static members, constants, or enum values in binary comparisons.
  • Lambda Parameter Rooting Check: A new extension method, IsRootedInLambdaParameter, was added to IOperationExtensions to verify if a member access originates from the lambda parameter, preventing incorrect flagging of static or external members.
  • Optimized Symbol Handling: MoqKnownSymbols is now threaded through the call chain to eliminate per-operation allocation, improving performance.
  • Comprehensive Guard Application: The default branch child operations in AnalyzeLambdaBody are now routed through the guarded path to ensure the IsRootedInLambdaParameter check is consistently applied, preventing bypasses.
Changelog
  • src/Analyzers/LinqToMocksExpressionShouldBeValidAnalyzer.cs
    • Updated AnalyzeMockOfArguments, AnalyzeLambdaExpression, and AnalyzeLambdaBody methods to pass MoqKnownSymbols for optimized symbol handling.
    • Modified AnalyzeLambdaBody to ensure all recursive analysis paths, including the default case for complex operations, route through AnalyzeMemberOperations.
    • Refactored AnalyzeMemberOperations to incorporate the IsRootedInLambdaParameter guard, filtering out operations not directly related to the lambda parameter.
  • src/Common/IOperationExtensions.cs
    • Added a new extension method IsRootedInLambdaParameter to determine if an operation's receiver chain originates from the given lambda's parameter.
  • tests/Moq.Analyzers.Test/LinqToMocksExpressionShouldBeValidAnalyzerTests.cs
    • Introduced MoqReferenceAssemblyGroups to provide both old and new Moq reference assembly versions for parameterized tests.
    • Added 13 new boundary case tests covering various scenarios like static constants, properties, enum values, chained comparisons, and external instance properties.
    • Converted existing [Fact] tests to [Theory] to ensure dual Moq version coverage for all relevant tests.
Activity
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.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 7, 2026

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

Threaded MoqKnownSymbols through LinqToMocksExpressionShouldBeValidAnalyzer analysis paths and added IOperationExtensions.IsRootedInLambdaParameter to determine whether operations originate from a lambda parameter. Refined member-operation traversal and guards; expanded theory-driven tests across multiple Moq reference assemblies.

Changes

Cohort / File(s) Summary
Analyzer Refactoring
src/Analyzers/LinqToMocksExpressionShouldBeValidAnalyzer.cs
Threaded MoqKnownSymbols through AnalyzeInvocationAnalyzeMockOfArgumentsAnalyzeLambdaExpressionAnalyzeLambdaBodyAnalyzeMemberOperations. Updated traversal to route composite operations through the lambda-parameter guard, skip nested Mock.Of invocations, and analyze both sides of binary operations. Added inline docs clarifying single-entry and guard behavior.
Operation Helper
src/Common/IOperationExtensions.cs
Added internal static bool IsRootedInLambdaParameter(this IOperation operation, IAnonymousFunctionOperation lambdaOperation) to walk receiver chains (instance members, invocations, conversions) and detect termination at the lambda's first parameter; used by analyzer to filter external/static members.
Tests — Coverage Expansion
tests/Moq.Analyzers.Test/LinqToMocksExpressionShouldBeValidAnalyzerTests.cs
Replaced a single fact with a theory-driven suite using MoqReferenceAssemblyGroups() and added many parameterized tests (static constants/properties/fields/methods, enums, chained/conditional/null-coalescing expressions, virtual vs non-virtual members, nested mocks) to cover behavior across Moq versions.

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 17.65% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title directly describes the main fix: filtering non-lambda-rooted members in Moq1302 analyzer to eliminate false positives.
Linked Issues check ✅ Passed Code changes successfully implement the requirements from issue #1010: IsRootedInLambdaParameter guards member accesses, knownSymbols are threaded through call chain, and comprehensive tests validate the fix across multiple scenarios.
Out of Scope Changes check ✅ Passed All changes align with the stated objectives: analyzer fix in LinqToMocksExpressionShouldBeValidAnalyzer, new extension method IsRootedInLambdaParameter, and expanded test coverage directly address issue #1010 without introducing unrelated modifications.

✏️ 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 bug/1010-false-positive-moq1302

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

@deepsource-io
Copy link
Copy Markdown

deepsource-io bot commented Mar 7, 2026

DeepSource Code Review

We reviewed changes in 6f6dcf9...e136faf 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

  • Analyzer naming rule indiscriminately flags tests
    • The Task-returning naming check is applied to test methods, which commonly omit an "Async" suffix; narrowing the rule to ignore recognised test attributes/frameworks or marking test projects as exempt will stop these false positives.
  • Bulk test creation without naming normalization
    • Many identical violations across a single test file indicate copy/paste or generated tests that never followed the project's naming convention; fixing the generator/templates or running a one-off codemod to append "Async" breaks the repetition.
  • Tooling lacks an automated remediation path
    • Repeated, trivial renames show there's no automated code-fix or template enforcement; adding an IDE/Roslyn code fix or an EditorConfig naming rule with auto-apply prevents recurrence and cleans existing occurrences quickly.

Code Review Summary

Analyzer Status Updated (UTC) Details
C# Mar 7, 2026 5:49a.m. Review ↗

Comment thread src/Common/IOperationExtensions.cs Outdated
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 effectively fixes a bug causing false positives in the Moq1302 analyzer by introducing a guard that verifies member accesses are rooted in the lambda parameter. The changes are well-implemented, including a new IsRootedInLambdaParameter extension method and significant improvements to the test suite with 13 new boundary-case tests and dual Moq version coverage. The refactoring to pass MoqKnownSymbols down the call stack is also a good improvement for efficiency. I've suggested a minor refactoring in IsRootedInLambdaParameter to reduce code duplication, but overall this is an excellent contribution.

Comment thread src/Common/IOperationExtensions.cs
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

Fixes Moq1302 false positives in LINQ-to-Mocks (Mock.Of<T>(...)) expressions by ensuring only member accesses rooted in the lambda parameter are analyzed/reported, avoiding flagging static/constant/enum/external-value expressions.

Changes:

  • Add IOperationExtensions.IsRootedInLambdaParameter(...) to determine whether a receiver chain originates from the Mock.Of lambda parameter.
  • Thread MoqKnownSymbols through the analyzer call chain and route recursive analysis through a guarded entry point.
  • Expand Moq1302 tests with boundary cases and run them against both old/new Moq reference assemblies.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/Analyzers/LinqToMocksExpressionShouldBeValidAnalyzer.cs Adds guarded recursion + threads MoqKnownSymbols to reduce allocations and avoid static-member false positives.
src/Common/IOperationExtensions.cs Introduces receiver-chain helper IsRootedInLambdaParameter(...).
tests/Moq.Analyzers.Test/LinqToMocksExpressionShouldBeValidAnalyzerTests.cs Adds boundary-case tests and dual-version Moq coverage for the new scenarios.

Comment thread src/Analyzers/LinqToMocksExpressionShouldBeValidAnalyzer.cs Outdated
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: Chained &&/|| comparisons silently skip member analysis
    • The fix narrows the IsRootedInLambdaParameter guard to only apply to leaf member operations, allowing IBinaryOperation nodes to pass through for recursive decomposition.

Comment thread src/Analyzers/LinqToMocksExpressionShouldBeValidAnalyzer.cs
The guard was applied to all operations, causing false negatives for
chained comparisons (&&, ||). Composite operations like IBinaryOperation
have no receiver chain and silently failed the rooted check.

Changes:
- Narrow guard to IMemberReferenceOperation and IInvocationOperation only
- Consolidate property/field/event cases into IMemberReferenceOperation
- Add 5 regression tests for chained &&, ||, and null-coalescing
- Update ternary test to expect diagnostic (no longer false negative)

All 138 Moq1302 tests pass.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comment thread src/Common/IOperationExtensions.cs Outdated
rjmurillo and others added 2 commits March 6, 2026 21:00
CI builds with PedanticMode=true (TreatWarningsAsErrors). The nested
if statement in AnalyzeMemberOperations triggered S1066. Merged into
a single condition using parenthesized pattern matching.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@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.14% (target: -1.00%) 100.00% (target: 95.00%)
Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (6f6dcf9) 2418 2159 89.29%
Head commit (e136faf) 2432 (+14) 2175 (+16) 89.43% (+0.14%)

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 (#1017) 28 28 100.00%

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
@rjmurillo rjmurillo added this to the v0.4.2 milestone Mar 7, 2026
- Remove unreachable null guard in AnalyzeMemberOperations by
  tightening parameter type from IOperation? to IOperation
- Restructure IsRootedInLambdaParameter loop from while(current!=null)
  to while(true) eliminating unreachable post-loop return
- Fold parameterless-lambda guard into inline null check at comparison
  site, removing uncoverable early-return block
- Add tests: nested Mock.Of, explicit cast receiver chain, virtual
  instance method on lambda parameter

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@rjmurillo rjmurillo merged commit 4b705e2 into main Mar 7, 2026
41 of 44 checks passed
@rjmurillo rjmurillo deleted the bug/1010-false-positive-moq1302 branch March 7, 2026 06:25
rjmurillo added a commit that referenced this pull request Mar 7, 2026
Add 10 new test scenarios covering real-world patterns that triggered
false positives in Moq1302 (LinqToMocksExpressionShouldBeValid):

- Exact reporter pattern: nested Mock.Of inside method argument
- Static lambda with external constant
- Captured local variable as comparison value
- Method parameter as comparison value
- Instance field (this member) as comparison value
- Inequality operators (!=) with external constants
- Chained external instance property (multi-hop)
- Non-virtual method with external arguments (true positive)
- String concatenation on right side of comparison
- || with all external constants (no false positives)

These complement the existing 22 tests from PR #1017 to ensure the
IsRootedInLambdaParameter guard handles every common customer pattern.

Closes #1010

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

## Summary

Adds 10 new test scenarios for Moq1302
(`LinqToMocksExpressionShouldBeValid`) to ensure the
`IsRootedInLambdaParameter` guard from PR #1017 handles every common
customer pattern. These tests run against both Moq 4.8.2 and 4.18.4.

### New test scenarios

**False positive prevention (should NOT flag):**
- Exact reporter pattern: nested `Mock.Of` inside method argument
(#1010)
- `static` lambda with external constant (reporter's exact syntax)
- Captured local variable as comparison value
- Method parameter as comparison value
- Instance field (`this` member) as comparison value
- Inequality operator (`!=`) with external constant
- Chained external instance property (multi-hop:
`settings.Service.DefaultName`)
- String concatenation on right side (`Prefix.Value + "-default"`)
- `||` with all external constants

**True positive preservation (SHOULD flag):**
- Non-virtual method with external arguments still flagged

### Test results

- **Before**: 144 Moq1302 tests
- **After**: 164 Moq1302 tests (+20, each runs against 2 Moq versions)
- **Full suite**: 2892 tests, 0 failures

## Test plan

- [x] All 164 Moq1302 tests pass
- [x] Full suite (2892 tests) passes
- [x] Build: 0 warnings, 0 errors

Closes #1010

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

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Tests**
* Expanded test coverage with comprehensive scenarios for analyzer
validation, including edge cases with external variables, method
parameters, instance fields, static lambdas, chained properties, and
comparison operations to improve reliability across complex patterns.

<!-- 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>
rjmurillo added a commit that referenced this pull request Mar 7, 2026
…ositives (#1017)

Fixes #1010. The Moq1302 analyzer
(`LinqToMocksExpressionShouldBeValidAnalyzer`) produced false positives
when `Mock.Of<T>()` expressions contained static members, constants, or
enum values in binary comparisons (e.g., `r => r.Status ==
StatusCodes.Status200OK`).

**Root cause**: The analyzer walked both sides of binary expressions
without checking whether the member access originated from the lambda
parameter. Static members (`Instance == null`), constants, and external
instance properties were incorrectly flagged as invalid mock members.

**Fix**:
- Add `IsRootedInLambdaParameter` extension method to
`IOperationExtensions` that walks the receiver chain to verify a member
access terminates in the lambda parameter
- Apply the guard in `AnalyzeMemberOperations` only to leaf member
operations (`IMemberReferenceOperation`, `IInvocationOperation`),
allowing composite operations (`IBinaryOperation` for chained
`&&`/`||`/`==`) to pass through for decomposition
- Thread `MoqKnownSymbols` through the call chain to eliminate
per-operation allocation
- Route the `default` branch child operations through the guarded path
to prevent bypass
- Achieve 100% block code coverage on all changed lines

- [x] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature with breaking side effects)
- [ ] Documentation update

| File | Change |
|------|--------|
| `src/Analyzers/LinqToMocksExpressionShouldBeValidAnalyzer.cs` | Add
`IsRootedInLambdaParameter` guard (leaf ops only), thread
`MoqKnownSymbols`, route default branch through guarded path, tighten
`AnalyzeMemberOperations` parameter to non-nullable |
| `src/Common/IOperationExtensions.cs` | Add `IsRootedInLambdaParameter`
extension method with `IMemberReferenceOperation` consolidated receiver
chain walk, `while(true)` loop structure |
| `tests/.../LinqToMocksExpressionShouldBeValidAnalyzerTests.cs` | Add
16 boundary case tests, convert existing `[Fact]` to `[Theory]` with
dual Moq version coverage |

- [x] 16 new boundary case tests covering: static const, static
property, enum value, reversed operands, chained `&&`, static method
call, non-virtual member (true positive), field on lambda parameter
(true positive), external instance property, ternary with static member,
null-coalescing, conditional with mixed sources, chained property
access, nested Mock.Of, explicit cast receiver chain, virtual instance
method
- [x] All tests run against both Moq 4.8.2 and 4.18.4 via `[Theory]`
with `MoqReferenceAssemblyGroups`
- [x] Existing `ShouldNotAnalyzeNonMockOfInvocations` converted from
`[Fact]` to `[Theory]` for dual-version coverage
- [x] 100% block code coverage on all changed lines (verified via
coverlet)
- [x] All tests pass locally

`IConditionalOperation` (ternary expressions) inside `Mock.Of` lambdas
are not fully analyzed. Non-virtual members inside ternary branches are
not flagged (false negative). This is a deliberate trade-off to prevent
false positives. Documented in the
`ShouldNotFlagConditionalWithMixedSources` test.

`IParenthesizedOperation` is intentionally omitted from
`IsRootedInLambdaParameter`. The C# compiler never emits it in
IOperation trees (VB.NET only), and this analyzer targets C# exclusively
via `[DiagnosticAnalyzer(LanguageNames.CSharp)]`.

None. Changes are limited to compile-time Roslyn analyzer logic with no
runtime attack surface.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

* **Bug Fixes**
* Improved Linq to Mocks expression validation to correctly handle
comparisons, chained properties, and conditional expressions involving
external members and static values.

* **Tests**
* Expanded test coverage across multiple Moq versions with scenarios for
ternaries, null-coalescing operators, nested comparisons, and complex
expression patterns.

<!-- 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>
rjmurillo added a commit that referenced this pull request Mar 7, 2026
…1020)

## Summary

Adds 10 new test scenarios for Moq1302
(`LinqToMocksExpressionShouldBeValid`) to ensure the
`IsRootedInLambdaParameter` guard from PR #1017 handles every common
customer pattern. These tests run against both Moq 4.8.2 and 4.18.4.

### New test scenarios

**False positive prevention (should NOT flag):**
- Exact reporter pattern: nested `Mock.Of` inside method argument
(#1010)
- `static` lambda with external constant (reporter's exact syntax)
- Captured local variable as comparison value
- Method parameter as comparison value
- Instance field (`this` member) as comparison value
- Inequality operator (`!=`) with external constant
- Chained external instance property (multi-hop:
`settings.Service.DefaultName`)
- String concatenation on right side (`Prefix.Value + "-default"`)
- `||` with all external constants

**True positive preservation (SHOULD flag):**
- Non-virtual method with external arguments still flagged

### Test results

- **Before**: 144 Moq1302 tests
- **After**: 164 Moq1302 tests (+20, each runs against 2 Moq versions)
- **Full suite**: 2892 tests, 0 failures

## Test plan

- [x] All 164 Moq1302 tests pass
- [x] Full suite (2892 tests) passes
- [x] Build: 0 warnings, 0 errors

Closes #1010

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

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Tests**
* Expanded test coverage with comprehensive scenarios for analyzer
validation, including edge cases with external variables, method
parameters, instance fields, static lambdas, chained properties, and
comparison operations to improve reliability across complex patterns.

<!-- 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>
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.

False positive in Moq1302: Invalid member 'StatusCodes.Status202Accepted' in LINQ to Mocks expression

2 participants