You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Lambda bodies declared inside a member that carries [ExcludeFromCodeCoverage]and is async (or
an iterator) remain in the coverage denominator after the issue #457 closure filter. This is residual
(a) of #457: a deliberate, documented scope choice rather than an oversight, recorded so it can be
addressed on its own terms rather than by widening #457.
Remove-CoberturaExemptClosureCoverage (added by #457 in scripts/vscode/Invoke-MSTestWithCoverage.ClosureFilter.ps1) infers that a member is exempt from its
absence from the report's instrumented method set. An async member emits no plain <method>
element - its whole body moves to a state machine class Type.<Member>d__<N> - so the filter must
admit d__ class names as proof that the declaring member exists. That admission is mandatory in the
other direction: without it, covered lambdas inside non-exempt async members would be deleted,
failing #457's required direction 2. The consequence is that an attributed async member also enters
the presence set, and its lambdas are retained.
Environment
OS/version: Windows 11 Pro 10.0.26200
.NET/framework: .NET Framework 4.8.1 (net481); post-processing runs under PowerShell 7
Data source or fixture: any first-party C# file with an async member carrying [ExcludeFromCodeCoverage] that declares a lambda in its body
Steps to Reproduce
Take a member that carries [ExcludeFromCodeCoverage], is declared async, and declares one or more lambdas in its body. QuickFiler.Controllers.QfcItemController.ToggleExpansionAsync (QuickFiler/Controllers/QfcItemController.Navigation.cs:191-192) is a live example of the attributed async shape.
Run the coverage pipeline.
Inspect the post-processed Cobertura report for that file.
Observe that the closure class carrying the member's lambdas survives the filter, and its lines remain in lines-valid.
Expected Behavior
A lambda declared inside a member carrying [ExcludeFromCodeCoverage] is excluded from the coverage
denominator regardless of whether that member is async.
Actual Behavior
The lambda's lines are retained when the declaring member is async, because the member's state
machine class admits it to the presence set. The lines are correctly removed when the declaring
member is not async.
ToggleExpansionAsync carries [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage], and the
attribute was verified present in commit 6b821480 (2026-07-03), 35 days before the raw corpus was
captured (2026-08-07T02:19:25Z). Probe answer: YES - the collector does emit a d__ class for an
attributed async member.
Impact / Severity
Blocker
High
Medium
Low
The failure mode is under-exclusion: a file measures no better than it truly is. No coverage is
wrongly deleted, and no gate can be passed that should have failed. The cost is a residual, invisible
ceiling on any file that uses the "thin exempt production forwarder" seam pattern on an async
member. 62 first-party members carry [ExcludeFromCodeCoverage] and are declared async (enumerated
in the probe artifact), though only those that also declare lambdas are affected.
Summary
Lambda bodies declared inside a member that carries
[ExcludeFromCodeCoverage]and isasync(oran iterator) remain in the coverage denominator after the issue #457 closure filter. This is residual
(a) of #457: a deliberate, documented scope choice rather than an oversight, recorded so it can be
addressed on its own terms rather than by widening #457.
Remove-CoberturaExemptClosureCoverage(added by #457 inscripts/vscode/Invoke-MSTestWithCoverage.ClosureFilter.ps1) infers that a member is exempt from itsabsence from the report's instrumented method set. An
asyncmember emits no plain<method>element - its whole body moves to a state machine class
Type.<Member>d__<N>- so the filter mustadmit
d__class names as proof that the declaring member exists. That admission is mandatory in theother direction: without it, covered lambdas inside non-exempt async members would be deleted,
failing #457's required direction 2. The consequence is that an attributed async member also enters
the presence set, and its lambdas are retained.
Environment
net481); post-processing runs under PowerShell 7pwsh -NoProfile -File scripts/vscode/Invoke-MSTestWithCoverage.ps1 -SearchRoot . -Configuration Debug -CoverageOutput "coverage\coverage.cobertura.xml"asyncmember carrying[ExcludeFromCodeCoverage]that declares a lambda in its bodySteps to Reproduce
[ExcludeFromCodeCoverage], is declaredasync, and declares one or more lambdas in its body.QuickFiler.Controllers.QfcItemController.ToggleExpansionAsync(QuickFiler/Controllers/QfcItemController.Navigation.cs:191-192) is a live example of the attributed async shape.lines-valid.Expected Behavior
A lambda declared inside a member carrying
[ExcludeFromCodeCoverage]is excluded from the coveragedenominator regardless of whether that member is
async.Actual Behavior
The lambda's lines are retained when the declaring member is
async, because the member's statemachine class admits it to the presence set. The lines are correctly removed when the declaring
member is not
async.Logs / Screenshots
Attached minimal logs or screenshot
Measured evidence from the Bug: excludefromcodecoverage-does-not-suppress-nested-lambdas #457
[P0-T12]probe, recorded indocs/features/active/2026-08-10-excludefromcodecoverage-nested-lambdas-457/evidence/baseline/async-d-state-machine-probe.2026-08-11T00-38.md:ToggleExpansionAsynccarries[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage], and theattribute was verified present in commit
6b821480(2026-07-03), 35 days before the raw corpus wascaptured (2026-08-07T02:19:25Z). Probe answer: YES - the collector does emit a
d__class for anattributed async member.
Impact / Severity
The failure mode is under-exclusion: a file measures no better than it truly is. No coverage is
wrongly deleted, and no gate can be passed that should have failed. The cost is a residual, invisible
ceiling on any file that uses the "thin exempt production forwarder" seam pattern on an
asyncmember. 62 first-party members carry
[ExcludeFromCodeCoverage]and are declaredasync(enumeratedin the probe artifact), though only those that also declare lambdas are affected.
Source
From: docs/features/potential/2026-08-11-exempt-async-member-lambdas-remain-counted.md