Skip to content

Commit

Permalink
Fix C# 11 warning on inline arrays
Browse files Browse the repository at this point in the history
Fixes #637
  • Loading branch information
AArnott committed Aug 19, 2022
1 parent 22521d1 commit 3c5020f
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 12 deletions.
87 changes: 75 additions & 12 deletions src/Microsoft.Windows.CsWin32/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ public class Generator : IDisposable
private static readonly AttributeSyntax PreserveSigAttribute = Attribute(IdentifierName("PreserveSig"));
private static readonly AttributeSyntax ObsoleteAttribute = Attribute(IdentifierName("Obsolete")).WithArgumentList(null);
private static readonly AttributeSyntax SupportedOSPlatformAttribute = Attribute(IdentifierName("SupportedOSPlatform"));
private static readonly AttributeSyntax UnscopedRefAttribute = Attribute(ParseName("UnscopedRef")).WithArgumentList(null);
private static readonly AttributeListSyntax DefaultDllImportSearchPathsAttributeList = AttributeList()
.WithCloseBracketToken(TokenWithLineFeed(SyntaxKind.CloseBracketToken))
.AddAttributes(Attribute(IdentifierName("DefaultDllImportSearchPaths")).AddArgumentListArguments(AttributeArgument(MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, IdentifierName(nameof(DllImportSearchPath)), IdentifierName(nameof(DllImportSearchPath.System32))))));
Expand Down Expand Up @@ -336,6 +337,7 @@ public class Generator : IDisposable
private readonly bool canCallCreateSpan;
private readonly bool canUseUnsafeAsRef;
private readonly bool canUseUnsafeNullRef;
private readonly bool unscopedRefAttributePredefined;
private readonly bool getDelegateForFunctionPointerGenericExists;
private readonly bool generateSupportedOSPlatformAttributes;
private readonly bool generateSupportedOSPlatformAttributesOnInterfaces; // only supported on net6.0 (https://github.com/dotnet/runtime/pull/48838)
Expand Down Expand Up @@ -383,6 +385,7 @@ public Generator(string metadataLibraryPath, Docs? docs, GeneratorOptions option
this.canCallCreateSpan = this.compilation?.GetTypeByMetadataName(typeof(MemoryMarshal).FullName)?.GetMembers("CreateSpan").Any() is true;
this.canUseUnsafeAsRef = this.compilation?.GetTypeByMetadataName(typeof(Unsafe).FullName)?.GetMembers("AsRef").Any() is true;
this.canUseUnsafeNullRef = this.compilation?.GetTypeByMetadataName(typeof(Unsafe).FullName)?.GetMembers("NullRef").Any() is true;
this.unscopedRefAttributePredefined = this.compilation?.GetTypeByMetadataName("System.Diagnostics.CodeAnalysis.UnscopedRefAttribute") is not null;
this.getDelegateForFunctionPointerGenericExists = this.compilation?.GetTypeByMetadataName(typeof(Marshal).FullName)?.GetMembers(nameof(Marshal.GetDelegateForFunctionPointer)).Any(m => m is IMethodSymbol { IsGenericMethod: true }) is true;
this.generateDefaultDllImportSearchPathsAttribute = this.compilation?.GetTypeByMetadataName(typeof(DefaultDllImportSearchPathsAttribute).FullName) is object;
if (this.compilation?.GetTypeByMetadataName("System.Runtime.Versioning.SupportedOSPlatformAttribute") is { } attribute
Expand Down Expand Up @@ -1085,7 +1088,7 @@ public IEnumerable<string> GetSuggestions(string name)

// .g.cs because the resulting files are not user-created.
const string FilenamePattern = "{0}.g.cs";
var results = new Dictionary<string, NamespaceDeclarationSyntax>(StringComparer.OrdinalIgnoreCase);
Dictionary<string, CompilationUnitSyntax> results = new(StringComparer.OrdinalIgnoreCase);

IEnumerable<MemberDeclarationSyntax> GroupMembersByNamespace(IEnumerable<MemberDeclarationSyntax> members)
{
Expand All @@ -1099,12 +1102,23 @@ IEnumerable<MemberDeclarationSyntax> GroupMembersByNamespace(IEnumerable<MemberD

if (this.options.EmitSingleFile)
{
CompilationUnitSyntax file = CompilationUnit()
.AddMembers(starterNamespace.AddMembers(GroupMembersByNamespace(this.NamespaceMembers).ToArray()))
.AddMembers(this.committedCode.GeneratedTopLevelTypes.ToArray());
results.Add(
string.Format(CultureInfo.InvariantCulture, FilenamePattern, "NativeMethods"),
starterNamespace.AddMembers(GroupMembersByNamespace(this.NamespaceMembers).ToArray()));
file);
}
else
{
foreach (MemberDeclarationSyntax topLevelType in this.committedCode.GeneratedTopLevelTypes)
{
string typeName = topLevelType.DescendantNodesAndSelf().OfType<BaseTypeDeclarationSyntax>().First().Identifier.ValueText;
results.Add(
string.Format(CultureInfo.InvariantCulture, FilenamePattern, typeName),
CompilationUnit().AddMembers(topLevelType));
}

IEnumerable<IGrouping<string?, MemberDeclarationSyntax>>? membersByFile = this.NamespaceMembers.GroupBy(
member => member.HasAnnotations(SimpleFileNameAnnotation)
? member.GetAnnotations(SimpleFileNameAnnotation).Single().Data
Expand All @@ -1123,9 +1137,11 @@ IEnumerable<MemberDeclarationSyntax> GroupMembersByNamespace(IEnumerable<MemberD
{
try
{
CompilationUnitSyntax file = CompilationUnit()
.AddMembers(starterNamespace.AddMembers(GroupMembersByNamespace(fileSimpleName).ToArray()));
results.Add(
string.Format(CultureInfo.InvariantCulture, FilenamePattern, fileSimpleName.Key),
starterNamespace.AddMembers(GroupMembersByNamespace(fileSimpleName).ToArray()));
file);
}
catch (ArgumentException ex)
{
Expand All @@ -1138,6 +1154,7 @@ IEnumerable<MemberDeclarationSyntax> GroupMembersByNamespace(IEnumerable<MemberD
{
UsingDirective(AliasQualifiedName(IdentifierName(Token(SyntaxKind.GlobalKeyword)), IdentifierName(nameof(System)))),
UsingDirective(AliasQualifiedName(IdentifierName(Token(SyntaxKind.GlobalKeyword)), IdentifierName(nameof(System) + "." + nameof(System.Diagnostics)))),
UsingDirective(AliasQualifiedName(IdentifierName(Token(SyntaxKind.GlobalKeyword)), IdentifierName(nameof(System) + "." + nameof(System.Diagnostics) + "." + nameof(System.Diagnostics.CodeAnalysis)))),
UsingDirective(ParseName(GlobalNamespacePrefix + SystemRuntimeCompilerServices)),
UsingDirective(ParseName(GlobalNamespacePrefix + SystemRuntimeInteropServices)),
};
Expand All @@ -1152,9 +1169,8 @@ IEnumerable<MemberDeclarationSyntax> GroupMembersByNamespace(IEnumerable<MemberD
var normalizedResults = new Dictionary<string, CompilationUnitSyntax>(StringComparer.OrdinalIgnoreCase);
results.AsParallel().WithCancellation(cancellationToken).ForAll(kv =>
{
CompilationUnitSyntax? compilationUnit = ((CompilationUnitSyntax)CompilationUnit()
.AddMembers(
kv.Value.AddUsings(usingDirectives.ToArray()))
CompilationUnitSyntax? compilationUnit = ((CompilationUnitSyntax)kv.Value
.AddUsings(usingDirectives.ToArray())
.Accept(new WhitespaceRewriter())!)
.WithLeadingTrivia(FileHeader);
Expand Down Expand Up @@ -4930,6 +4946,7 @@ private ParameterSyntax CreateParameter(TypeHandleInfo parameterInfo, Parameter
return (ranklessArray, default(SyntaxList<MemberDeclarationSyntax>), marshalAs);
}

SyntaxList<MemberDeclarationSyntax> additionalMembers = default;
int length = int.Parse(((LiteralExpressionSyntax)arrayType.RankSpecifiers[0].Sizes[0]).Token.ValueText, CultureInfo.InvariantCulture);
TypeSyntax elementType = arrayType.ElementType;

Expand Down Expand Up @@ -4980,7 +4997,7 @@ private ParameterSyntax CreateParameter(TypeHandleInfo parameterInfo, Parameter
{
// ...
// internal ref TheStruct this[int index] => ref AsSpan()[index];
// internal Span<TheStruct> AsSpan() => MemoryMarshal.CreateSpan(ref _0, 4);
// [UnscopedRef] internal Span<TheStruct> AsSpan() => MemoryMarshal.CreateSpan(ref _0, 4);
fixedLengthStruct = fixedLengthStruct
.AddMembers(
IndexerDeclaration(RefType(elementType).WithTrailingTrivia(TriviaList(Space)))
Expand All @@ -4999,7 +5016,9 @@ private ParameterSyntax CreateParameter(TypeHandleInfo parameterInfo, Parameter
Argument(nameColon: null, TokenWithSpace(SyntaxKind.RefKeyword), firstElementFieldName),
Argument(lengthLiteralSyntax))))))
.WithSemicolonToken(SemicolonWithLineFeed)
.AddAttributeLists(AttributeList().AddAttributes(UnscopedRefAttribute))
.WithLeadingTrivia(InlineArrayUnsafeAsSpanComment));
this.DeclareUnscopedRefAttributeIfNecessary();
}

#pragma warning disable SA1515 // Single-line comment should be preceded by blank line
Expand Down Expand Up @@ -5419,7 +5438,41 @@ private ParameterSyntax CreateParameter(TypeHandleInfo parameterInfo, Parameter
.WithBody(body);
this.volatileCode.AddInlineArrayIndexerExtension(getOrSetAtMethod);

return (fixedLengthStructName, List<MemberDeclarationSyntax>().Add(fixedLengthStruct), null);
additionalMembers = additionalMembers.Add(fixedLengthStruct);
return (fixedLengthStructName, additionalMembers, null);
}

private void DeclareUnscopedRefAttributeIfNecessary()
{
if (this.unscopedRefAttributePredefined)
{
return;
}

const string name = "UnscopedRefAttribute";
this.volatileCode.GenerateSpecialType(name, delegate
{
ExpressionSyntax[] uses = new[]
{
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, IdentifierName(nameof(AttributeTargets)), IdentifierName(nameof(AttributeTargets.Method))),
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, IdentifierName(nameof(AttributeTargets)), IdentifierName(nameof(AttributeTargets.Property))),
MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, IdentifierName(nameof(AttributeTargets)), IdentifierName(nameof(AttributeTargets.Parameter))),
};
AttributeListSyntax usageAttr = AttributeList().AddAttributes(
Attribute(IdentifierName(nameof(AttributeUsageAttribute))).AddArgumentListArguments(
AttributeArgument(
uses.Aggregate((left, right) => BinaryExpression(SyntaxKind.BitwiseOrExpression, left, right))),
AttributeArgument(LiteralExpression(SyntaxKind.FalseLiteralExpression)).WithNameEquals(NameEquals(IdentifierName("AllowMultiple"))),
AttributeArgument(LiteralExpression(SyntaxKind.FalseLiteralExpression)).WithNameEquals(NameEquals(IdentifierName("Inherited")))));
ClassDeclarationSyntax attrDecl = ClassDeclaration(Identifier("UnscopedRefAttribute"))
.WithBaseList(BaseList(SingletonSeparatedList<BaseTypeSyntax>(SimpleBaseType(IdentifierName("Attribute")))))
.AddModifiers(Token(SyntaxKind.InternalKeyword), TokenWithSpace(SyntaxKind.SealedKeyword))
.AddAttributeLists(usageAttr);
NamespaceDeclarationSyntax nsDeclaration = NamespaceDeclaration(ParseName("System.Diagnostics.CodeAnalysis"))
.AddMembers(attrDecl);

this.volatileCode.AddSpecialType(name, nsDeclaration, topLevel: true);
});
}

private bool IsTypeDefStruct(TypeHandleInfo? typeHandleInfo)
Expand Down Expand Up @@ -5777,7 +5830,7 @@ private class GeneratedCode

private readonly List<ClassDeclarationSyntax> safeHandleTypes = new();

private readonly Dictionary<string, MemberDeclarationSyntax> specialTypes = new(StringComparer.Ordinal);
private readonly Dictionary<string, (MemberDeclarationSyntax Type, bool TopLevel)> specialTypes = new(StringComparer.Ordinal);

/// <summary>
/// The set of types that are or have been generated so we don't stack overflow for self-referencing types.
Expand Down Expand Up @@ -5814,7 +5867,11 @@ internal GeneratedCode(GeneratedCode parent)
internal bool IsEmpty => this.modulesAndMembers.Count == 0 && this.types.Count == 0 && this.fieldsToSyntax.Count == 0 && this.safeHandleTypes.Count == 0 && this.specialTypes.Count == 0
&& this.inlineArrayIndexerExtensionsMembers.Count == 0 && this.comInterfaceFriendlyExtensionsMembers.Count == 0;

internal IEnumerable<MemberDeclarationSyntax> GeneratedTypes => this.GetTypesWithInjectedFields().Concat(this.specialTypes.Values).Concat(this.safeHandleTypes);
internal IEnumerable<MemberDeclarationSyntax> GeneratedTypes => this.GetTypesWithInjectedFields()
.Concat(this.specialTypes.Values.Where(st => !st.TopLevel).Select(st => st.Type))
.Concat(this.safeHandleTypes);

internal IEnumerable<MemberDeclarationSyntax> GeneratedTopLevelTypes => this.specialTypes.Values.Where(st => st.TopLevel).Select(st => st.Type);

internal IEnumerable<MethodDeclarationSyntax> ComInterfaceExtensions => this.comInterfaceFriendlyExtensionsMembers;

Expand Down Expand Up @@ -5895,10 +5952,16 @@ internal void AddComInterfaceExtension(IEnumerable<MethodDeclarationSyntax> exte
this.comInterfaceFriendlyExtensionsMembers.AddRange(extension);
}

internal void AddSpecialType(string specialName, MemberDeclarationSyntax specialDeclaration)
/// <summary>
/// Adds a declaration to the generated code.
/// </summary>
/// <param name="specialName">The same constant provided to <see cref="GenerateSpecialType(string, Action)"/>. This serves to avoid repeat declarations.</param>
/// <param name="specialDeclaration">The declaration.</param>
/// <param name="topLevel"><see langword="true" /> if this declaration should <em>not</em> be nested within the top-level namespace for generated code.</param>
internal void AddSpecialType(string specialName, MemberDeclarationSyntax specialDeclaration, bool topLevel = false)
{
this.ThrowIfNotGenerating();
this.specialTypes.Add(specialName, specialDeclaration);
this.specialTypes.Add(specialName, (specialDeclaration, topLevel));
}

internal void AddInteropType(TypeDefinitionHandle typeDefinitionHandle, bool hasUnmanagedName, MemberDeclarationSyntax typeDeclaration)
Expand Down
3 changes: 3 additions & 0 deletions test/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,6 @@ dotnet_diagnostic.SA1401.severity = silent

# SA1133: Do not combine attributes
dotnet_diagnostic.SA1133.severity = silent

# SA1611: Element parameters should be documented
dotnet_diagnostic.SA1611.severity = suggestion
1 change: 1 addition & 0 deletions test/Microsoft.Windows.CsWin32.Tests/GeneratorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1409,6 +1409,7 @@ internal partial struct __uint_4
/// <remarks>
/// ⚠ Important ⚠: When this struct is on the stack, do not let the returned span outlive the stack frame that defines it.
/// </remarks>
[UnscopedRef]
internal Span<uint> AsSpan() => MemoryMarshal.CreateSpan(ref _0, 4);
internal unsafe readonly void CopyTo(Span<uint> target, int length = 4)
Expand Down

0 comments on commit 3c5020f

Please sign in to comment.