Skip to content

Commit

Permalink
Require that methods marked with GeneratedDllImport are in types that…
Browse files Browse the repository at this point in the history
… are marked partial and any parents of their containing types are also marked partial. (dotnet/runtimelab#1091)

Commit migrated from dotnet/runtimelab@53af4b5
  • Loading branch information
jkoritzinsky authored May 21, 2021
1 parent 7d9a4d9 commit 0fd1fa1
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ DLLIMPORTGENANALYZER013 | Usage | Warning | GeneratedDllImportMissin
DLLIMPORTGENANALYZER014 | Usage | Error | RefValuePropertyUnsupported
DLLIMPORTGENANALYZER015 | Interoperability | Disabled | ConvertToGeneratedDllImportAnalyzer
DLLIMPORTGENANALYZER016 | Usage | Error | GenericTypeMustBeClosed
DLLIMPORTGENANALYZER017 | Usage | Warning | GeneratedDllImportContainingTypeMissingModifiers
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public static class Ids

// GeneratedDllImport
public const string GeneratedDllImportMissingRequiredModifiers = Prefix + "013";
public const string GeneratedDllImportContaiingTypeMissingRequiredModifiers = Prefix + "017";

// Migration from DllImport to GeneratedDllImport
public const string ConvertToGeneratedDllImport = Prefix + "015";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,17 @@ public class GeneratedDllImportAnalyzer : DiagnosticAnalyzer
isEnabledByDefault: true,
description: GetResourceString(nameof(Resources.GeneratedDllImportMissingModifiersDescription)));

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(GeneratedDllImportMissingModifiers);
public readonly static DiagnosticDescriptor GeneratedDllImportContainingTypeMissingModifiers =
new DiagnosticDescriptor(
Ids.GeneratedDllImportContaiingTypeMissingRequiredModifiers,
GetResourceString(nameof(Resources.GeneratedDllImportContainingTypeMissingModifiersTitle)),
GetResourceString(nameof(Resources.GeneratedDllImportContainingTypeMissingModifiersMessage)),
Category,
DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: GetResourceString(nameof(Resources.GeneratedDllImportContainingTypeMissingModifiersDescription)));

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(GeneratedDllImportMissingModifiers, GeneratedDllImportContainingTypeMissingModifiers);

public override void Initialize(AnalysisContext context)
{
Expand Down Expand Up @@ -64,17 +74,27 @@ private static void AnalyzeSymbol(SymbolAnalysisContext context, INamedTypeSymbo
foreach (var reference in methodSymbol.DeclaringSyntaxReferences)
{
var syntax = reference.GetSyntax(context.CancellationToken);
var methodSyntax = syntax as MethodDeclarationSyntax;
if (methodSyntax == null)
continue;

if (!methodSyntax.Modifiers.Any(SyntaxKind.PartialKeyword))
if (syntax is MethodDeclarationSyntax methodSyntax && !methodSyntax.Modifiers.Any(SyntaxKind.PartialKeyword))
{
// Must be marked partial
context.ReportDiagnostic(methodSymbol.CreateDiagnostic(GeneratedDllImportMissingModifiers, methodSymbol.Name));
break;
}
}

for (INamedTypeSymbol? typeSymbol = methodSymbol.ContainingType; typeSymbol is not null; typeSymbol = typeSymbol.ContainingType)
{
foreach (var reference in typeSymbol.DeclaringSyntaxReferences)
{
var syntax = reference.GetSyntax(context.CancellationToken);
if (syntax is TypeDeclarationSyntax typeSyntax && !typeSyntax.Modifiers.Any(SyntaxKind.PartialKeyword))
{
// Must be marked partial
context.ReportDiagnostic(typeSymbol.CreateDiagnostic(GeneratedDllImportContainingTypeMissingModifiers, typeSymbol.Name));
break;
}
}
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,15 @@ public void OnVisitSyntaxNode(GeneratorSyntaxContext context)
return;
}

// Verify that the types the method is declared in are marked partial.
for (SyntaxNode? parentNode = methodSyntax.Parent; parentNode is TypeDeclarationSyntax typeDecl; parentNode = parentNode.Parent)
{
if (!typeDecl.Modifiers.Any(SyntaxKind.PartialKeyword))
{
return;
}
}

// Check if the method is marked with the GeneratedDllImport attribute.
foreach (AttributeListSyntax listSyntax in methodSyntax.AttributeLists)
{
Expand Down

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

Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,15 @@
<data name="CustomTypeMarshallingNativeToManagedUnsupported" xml:space="preserve">
<value>The specified parameter needs to be marshalled from native to managed, but the native type '{0}' does not support it.</value>
</data>
<data name="GeneratedDllImportContainingTypeMissingModifiersDescription" xml:space="preserve">
<value>Types that contain methods marked with 'GeneratedDllImportAttribute' must be 'partial'. P/Invoke source generation will ignore methods contained within non-partial types.</value>
</data>
<data name="GeneratedDllImportContainingTypeMissingModifiersMessage" xml:space="preserve">
<value>Type '{0}' contains methods marked with 'GeneratedDllImportAttribute' and should be 'partial'. P/Invoke source generation will ignore methods contained within non-partial types.</value>
</data>
<data name="GeneratedDllImportContainingTypeMissingModifiersTitle" xml:space="preserve">
<value>Types that contain methods marked with 'GeneratedDllImportAttribute' must be 'partial'.</value>
</data>
<data name="GeneratedDllImportMissingModifiersDescription" xml:space="preserve">
<value>Methods marked with 'GeneratedDllImportAttribute' should be 'static' and 'partial'. P/Invoke source generation will ignore methods that are not 'static' and 'partial'.</value>
</data>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,5 +139,55 @@ partial class Test
";
await VerifyCS.VerifyAnalyzerAsync(source);
}

[Theory]
[InlineData("class")]
[InlineData("struct")]
[InlineData("record")]

public async Task NonPartialParentType_Diagnostic(string typeKind)
{
string source = $@"
using System.Runtime.InteropServices;
{typeKind} {{|#0:Test|}}
{{
[GeneratedDllImport(""DoesNotExist"")]
static partial void {{|CS0751:Method2|}}();
}}
";

await VerifyCS.VerifyAnalyzerAsync(
source,
VerifyCS.Diagnostic(GeneratedDllImportContainingTypeMissingModifiers)
.WithLocation(0)
.WithArguments("Test"));
}

[Theory]
[InlineData("class")]
[InlineData("struct")]
[InlineData("record")]

public async Task NonPartialGrandparentType_Diagnostic(string typeKind)
{

string source = $@"
using System.Runtime.InteropServices;
{typeKind} {{|#0:Test|}}
{{
partial class TestInner
{{
[GeneratedDllImport(""DoesNotExist"")]
static partial void Method2();
}}
}}
";

await VerifyCS.VerifyAnalyzerAsync(
source,
VerifyCS.Diagnostic(GeneratedDllImportContainingTypeMissingModifiers)
.WithLocation(0)
.WithArguments("Test"));
}
}
}

0 comments on commit 0fd1fa1

Please sign in to comment.