Skip to content
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 @@ -262,8 +262,8 @@ public void AnalyzeAttribute(SyntaxNodeAnalysisContext context)
AttributeSyntax syntax = (AttributeSyntax)context.Node;
ISymbol attributedSymbol = context.ContainingSymbol!;

AttributeData attr = GetAttributeData(syntax, attributedSymbol);
if (attr.AttributeClass?.ToDisplayString() == TypeNames.CustomMarshallerAttribute
AttributeData? attr = syntax.FindAttributeData(attributedSymbol);
if (attr?.AttributeClass?.ToDisplayString() == TypeNames.CustomMarshallerAttribute
&& attr.AttributeConstructor is not null)
{
DiagnosticReporter managedTypeReporter = DiagnosticReporter.CreateForLocation(syntax.FindArgumentWithNameOrArity("managedType", 0).FindTypeExpressionOrNullLocation(), context.ReportDiagnostic);
Expand Down Expand Up @@ -313,20 +313,6 @@ private void AnalyzeMarshallerType(DiagnosticReporter diagnosticFactory, INamedT
{
// TODO: Implement for the V2 shapes
}

private static AttributeData GetAttributeData(AttributeSyntax syntax, ISymbol symbol)
{
if (syntax.FirstAncestorOrSelf<AttributeListSyntax>().Target?.Identifier.IsKind(SyntaxKind.ReturnKeyword) == true)
{
return ((IMethodSymbol)symbol).GetReturnTypeAttributes().First(attributeSyntaxLocationMatches);
}
return symbol.GetAttributes().First(attributeSyntaxLocationMatches);

bool attributeSyntaxLocationMatches(AttributeData attrData)
{
return attrData.ApplicationSyntaxReference!.SyntaxTree == syntax.SyntaxTree && attrData.ApplicationSyntaxReference.Span == syntax.Span;
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ public void AnalyzeAttribute(SyntaxNodeAnalysisContext context)
AttributeSyntax syntax = (AttributeSyntax)context.Node;
ISymbol attributedSymbol = context.ContainingSymbol!;

AttributeData attr = GetAttributeData(syntax, attributedSymbol);
if (attr.AttributeClass?.ToDisplayString() == TypeNames.NativeMarshallingAttribute
AttributeData? attr = syntax.FindAttributeData(attributedSymbol);
if (attr?.AttributeClass?.ToDisplayString() == TypeNames.NativeMarshallingAttribute
&& attr.AttributeConstructor is not null)
{
INamedTypeSymbol? entryType = (INamedTypeSymbol?)attr.ConstructorArguments[0].Value;
Expand Down Expand Up @@ -163,20 +163,6 @@ private void AnalyzeManagedTypeMarshallingInfo(
}
}

private static AttributeData GetAttributeData(AttributeSyntax syntax, ISymbol symbol)
{
if (syntax.FirstAncestorOrSelf<AttributeListSyntax>().Target?.Identifier.IsKind(SyntaxKind.ReturnKeyword) == true)
{
return ((IMethodSymbol)symbol).GetReturnTypeAttributes().First(attributeSyntaxLocationMatches);
}
return symbol.GetAttributes().First(attributeSyntaxLocationMatches);

bool attributeSyntaxLocationMatches(AttributeData attrData)
{
return attrData.ApplicationSyntaxReference!.SyntaxTree == syntax.SyntaxTree && attrData.ApplicationSyntaxReference.Span == syntax.Span;
}
}

private static ITypeSymbol GetSymbolType(ISymbol symbol)
{
return symbol switch
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,31 @@ public static Location FindTypeExpressionOrNullLocation(this AttributeArgumentSy
return walker.TypeExpressionLocation;
}

public static AttributeData? FindAttributeData(this AttributeSyntax syntax, ISymbol targetSymbol)
{
AttributeTargetSpecifierSyntax attributeTarget = syntax.FirstAncestorOrSelf<AttributeListSyntax>().Target;
if (attributeTarget is not null)
{
switch (attributeTarget.Identifier.Kind())
{
case SyntaxKind.ReturnKeyword:
return ((IMethodSymbol)targetSymbol).GetReturnTypeAttributes().First(attributeSyntaxLocationMatches);
case SyntaxKind.AssemblyKeyword:
return targetSymbol.ContainingAssembly.GetAttributes().First(attributeSyntaxLocationMatches);
case SyntaxKind.ModuleKeyword:
return targetSymbol.ContainingModule.GetAttributes().First(attributeSyntaxLocationMatches);
default:
return null;
Comment on lines +35 to +42

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't look at the context of the callers or what's possible and what's not.

But there are other attribute targets to consider (only if they're relevant):

https://github.com/dotnet/roslyn/blob/1c480b07dbd55e2dce5580fc6a3e5b468d53c64d/src/Compilers/CSharp/Test/Emit2/Attributes/AttributeTests_Locations.cs#L87-L97

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rest of them aren't relevant enough to cover here. Returning null is fine. Technically, we don't need to handle assembly and module as well as this PR does, but I'd rather cleanly handle them than returning null if possible.

If your PR to add IAttributeOperation ever gets merged, we're going to switch to use that API from Roslyn and work with the IOperation tree instead of the syntax tree (and effectively obsolete all usages of this class).

}
}
return targetSymbol.GetAttributes().First(attributeSyntaxLocationMatches);

bool attributeSyntaxLocationMatches(AttributeData attrData)
{
return attrData.ApplicationSyntaxReference!.SyntaxTree == syntax.SyntaxTree && attrData.ApplicationSyntaxReference.Span == syntax.Span;
}
}

private sealed class FindTypeLocationWalker : CSharpSyntaxWalker
{
public Location? TypeExpressionLocation { get; private set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,5 +256,19 @@ static unsafe class MarshallerType<U, V, W> where W : unmanaged
await VerifyCS.VerifyAnalyzerAsync(source,
VerifyCS.Diagnostic(GenericEntryPointMarshallerTypeMustBeClosedOrMatchArityRule).WithLocation(0).WithArguments("MarshallerType<U, V, W>", "ManagedType<T>"));
}

[Fact]
public async Task UnrelatedAssemblyOrModuleTargetDiagnostic_DoesNotCauseException()
{
string source = """
using System.Reflection;
using System.Runtime.CompilerServices;

[assembly:AssemblyMetadata("MyKey", "MyValue")]
[module:SkipLocalsInit]
""";

await VerifyCS.VerifyAnalyzerAsync(source);
}
}
}