Skip to content

Bug: overload-name-collision-under-exclusion #560

Description

@drmoisan
  • Work Mode: full-bug

Summary

When one overload of a member carries [ExcludeFromCodeCoverage] and another overload of the same
name does not, lambdas declared inside the attributed overload remain in the coverage denominator.
This is residual (c) of #457: a deliberate scope choice, recorded so it can be addressed on its own
terms rather than by widening #457.

The #457 presence set is keyed by member name, not by signature. The non-attributed overload
contributes its name to the presence set, the attributed overload's closure resolves to that same
name, and the closure is therefore retained.

Environment

  • OS/version: Windows 11 Pro 10.0.26200
  • .NET/framework: .NET Framework 4.8.1 (net481); post-processing runs under PowerShell 7
  • Command/flags used: pwsh -NoProfile -File scripts/vscode/Invoke-MSTestWithCoverage.ps1 -SearchRoot . -Configuration Debug -CoverageOutput "coverage\coverage.cobertura.xml"
  • Data source or fixture: any first-party C# type with two same-named overloads where exactly one carries [ExcludeFromCodeCoverage] and declares a lambda

Steps to Reproduce

  1. Take a type with two overloads of the same member name. Attribute one with [ExcludeFromCodeCoverage] and have it declare a lambda; leave the other unattributed.
  2. Run the coverage pipeline.
  3. Inspect the post-processed Cobertura report for that file.
  4. Observe that the attributed overload's closure class survives, because the unattributed overload keeps the shared name in the presence set.

UtilitiesCS.SortEmail is a live instance of the shape: SortAsync appears as four attributed
overloads at UtilitiesCS/EmailIntelligence/EmailParsingSorting/SortEmail.cs:43, 77, 113, 304, and
SaveAttachmentAsync as two at :763, 826.

Expected Behavior

Exemption is resolved per overload, so a lambda inside an attributed overload is excluded even when a
same-named unattributed overload exists.

Actual Behavior

The lambda's lines are retained, because the presence-set key cannot distinguish overloads.

Logs / Screenshots

  • Attached minimal logs or screenshot
  • The keying is visible in Get-CoberturaInstrumentedMemberName
    (scripts/vscode/Invoke-MSTestWithCoverage.ClosureFilter.ps1), whose presence set is keyed by
    "$declaringType|$filename" with member names as the set members. The Cobertura
    <method> element does carry a signature attribute, but the synthesized closure method name
    (<Member>b__N_M) carries no signature information for its declaring member, so the two cannot be
    joined on signature from the report alone.

Impact / Severity

  • Blocker
  • High
  • Medium
  • Low

The failure mode is under-exclusion in every case: a file measures no better than it truly is. No
coverage is wrongly deleted, and no gate can be passed that should have failed. Two related keying
dimensions behave correctly and are not affected: two types in the same file sharing a member name are
separated by the declaring-type component of the key, and a partial type spanning files is separated
by the filename component (which also errs toward under-exclusion).

Source

From: docs/features/potential/2026-08-11-overload-name-collision-under-exclusion.md

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions