Skip to content

Suppress CS3016 on internal COM wrappers without [CLSCompliant(false)] (#1703)#1723

Merged
jevansaks merged 1 commit into
microsoft:mainfrom
JeremyKuhne:feature/clscompliant-nested-types
Jun 12, 2026
Merged

Suppress CS3016 on internal COM wrappers without [CLSCompliant(false)] (#1703)#1723
jevansaks merged 1 commit into
microsoft:mainfrom
JeremyKuhne:feature/clscompliant-nested-types

Conversation

@JeremyKuhne

Copy link
Copy Markdown
Member

Summary

Fixes the dotnet/winforms#14639 fallout from #1703: a consumer that adds a friendly-helper partial to a generated CCW-bearing COM struct (e.g. System.Private.Windows.Core's ITypeInfo.cs) now builds clean.

#1703 marked CCW-bearing COM struct wrappers [CLSCompliant(false)] to silence CS3016 (a Roslyn limitation that flags the array-valued [UnmanagedCallersOnly(CallConvs = new[]{...})] thunk argument even on non-visible types — dotnet/roslyn#68526). But that type-level attribute induces CS3019/CS3021, which Roslyn reports against the type symbol. When a consumer adds their own partial to the struct, the diagnostic lands on the consumer''s file and escapes the generated file''s pragma:

  • assembly has no [assembly: CLSCompliant] -> CS3021
  • assembly is [assembly: CLSCompliant(true)] -> CS3019

Under TreatWarningsAsErrors this breaks the consumer build.

Fix

Stop emitting [CLSCompliant(false)]. Instead suppress CS3016 directly in the generated file''s header pragma, where the offending thunk lives:

  • CS3016 is reported at the thunk method (always in our generated file), so the file-scoped pragma silences it precisely without masking user code.
  • Because no attribute is emitted, CS3019/CS3021 can never arise — robust to consumer partials and any assembly CLS posture.
  • The suppression is applied to internal projections only (the default). Public projections do not get it: a public COM interop surface is the consumer''s CLS contract to own (and is inherently non-CLS-compliant anyway), so CsWin32 must not unilaterally silence it.

Removes the now-unused CLSCompliantFalse() factory and the CS3019/CS3021 entries from the generated-file pragma.

Tests

  • Internal cross-TFM (net472/net10.0) x assembly-CLS-posture matrix asserting no CS3016/CS3019/CS3021.
  • Public projection omits the CS3016 suppression (and emits no [CLSCompliant]).
  • Friendly-helper-partial regressions with and without an assembly [CLSCompliant] attribute (the winforms#14639 shape).
  • A load-bearing test that strips the generated pragma and asserts CS3016 does fire (proving the suppression is real, not vacuous).

Validated end-to-end with a real net472;net10.0 cross-compile consuming the locally-built generator.

microsoft#1703)

The microsoft#1703 fix marked CCW-bearing COM struct wrappers [CLSCompliant(false)] to silence CS3016 (a Roslyn limitation that flags array-valued [UnmanagedCallersOnly] attribute arguments even on non-visible types, dotnet/roslyn#68526). That type-level attribute induces CS3019/CS3021, which Roslyn reports against the type *symbol* — so when a consumer adds their own friendly-helper partial to a generated COM struct (e.g. dotnet/winforms System.Private.Windows.Core's ITypeInfo.cs), the diagnostic lands on the consumer's file and escapes the generated file's pragma. Result: CS3021 (no assembly CLSCompliant attribute) or CS3019 (assembly is CLS-compliant) breaks the consumer build under TreatWarningsAsErrors.

Fix: stop emitting [CLSCompliant(false)] entirely. Instead suppress CS3016 directly in the generated file's header pragma, where the offending thunk lives. CS3016 is reported at the thunk method (always in our generated file), so the file-scoped pragma silences it precisely without masking user code, and because no attribute is emitted, CS3019/CS3021 can never arise regardless of consumer partials or assembly CLS posture.

The suppression is applied only to internal projections (the default). Public projections do not get it: a public COM interop surface is the consumer's CLS contract to own (and is inherently non-CLS-compliant anyway), so CsWin32 must not unilaterally silence it.

Removes the now-unused CLSCompliantFalse() factory and the CS3019/CS3021 entries from the generated-file pragma. Adds regression coverage: the internal cross-TFM x assembly-CLS matrix, public-projection omits-suppression, friendly-helper-partial with and without an assembly CLSCompliant attribute, and a load-bearing test proving CS3016 still fires when the pragma is stripped.
@jevansaks jevansaks enabled auto-merge (squash) June 12, 2026 03:27
@jevansaks jevansaks merged commit 9aeca4d into microsoft:main Jun 12, 2026
14 checks passed
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.

2 participants