Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 0 additions & 14 deletions src/Microsoft.Windows.CsWin32/Generator.Com.cs
Original file line number Diff line number Diff line change
Expand Up @@ -769,20 +769,6 @@ static ExpressionSyntax ThisPointer(PointerTypeSyntax? typedPointer = null)
iface = iface.AddAttributeLists(AttributeList(GUID(DecodeGuidFromAttribute(guidAttribute.Value))));
}

// CS3016 ("Arrays as attribute arguments is not CLS-compliant") fires under
// [assembly: CLSCompliant(true)] on the CCW thunks we emit with
// [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]. CCW thunks are
// only emitted when canUseUnmanagedCallersOnlyAttribute is true (.NET 5+); on
// net472 / netstandard2.0 the generator produces no array-valued attribute arguments and
// [CLSCompliant(false)] is unnecessary. The attribute is also a no-op on public types
// (which the consumer's CLS surface owns), so we only emit it on internal struct
// wrappers that we know carry the array-valued attribute.
// See https://github.com/microsoft/CsWin32/issues/1703.
if (ccwThisParameter is not null && this.Visibility == SyntaxKind.InternalKeyword)
{
iface = iface.AddAttributeLists(AttributeList(CLSCompliantFalse()));
}

if (this.GetSupportedOSPlatformAttribute(typeDef.GetCustomAttributes()) is AttributeSyntax supportedOSPlatformAttribute)
{
iface = iface.AddAttributeLists(AttributeList(supportedOSPlatformAttribute));
Expand Down
28 changes: 26 additions & 2 deletions src/Microsoft.Windows.CsWin32/Generator.Invariants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -296,16 +296,40 @@ public partial class Generator
"CS0436", // conflicts with the imported type (InternalsVisibleTo between two projects that both use CsWin32)
"CS8981", // The type name only contains lower-cased ascii characters
"SYSLIB1092", // The return value in the managed definition will be converted to an 'out' parameter when calling the unmanaged COM method
"CS3021", // Type does not need a CLSCompliant attribute because the assembly does not have a CLSCompliant attribute (fires on generated COM struct wrappers that we mark [CLSCompliant(false)] to silence CS3016 — see https://github.com/microsoft/CsWin32/issues/1703)
"CS3019", // CLS compliance checking will not be performed on '...' because it is not visible from outside this assembly (same root cause: our internal COM struct wrappers are marked [CLSCompliant(false)] to silence CS3016)
};

/// <summary>
/// The leading trivia for every generated file: the auto-generated banner plus a <c>#pragma warning disable</c> for the
/// warnings CsWin32-generated code is expected to trip. Used for <em>public</em> projections.
/// </summary>
private static readonly SyntaxTriviaList FileHeader = ParseLeadingTrivia(AutoGeneratedHeader).Add(
Trivia(PragmaWarningDirectiveTrivia(
disableOrRestoreKeyword: TokenWithSpace(SyntaxKind.DisableKeyword),
errorCodes: [.. WarningsToSuppressInGeneratedCode.Select(IdentifierName)],
isActive: true)));

/// <summary>
/// Same as <see cref="FileHeader"/>, but additionally suppresses CS3016 ("Arrays as attribute arguments is not
/// CLS-compliant"). Used for <em>internal</em> projections (the default).
/// </summary>
/// <remarks>
/// CS3016 fires under <c>[assembly: CLSCompliant(true)]</c> on the CCW thunks we emit with
/// <c>[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]</c> — a Roslyn limitation that reports the
/// diagnostic even for non-visible members (dotnet/roslyn#68526). The thunks only exist on .NET 5+ (net472 /
/// netstandard2.0 never trip it). Suppressing it via this header pragma — at the thunk site, always in our generated file —
/// masks no user-authored CS3016. We deliberately do NOT instead stamp <c>[CLSCompliant(false)]</c> on the struct: that
/// type-level attribute induces CS3019/CS3021, which Roslyn reports against the type symbol and therefore leak onto any
/// consumer-authored partial of the struct (e.g. a friendly-helper partial) where the generated pragma cannot reach.
/// We use this header only for internal projections; for public ones the consumer owns the CLS-compliance contract of
/// their public surface (which inherently exposes other non-CLS COM types), so CsWin32 must not unilaterally silence it.
/// See https://github.com/microsoft/CsWin32/issues/1703.
/// </remarks>
private static readonly SyntaxTriviaList FileHeaderWithClsArrayAttributeSuppression = ParseLeadingTrivia(AutoGeneratedHeader).Add(
Trivia(PragmaWarningDirectiveTrivia(
disableOrRestoreKeyword: TokenWithSpace(SyntaxKind.DisableKeyword),
errorCodes: [.. WarningsToSuppressInGeneratedCode.Append("CS3016").Select(IdentifierName)],
isActive: true)));

private static readonly AttributeSyntax InAttributeSyntax = Attribute(IdentifierName("In")).WithArgumentList(null);
private static readonly AttributeSyntax OutAttributeSyntax = Attribute(IdentifierName("Out")).WithArgumentList(null);
private static readonly AttributeSyntax OptionalAttributeSyntax = Attribute(IdentifierName("Optional")).WithArgumentList(null);
Expand Down
9 changes: 7 additions & 2 deletions src/Microsoft.Windows.CsWin32/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public partial class Generator : IGenerator, IDisposable
private readonly GeneratorOptions options;
private readonly CSharpCompilation? compilation;
private readonly CSharpParseOptions? parseOptions;
private readonly SyntaxTriviaList fileHeader;
private readonly bool comIIDInterfacePredefined;
private readonly bool getDelegateForFunctionPointerGenericExists;
private readonly GeneratedCode committedCode = new();
Expand Down Expand Up @@ -107,6 +108,10 @@ public Generator(string metadataLibraryPath, Docs? docs, IEnumerable<string> add
this.parseOptions = parseOptions;
this.volatileCode = new(this.committedCode);

// Suppress CS3016 (the CLS array-attribute-argument warning our CCW thunks trip) in the generated file header,
// but only for internal projections. For public projections the consumer owns their public surface's CLS contract.
this.fileHeader = this.options.Public ? FileHeader : FileHeaderWithClsArrayAttributeSuppression;

// UnscopedRefAttribute may be emitted to work on downlevel *runtimes*, but we can't use it
// on downlevel *compilers*. Only .NET 8+ SDK compilers support it. Since we cannot detect
// compiler version, we use language version instead.
Expand Down Expand Up @@ -854,7 +859,7 @@ .. this.committedCode.GeneratedTopLevelTypes
CompilationUnitSyntax? compilationUnit = ((CompilationUnitSyntax)kv.Value
.AddUsings(usingDirectives.ToArray())
.Accept(new WhitespaceRewriter())!)
.WithLeadingTrivia(FileHeader);
.WithLeadingTrivia(this.fileHeader);

lock (normalizedResults)
{
Expand All @@ -871,7 +876,7 @@ .. this.committedCode.GeneratedTopLevelTypes
}
else
{
normalizedResults.Add(string.Format(CultureInfo.InvariantCulture, FilenamePattern, "CsWin32Stamp"), CompilationUnit().AddAttributeLists(CsWin32StampAttribute).WithLeadingTrivia(FileHeader));
normalizedResults.Add(string.Format(CultureInfo.InvariantCulture, FilenamePattern, "CsWin32Stamp"), CompilationUnit().AddAttributeLists(CsWin32StampAttribute).WithLeadingTrivia(this.fileHeader));
}
}

Expand Down
6 changes: 0 additions & 6 deletions src/Microsoft.Windows.CsWin32/SimpleSyntaxFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -197,12 +197,6 @@ internal static AttributeSyntax GUID(Guid guid)
AttributeArgument(LiteralExpression(SyntaxKind.StringLiteralExpression, Literal(guid.ToString().ToUpperInvariant()))));
}

internal static AttributeSyntax CLSCompliantFalse()
{
return Attribute(IdentifierName("CLSCompliant")).AddArgumentListArguments(
AttributeArgument(LiteralExpression(SyntaxKind.FalseLiteralExpression)));
}

internal static AttributeSyntax InterfaceType(ComInterfaceType interfaceType)
{
return Attribute(IdentifierName("InterfaceType")).AddArgumentListArguments(
Expand Down
Loading
Loading