Skip to content

Commit

Permalink
Move diagnostics for invalid GeneratedDllImportAttribute usage to gen…
Browse files Browse the repository at this point in the history
…erator instead of analyzer (#65915)
  • Loading branch information
elinor-fung committed Mar 1, 2022
1 parent b4c746b commit d9eafd0
Show file tree
Hide file tree
Showing 9 changed files with 245 additions and 433 deletions.
47 changes: 0 additions & 47 deletions docs/design/libraries/DllImportGenerator/Diagnostics.md

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -75,23 +75,31 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
.CreateSyntaxProvider(
static (node, ct) => ShouldVisitNode(node),
static (context, ct) =>
new
{
MethodDeclarationSyntax syntax = (MethodDeclarationSyntax)context.Node;
if (context.SemanticModel.GetDeclaredSymbol(syntax, ct) is IMethodSymbol methodSymbol
&& methodSymbol.GetAttributes().Any(static attribute => attribute.AttributeClass?.ToDisplayString() == TypeNames.GeneratedDllImportAttribute))
{
Syntax = (MethodDeclarationSyntax)context.Node,
Symbol = (IMethodSymbol)context.SemanticModel.GetDeclaredSymbol(context.Node, ct)!
})
return new { Syntax = syntax, Symbol = methodSymbol };
}
return null;
})
.Where(
static modelData => modelData.Symbol.IsStatic && modelData.Symbol.GetAttributes().Any(
static attribute => attribute.AttributeClass?.ToDisplayString() == TypeNames.GeneratedDllImportAttribute)
);
static modelData => modelData is not null);

var methodsToGenerate = attributedMethods.Where(static data => !data.Symbol.ReturnsByRef && !data.Symbol.ReturnsByRefReadonly);
var methodsWithDiagnostics = attributedMethods.Select(static (data, ct) =>
{
Diagnostic? diagnostic = GetDiagnosticIfInvalidMethodForGeneration(data.Syntax, data.Symbol);
return new { Syntax = data.Syntax, Symbol = data.Symbol, Diagnostic = diagnostic };
});

var refReturnMethods = attributedMethods.Where(static data => data.Symbol.ReturnsByRef || data.Symbol.ReturnsByRefReadonly);
var methodsToGenerate = methodsWithDiagnostics.Where(static data => data.Diagnostic is null);
var invalidMethodDiagnostics = methodsWithDiagnostics.Where(static data => data.Diagnostic is not null);

context.RegisterSourceOutput(refReturnMethods, static (context, refReturnMethod) =>
context.RegisterSourceOutput(invalidMethodDiagnostics, static (context, invalidMethod) =>
{
context.ReportDiagnostic(Diagnostic.Create(GeneratorDiagnostics.ReturnConfigurationNotSupported, refReturnMethod.Syntax.GetLocation(), "ref return", refReturnMethod.Symbol.ToDisplayString()));
context.ReportDiagnostic(invalidMethod.Diagnostic);
});

IncrementalValueProvider<(Compilation compilation, TargetFramework targetFramework, Version targetFrameworkVersion)> compilationAndTargetFramework = context.CompilationProvider
Expand Down Expand Up @@ -739,34 +747,38 @@ private static bool ShouldVisitNode(SyntaxNode syntaxNode)
return false;
}

var methodSyntax = (MethodDeclarationSyntax)syntaxNode;
// Filter out methods with no attributes early.
return ((MethodDeclarationSyntax)syntaxNode).AttributeLists.Count > 0;
}

private static Diagnostic? GetDiagnosticIfInvalidMethodForGeneration(MethodDeclarationSyntax methodSyntax, IMethodSymbol method)
{
// Verify the method has no generic types or defined implementation
// and is marked static and partial.
if (methodSyntax.TypeParameterList is not null
|| methodSyntax.Body is not null
|| !methodSyntax.Modifiers.Any(SyntaxKind.StaticKeyword)
|| !methodSyntax.Modifiers.Any(SyntaxKind.PartialKeyword))
{
return false;
return Diagnostic.Create(GeneratorDiagnostics.InvalidAttributedMethodSignature, methodSyntax.Identifier.GetLocation(), method.Name);
}

// 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 false;
return Diagnostic.Create(GeneratorDiagnostics.InvalidAttributedMethodContainingTypeMissingModifiers, methodSyntax.Identifier.GetLocation(), method.Name, typeDecl.Identifier);
}
}

// Filter out methods with no attributes early.
if (methodSyntax.AttributeLists.Count == 0)
// Verify the method does not have a ref return
if (method.ReturnsByRef || method.ReturnsByRefReadonly)
{
return false;
return Diagnostic.Create(GeneratorDiagnostics.ReturnConfigurationNotSupported, methodSyntax.Identifier.GetLocation(), "ref return", method.ToDisplayString());
}

return true;
return null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,26 @@ public class Ids

private const string Category = "SourceGeneration";

public static readonly DiagnosticDescriptor InvalidAttributedMethodSignature =
new DiagnosticDescriptor(
Ids.InvalidGeneratedDllImportAttributeUsage,
GetResourceString(nameof(Resources.InvalidLibraryImportAttributeUsageTitle)),
GetResourceString(nameof(Resources.InvalidAttributedMethodSignatureMessage)),
Category,
DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: GetResourceString(nameof(Resources.InvalidAttributedMethodDescription)));

public static readonly DiagnosticDescriptor InvalidAttributedMethodContainingTypeMissingModifiers =
new DiagnosticDescriptor(
Ids.InvalidGeneratedDllImportAttributeUsage,
GetResourceString(nameof(Resources.InvalidLibraryImportAttributeUsageTitle)),
GetResourceString(nameof(Resources.InvalidAttributedMethodContainingTypeMissingModifiersMessage)),
Category,
DiagnosticSeverity.Error,
isEnabledByDefault: true,
description: GetResourceString(nameof(Resources.InvalidAttributedMethodDescription)));

public static readonly DiagnosticDescriptor InvalidStringMarshallingConfiguration =
new DiagnosticDescriptor(
Ids.InvalidGeneratedDllImportAttributeUsage,
Expand Down

0 comments on commit d9eafd0

Please sign in to comment.