Repository navigation
Bug: excludefromcodecoverage-does-not-suppress-nested-lambdas #457
Description
Activity
Remedy proven in-repo (added during #455 preparation research)
A type-level
[ExcludeFromCodeCoverage]DOES suppress instrumentation of nested lambdas; only the method-level attribute fails to propagate.Evidence:
QuickFiler/Viewers/WebView2Messenger.cscarries a type-level attribute at line 20 and declares lambdas at:40-48and:62-68. It produces zerofilename=entries in the committed Cobertura report — the closures are suppressed along with the type.Contrast
QuickFiler/Viewers/BreadcrumbPopupUiOperations.cs, which uses method-level attributes at:394and:457and leaks its nested lambda bodies (source lines 406, 409, 471-490) into the denominator as permanently-uncovered lines.Consequence for the suggested remediation
This makes option 3 ("restructure to avoid lambdas") unnecessary in most cases. The cheaper and more auditable fix is:
Extract the exempt thin production forwarders into their own type in their own file, and put a single type-level
[ExcludeFromCodeCoverage]on that type.Important caveat: the extracted forwarders must be a separate type, not a
partialof the original. An attribute on one part of a partial class applies to the whole type, which would silently exempt every covered line in the primary file.Correction to the impact statement above
The originally-stated ~91.5% hard ceiling for
BreadcrumbPopupUiOperations.csapplies only while the lambdas remain in the measured file. Under the extract-to-exempt-type remedy the leaked lines leave the denominator entirely; that file is projected to reach ~99.6% line / ~99.2% branch.The repo-wide defect stands: a method-level attribute still silently under-reports. Teams applying the thin-forwarder seam pattern should prefer a type-level boundary.
- added 16 commits that reference this issue
on Aug 8, 2026
Summary
A method-level
[ExcludeFromCodeCoverage]attribute does not suppress instrumentation of lambdasdeclared inside the attributed member. The C# compiler hoists those lambdas into a separate
compiler-generated closure type whose members do not inherit the attribute, so the lambda bodies
remain in the coverage denominator. When the attributed member is exempt precisely because it
cannot execute in a unit-test host, its nested lambda bodies are therefore permanently
uncovered and permanently counted against the file.
This is a silent measurement defect: it does not crash, it quietly and irreducibly depresses the
line-coverage figure of any file that uses the "thin exempt production forwarder" seam pattern.
Environment
scripts/vscode/Invoke-MSTestWithCoverage.ps1producing Cobertura outputSteps to Reproduce
[ExcludeFromCodeCoverage]that declares one or more lambdas in its body.<line>entries.hits="0", while theattributed member's own lines are correctly absent.
Expected Behavior
(not provided in potential file)
Actual Behavior
(not provided in potential file)
Logs / Screenshots
(not provided in potential file)
Impact / Severity
The seam pattern this repository prefers - move decision logic into a testable host-neutral member
and leave a thin, exempt production forwarder - is precisely the pattern that triggers the defect,
because those forwarders commonly wire SDK event handlers using lambdas. The consequence is a hard,
invisible ceiling on the achievable line coverage of every file that adopts the pattern.
Concretely,
BreadcrumbPopupUiOperations.cscannot exceed roughly 91.5% line coverage((258 - 22) / 258) no matter how many tests are written. It currently measures 90.7%. Any gate,
audit, or acceptance criterion that assumes the remaining 9.3% is closable by testing is working
from a false premise.
This matters at epic scale: epic #136 requires every testable file to reach >= 80% line coverage,
and several children plan to adopt exactly this seam pattern to remove
[ExcludeFromCodeCoverage]attributes. Each will inherit an unannounced ceiling.
Source
From: docs/features/potential/2026-08-07-excludefromcodecoverage-does-not-suppress-nested-lambdas.md