Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Require that methods marked with GeneratedDllImport are in types that are marked partial and any parents of their containing types are also marked partial. #1091

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
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"));
}
}
}
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
9 changes: 9 additions & 0 deletions DllImportGenerator/DllImportGenerator/DllImportGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,15 @@ public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
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
27 changes: 27 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.

9 changes: 9 additions & 0 deletions DllImportGenerator/DllImportGenerator/Resources.resx
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>
AaronRobinsonMSFT marked this conversation as resolved.
Show resolved Hide resolved
</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