[fix] Replace TestSDKAutoGeneratedCode with ExcludeFromCodeCoverage in auto-generated Program files - #16101
Conversation
…ry (#3876) Replace [TestSDKAutoGeneratedCode] with [ExcludeFromCodeCoverage] in the auto-generated Microsoft.NET.Test.Sdk.Program files (C#, VB, F#). Root cause: when a test project and a class library project are in the same directory, both projects share the same obj/ folder. The project.assets.json file gets overwritten by the other project's restore, temporarily losing the reference to Microsoft.TestPlatform.ObjectModel.dll. This causes the compiler to fail to resolve [Microsoft.VisualStudio.TestPlatform.TestSDKAutoGeneratedCode] with CS0234: 'VisualStudio' does not exist in namespace 'Microsoft'. Fix: replace with [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage], which is a BCL attribute that requires no extra assembly references. The AutoGeneratedProgram class is still excluded from code coverage because ExcludeFromCodeCoverageAttribute is already in the default code coverage exclusion config (EnableCodeCoverageArgumentProcessor.cs line 137). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR updates the auto-generated entry-point templates shipped in Microsoft.NET.Test.Sdk (netcoreapp build assets) to avoid referencing Microsoft.VisualStudio.TestPlatform.TestSDKAutoGeneratedCode, eliminating a nondeterministic CS0234 failure when multiple projects share the same obj/ folder and project.assets.json gets overwritten.
Changes:
- Replaced
[Microsoft.VisualStudio.TestPlatform.TestSDKAutoGeneratedCode]with[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]in the C# auto-generated Program template. - Replaced the same attribute in the VB auto-generated Program template.
- Replaced the same attribute in the F# auto-generated Program template.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/package/Microsoft.NET.Test.Sdk/netcoreapp/Microsoft.NET.Test.Sdk.Program.cs | Switches the generated C# entry point to use a BCL-only coverage exclusion attribute. |
| src/package/Microsoft.NET.Test.Sdk/netcoreapp/Microsoft.NET.Test.Sdk.Program.vb | Switches the generated VB entry point to use a BCL-only coverage exclusion attribute. |
| src/package/Microsoft.NET.Test.Sdk/netcoreapp/Microsoft.NET.Test.Sdk.Program.fs | Switches the generated F# entry point to use a BCL-only coverage exclusion attribute. |
| using System; | ||
| [Microsoft.VisualStudio.TestPlatform.TestSDKAutoGeneratedCode] | ||
| [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] |
There was a problem hiding this comment.
Right, dropped it. Also removed Imports System from the VB file for the same reason.
Jakub Jareš (nohwnd)
left a comment
There was a problem hiding this comment.
Code Review — PR #16101
Dimensions activated (files in src/package/): Dependency & Package Integrity · Cross-TFM & Framework Resolution · Backward Compatibility & Rollback Safety · Build Script & Infrastructure Hygiene
What was checked
Dependency & Package Integrity
The three auto-generated Program files (Microsoft.NET.Test.Sdk.Program.{cs,vb,fs}) are included directly into user projects via the GenerateProgramFile MSBuild target as <Compile Include> items. Replacing TestSDKAutoGeneratedCode (from Microsoft.TestPlatform.ObjectModel) with ExcludeFromCodeCoverage (BCL) eliminates the external assembly reference from these files. No other files in the package were affected.
Cross-TFM & Framework Resolution
System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute is available in mscorlib (net4.0+) and System.Runtime (netstandard1.0+, all .NET Core/5+). This covers every TFM vstest targets. The attribute's valid targets (Class | Struct | Constructor | Method | Property | Event) are appropriate: applied to a class in the C# file, and to a method in the VB and F# files.
Backward Compatibility & Rollback Safety
The PR description's core claim is verified: EnableCodeCoverageArgumentProcessor.cs lines 137–138 confirm that ExcludeFromCodeCoverageAttribute is already in the default code coverage exclusion config alongside TestSDKAutoGeneratedCode. Users' coverage results will not change. Third-party coverage tools (Coverlet etc.) natively honor ExcludeFromCodeCoverage, so this change actually improves coverage exclusion for non-VSTest coverage flows.
Build Script & Infrastructure Hygiene
No .csproj, .nuspec, or .targets file changes were required — the template files are included verbatim, so no packaging metadata updates are needed. No changes to eng/expected-nupkg-file-counts.json or eng/expected-dll-frameworks.json are required.
Findings
No issues found. The fix is mechanically correct, minimal in scope, and preserves all existing behavior.
🧠 Reviewed by Expert Reviewer
🧠 Reviewed by Expert Code Reviewer 🧠
…tays The attribute is now unused by the auto-generated Program files but must remain for back-compat: test assemblies built against older Microsoft.NET.Test.Sdk versions still carry it, and the default code coverage exclusion config keeps matching on it. - [EditorBrowsable(Never)] on the attribute so new code doesn't pick it up by IntelliSense. No [Obsolete] - a deprecation warning emitted from the old generated Main file could bleed into builds that resolve a newer ObjectModel against an older Microsoft.NET.Test.Sdk. - Expand the XML doc to point at ExcludeFromCodeCoverageAttribute and explain the back-compat role. - Comment next to the runsettings regex so a future "this looks dead" cleanup doesn't drop it. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Jakub Jareš <me@jakubjares.com>
| @" <Attribute>^System\.Diagnostics\.DebuggerHiddenAttribute$</Attribute>" + Environment.NewLine + | ||
| @" <Attribute>^System\.Diagnostics\.DebuggerNonUserCodeAttribute$</Attribute>" + Environment.NewLine + | ||
| @" <Attribute>^System\.Diagnostics\.CodeAnalysis\.ExcludeFromCodeCoverageAttribute$</Attribute>" + Environment.NewLine + | ||
| // Kept for back-compat: test assemblies built against Microsoft.NET.Test.Sdk <= 17.x carry |
There was a problem hiding this comment.
version does not seem accurate?
There was a problem hiding this comment.
Yeah, main is on 18.9 so <= 17.x was wrong. Dropped the version pin — older versions reads accurately whether this ships in 18.9 or gets backported.
- Remove `using System;` from Program.cs and `Imports System` from Program.vb. Both are unused now that the attribute is fully qualified; leaving them in trips IDE0005 in consumer builds that treat analyzer warnings as errors. (Copilot reviewer + nohwnd suggestion.) - Drop the "<= 17.x" version pin in the EnableCodeCoverageArgumentProcessor comment - main is on 18.9 and the change will ship there, so the pin was wrong. "Older versions" stays correct under any backport. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Jakub Jareš (nohwnd)
left a comment
There was a problem hiding this comment.
Code Review — PR #16101 (follow-up commits)
Dimensions activated (files in src/Microsoft.TestPlatform.ObjectModel/ and src/vstest.console/): Public API Surface Protection · Backward Compatibility & Rollback Safety · Cross-TFM & Framework Resolution
What was checked (new commits since 2026-06-08 review)
Public API Surface Protection — TestSDKAutoGeneratedCode.cs
[EditorBrowsable(EditorBrowsableState.Never)] is the correct soft-deprecation tool here. It hides the type from IntelliSense in new code without touching the binary surface — the class stays public and fully callable, so NuGet-unified consumers of older Microsoft.NET.Test.Sdk that still emit [TestSDKAutoGeneratedCode] on their generated Main are unaffected at compile time. Choosing [EditorBrowsable(Never)] over [Obsolete] is deliberate and correct: [Obsolete] would emit CS0618 in any consumer that unifies a newer ObjectModel against an older SDK version and has TreatWarningsAsErrors set.
Backward Compatibility & Rollback Safety
Both using System.ComponentModel; and using System; are still present in the file (needed for EditorBrowsable/EditorBrowsableState and Attribute/AttributeUsage/AttributeTargets respectively). No dead imports introduced.
Cross-TFM & Framework Resolution
System.ComponentModel.EditorBrowsableAttribute is available in mscorlib (net4.0+) and System.Runtime (netstandard1.0+, all .NET Core/5+). No TFM gap.
Back-compat comment in EnableCodeCoverageArgumentProcessor.cs
Comment-only change; no behavioral impact. The text is accurate: the version pin ("≤ 17.x") was correctly dropped in the latest commit since the version boundary isn't known at ship time.
Findings
No issues found. The follow-up commits are clean.
🧠 Reviewed by Expert Code Reviewer 🧠
🧠 Reviewed by Expert Code Reviewer 🧠
|
The Build Windows Release failure is an infrastructure regression on Evidence:
The regression was introduced by one of the commits merged to
Once the Warning Firewall blocked 1 domainThe following domain was blocked by the firewall during workflow execution:
network:
allowed:
- defaults
- "dev.azure.com"See Network Configuration for more information.
|
The previous Windows Release CI failure (build #1459853) was an infrastructure regression on main, not caused by this PR's changes. PR #16108 (different code) failed the same check with the same pattern during that window. Since then, main has been stabilized and PR #16108 passes cleanly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Commit pushed:
|
|
fhnaseer quick sanity check please, we have our own attribute which avoids auto-generated main being in CC, and replacing it with the standard attribute. We keep the old attribute around so people can still build in case they mixobject model versions etc. Seems safe to me, wdyt? |
Fixes #3876.
Two test projects in the same directory share
obj/. NuGet writesproject.assets.jsonper-project, so if the class library is restored after the test project, it overwrites the test project''s assets file. MSBuild then compiles the test project against the wrong assets, the auto-generatedMaincan''t resolveMicrosoft.VisualStudio.TestPlatform.TestSDKAutoGeneratedCode, and the build fails withCS0234.The fix is to stop referencing that attribute from the auto-generated
Program.{cs,vb,fs}files.[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]lives inSystem.Runtime/mscorlib, is always resolvable regardless of what''s inproject.assets.json, and is already in vstest''s default coverage exclusion config alongsideTestSDKAutoGeneratedCode— so coverage behavior is unchanged.Follow-ups in the same PR:
[EditorBrowsable(Never)]onTestSDKAutoGeneratedCodeso new code doesn''t discover it via IntelliSense. Deliberately not[Obsolete]— that would emitCS0618from the old auto-generatedMainin any consumer where NuGet unifies a newerMicrosoft.TestPlatform.ObjectModelagainst an olderMicrosoft.NET.Test.Sdk(and break builds withTreatWarningsAsErrors).TestSDKAutoGeneratedCoderegex inEnableCodeCoverageArgumentProcessor.csexplaining why it stays. It has to remain until test assemblies built with the old SDK are out of support.using System;/Imports Systemfrom the generatedProgramfiles (Copilot reviewer caught it; would have tripped IDE0005 in consumer builds).Testing
Microsoft.TestPlatform.Utilities.UnitTests— pass on net11.0 + net481.vstest.console.UnitTests— pass on net11.0 + net481 (5 pre-existing[Ignore]).