Skip to content

Commit

Permalink
Add validation that the StackBufferSize member exists when a stackall…
Browse files Browse the repository at this point in the history
…oc constructor exists. (#125)
  • Loading branch information
jkoritzinsky authored Sep 26, 2020
1 parent f56021b commit 9e7e221
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,8 @@ class S
struct Native
{
public Native(S s, Span<byte> buffer) {}
public const int StackBufferSize = 0x100;
}";

await VerifyCS.VerifyAnalyzerAsync(source,
Expand All @@ -589,6 +591,8 @@ struct Native
public Native(S s, Span<byte> buffer) {}
public IntPtr Value => IntPtr.Zero;
public const int StackBufferSize = 0x100;
}";

await VerifyCS.VerifyAnalyzerAsync(source,
Expand Down Expand Up @@ -1046,5 +1050,29 @@ struct G<T, U> where U : class
}";
await VerifyCS.VerifyAnalyzerAsync(source);
}

[Fact]
public async Task NativeTypeWithStackallocConstructorWithoutBufferSize_ReportsDiagnostic()
{
string source = @"
using System;
using System.Runtime.InteropServices;
[NativeMarshalling(typeof(Native))]
class S
{
public byte c;
}
[BlittableType]
struct Native
{
public Native(S s) {}
public Native(S s, Span<byte> buffer) {}
}";

await VerifyCS.VerifyAnalyzerAsync(source,
VerifyCS.Diagnostic(StackallocConstructorMustHaveStackBufferSizeConstantRule).WithSpan(15, 5, 15, 45).WithArguments("Native"));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,16 @@ public class ManualTypeMarshallingAnalyzer : DiagnosticAnalyzer
isEnabledByDefault: true,
description: new LocalizableResourceString(nameof(Resources.StackallocMarshallingShouldSupportAllocatingMarshallingFallbackDescription), Resources.ResourceManager, typeof(Resources)));

public readonly static DiagnosticDescriptor StackallocConstructorMustHaveStackBufferSizeConstantRule =
new DiagnosticDescriptor(
"INTEROPGEN012",
"StackallocConstructorMustHaveStackBufferSizeConstant",
new LocalizableResourceString(nameof(Resources.StackallocConstructorMustHaveStackBufferSizeConstantMessage), Resources.ResourceManager, typeof(Resources)),
Category,
DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: new LocalizableResourceString(nameof(Resources.StackallocConstructorMustHaveStackBufferSizeConstantDescription), Resources.ResourceManager, typeof(Resources)));

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
ImmutableArray.Create(
BlittableTypeMustBeBlittableRule,
Expand All @@ -136,7 +146,8 @@ public class ManualTypeMarshallingAnalyzer : DiagnosticAnalyzer
ValuePropertyMustHaveSetterRule,
ValuePropertyMustHaveGetterRule,
GetPinnableReferenceShouldSupportAllocatingMarshallingFallbackRule,
StackallocMarshallingShouldSupportAllocatingMarshallingFallbackRule);
StackallocMarshallingShouldSupportAllocatingMarshallingFallbackRule,
StackallocConstructorMustHaveStackBufferSizeConstantRule);

public override void Initialize(AnalysisContext context)
{
Expand Down Expand Up @@ -309,6 +320,11 @@ private void AnalyzeNativeMarshalerType(SymbolAnalysisContext context, ITypeSymb
&& SymbolEqualityComparer.Default.Equals(SpanOfByte, ctor.Parameters[1].Type))
{
hasStackallocConstructor = true;
IFieldSymbol stackAllocSizeField = nativeType.GetMembers("StackBufferSize").OfType<IFieldSymbol>().FirstOrDefault();
if (stackAllocSizeField is null or { DeclaredAccessibility: not Accessibility.Public } or { IsConst: false } or { Type: not { SpecialType: SpecialType.System_Int32 } })
{
context.ReportDiagnostic(Diagnostic.Create(StackallocConstructorMustHaveStackBufferSizeConstantRule, ctor.DeclaringSyntaxReferences[0].GetSyntax()!.GetLocation(), nativeType.ToDisplayString()));
}
}
}

Expand Down
18 changes: 18 additions & 0 deletions DllImportGenerator/DllImportGenerator/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions DllImportGenerator/DllImportGenerator/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,12 @@
<data name="NativeTypeMustHaveRequiredShapeMessage" xml:space="preserve">
<value>The native type '{0}' must be a value type and have a constructor that takes one parameter of type '{1}' or a parameterless instance method named 'ToManaged' that returns '{1}'.</value>
</data>
<data name="StackallocConstructorMustHaveStackBufferSizeConstantDescription" xml:space="preserve">
<value>When constructor taking a Span&lt;byte&gt; is specified on the native type, the type must also have a public integer constant named StackBufferSize to provide the size of the stack-allocated buffer.</value>
</data>
<data name="StackallocConstructorMustHaveStackBufferSizeConstantMessage" xml:space="preserve">
<value>The native type '{0}' must have a 'public const int StackBufferSize' field that specifies the size of the stack buffer because it has a constructor that takes a stack-allocated Span&lt;byte&gt;.</value>
</data>
<data name="StackallocMarshallingShouldSupportAllocatingMarshallingFallbackDescription" xml:space="preserve">
<value>A type that supports marshalling from managed to native by stack allocation should also support marshalling from managed to native where stack allocation is impossible.</value>
</data>
Expand Down
2 changes: 1 addition & 1 deletion DllImportGenerator/designs/StructMarshalling.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ partial struct TNative
{
public TNative(TManaged managed, Span<byte> stackSpace) {}

public static const int StackBufferSize = /* */;
public const int StackBufferSize = /* */;
}
```

Expand Down

0 comments on commit 9e7e221

Please sign in to comment.