Skip to content

Transaction analyzer: fix local functions / lambdas - #3166

Merged
mgravell merged 1 commit into
mainfrom
marc/tran-analyzer-local-functions
Aug 6, 2026
Merged

Transaction analyzer: fix local functions / lambdas#3166
mgravell merged 1 commit into
mainfrom
marc/tran-analyzer-local-functions

Conversation

@mgravell

@mgravell mgravell commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

The new analyzer doesn't fire for local methods and lambdas; fix that.

docs/rules/index.md stated the old blanket behaviour as an intentional limitation; corrected.

No AnalyzerReleases change: detection coverage only, no new or altered diagnostic IDs.

Checklist

  • I fully and freely contribute this code in accordance with the project license (and am legally able to do so)
  • I take responsibility for this contribution's quality and correctness, including any portions produced with AI assistance (see CONTRIBUTING.md).

…queue more than once"

TryGetBranch lumped IAnonymousFunctionOperation and ILocalFunctionOperation in with ILoopOperation and
returned false, which the caller reads as "this call can queue N times" and uses to disqualify the whole
transaction. Roslyn hands out no separate operation block for a lambda or local function - the body arrives
as part of the containing method - so every transaction written inside one was silently invisible. That
includes top-level statements, where the entire program body is one synthesised method, so the analyzer said
nothing at all about the commonest way a small repro gets written.

The repeat risk belongs to the *captured* transaction, not to the boundary itself. A transaction that is a
local of the function being walked out of is created afresh on each invocation, so one invocation holds one
whole transaction and the counts within it are exact; whatever encloses the function governs how many
transactions there are, not what goes into each. The two boundary cases now stop the walk and accept when the
transaction local belongs to that function, and keep returning false when it was captured from outside. A
loop inside such a function is still hit first, as it must be.

Tests: the two existing negatives were already the captured shape, so they are unchanged and still pass. Four
added - transaction declared in a local function (the reported shape), in a lambda, in a local function called
in a loop (N transactions, not one with N commands), and a loop inside such a function (still suppressed).

docs/rules/index.md stated the old blanket behaviour as an intentional limitation; corrected.

No AnalyzerReleases change: detection coverage only, no new or altered diagnostic IDs.
@mgravell
mgravell merged commit 5517cbf into main Aug 6, 2026
3 of 4 checks passed
@mgravell
mgravell deleted the marc/tran-analyzer-local-functions branch August 6, 2026 10:42
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