Skip to content

SA1130: report anonymous methods whose parameter names differ from the delegate's - #4179

Merged
AArnott merged 1 commit into
masterfrom
sa1130-anonymous-method-parameter-names
Oct 8, 2026
Merged

AArnott merged 1 commit into
masterfrom
sa1130-anonymous-method-parameter-names

Conversation

@AArnott

@AArnott AArnott commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

SA1130 never reported an anonymous method passed as an argument if its parameter names differed from the delegate's parameter names. For example, list.Select(delegate (int x) { return x; }) was never reported.

Cause

To make sure the lambda binds to the same overload, the analyzer speculatively binds the call with a lambda in place of the anonymous method. It built that lambda from the delegate's parameter list (arg for Func<T, TResult>) instead of the anonymous method's own list. When the names differed, the body (return x;) didn't bind, the speculative binding failed, and nothing was reported. The existing test TestAnonymousMethodInferringTypeArgumentAsync names its parameter arg to work around this, and its doc comment says so.

Fix

  • Analyzer: an anonymous method with a parameter list now uses its own list for the speculative binding. The delegate's list is still used for delegate { } with no parameter list, which can't become a parameterless lambda.
  • Code fix: the newly reported cases exposed two code fix bugs, now fixed:
    • Removing parameter types could make a call ambiguous: Overloaded(delegate (int value) { return 0; }) with Func<int,int>/Func<string,int> overloads became Overloaded(value => { return 0; }) (CS0121). The fix now keeps the types when the call wouldn't bind to the same method without them.
    • An anonymous method with ref/out parameters was reported, but the code fix did nothing. It now produces (ref int x) => { ... }.
  • Docs: SA1130.md now says when the code fix keeps parameter names and types.

Coordination with #4133 and #4147

This branches from current master, which already has both:

GetDelegateParameterList is now only called for anonymous methods without a parameter list. Its params collection handling from #4147 still applies there, and the TestParamsAsync cases pass.

Behavior changes

SA1130 now reports anonymous methods passed as arguments (to methods, constructors and indexers) whose parameter names differ from the delegate's, e.g. list.Select(delegate (int x) { return x; }), Single(delegate (int value) { return value; }) and Multiple(delegate (int first, string second) { }). Existing code that uses this pattern will get new SA1130 warnings. All of them have a working code fix.

Tests

Added to SA1130UnitTests, so they run in every language version's project:

  • TestAnonymousMethodWithDifferentParameterNamesAsync: methods, multiple parameters, a constructor and an indexer. The fix gives value => ... and (first, second) => ....
  • TestAnonymousMethodWithDifferentParameterNamesPassedToLinqAsync: list.Select(delegate (int x) ...) becomes list.Select((int x) => ...).
  • TestAnonymousMethodWithOverloadsDifferingInParameterTypesAsync: the types are kept to avoid ambiguity.
  • TestAnonymousMethodWithRefAndOutParametersAsync.

All SA1130 tests pass locally in the C# 6, 7, 10, 13 and 15 projects (Linux, net6.0).

… differ from the delegate's

To check that a lambda binds to the same method, the analyzer built the lambda
from the delegate's parameter list instead of the anonymous method's own. When
the names differed, e.g. list.Select(delegate (int x) { return x; }), the body
no longer bound, the speculative binding failed, and nothing was reported. An
anonymous method with a parameter list now uses its own list; the delegate's
list is only used when the anonymous method has no parameter list.

These newly reported cases exposed two code fix problems, which are also fixed:

- Removing the parameter types could make a call ambiguous between overloads
  taking delegates with different parameter types. The code fix now keeps the
  types when the call wouldn't bind to the same method without them.
- An anonymous method with ref or out parameters was left unchanged by the
  code fix. It now becomes a lambda with explicit parameter types and modifiers.
@codecov

codecov Bot commented Oct 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.87234% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 97.50%. Comparing base (41c091f) to head (131561e).

Additional details and impacted files
@@           Coverage Diff            @@
##           master    #4179    +/-   ##
========================================
  Coverage   97.50%   97.50%            
========================================
  Files        1236     1236            
  Lines      131895   132077   +182     
  Branches     4276     4279     +3     
========================================
+ Hits       128602   128780   +178     
- Misses       2153     2157     +4     
  Partials     1140     1140            
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@AArnott
AArnott marked this pull request as ready for review October 8, 2026 20:37
@AArnott
AArnott merged commit 6536231 into master Oct 8, 2026
25 checks passed
@AArnott
AArnott deleted the sa1130-anonymous-method-parameter-names branch October 8, 2026 20:43
This was referenced Nov 11, 2026
This was referenced Nov 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant