Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/linker.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ILLink.CodeFix/DAMCodeFixProvider.cs134
-rw-r--r--src/ILLink.CodeFix/Resources.resx3
-rw-r--r--src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs10
3 files changed, 143 insertions, 4 deletions
diff --git a/src/ILLink.CodeFix/DAMCodeFixProvider.cs b/src/ILLink.CodeFix/DAMCodeFixProvider.cs
new file mode 100644
index 000000000..60de66cea
--- /dev/null
+++ b/src/ILLink.CodeFix/DAMCodeFixProvider.cs
@@ -0,0 +1,134 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using ILLink.CodeFixProvider;
+using ILLink.RoslynAnalyzer;
+using ILLink.Shared;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.CodeFixes;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Editing;
+using Microsoft.CodeAnalysis.Simplification;
+
+namespace ILLink.CodeFix
+{
+ public class DAMCodeFixProvider : Microsoft.CodeAnalysis.CodeFixes.CodeFixProvider
+ {
+ public static ImmutableArray<DiagnosticDescriptor> GetSupportedDiagnostics ()
+ {
+ var diagDescriptorsArrayBuilder = ImmutableArray.CreateBuilder<DiagnosticDescriptor> ();
+ diagDescriptorsArrayBuilder.Add (DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.DynamicallyAccessedMembersMismatchParameterTargetsThisParameter));
+ diagDescriptorsArrayBuilder.Add (DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.DynamicallyAccessedMembersMismatchFieldTargetsThisParameter));
+ return diagDescriptorsArrayBuilder.ToImmutable ();
+ }
+
+ public static ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => GetSupportedDiagnostics ();
+
+ public sealed override ImmutableArray<string> FixableDiagnosticIds => SupportedDiagnostics.Select (dd => dd.Id).ToImmutableArray ();
+
+ private protected static LocalizableString CodeFixTitle => new LocalizableResourceString (nameof (Resources.DynamicallyAccessedMembersCodeFixTitle), Resources.ResourceManager, typeof (Resources));
+
+ private protected static string FullyQualifiedAttributeName => DynamicallyAccessedMembersAnalyzer.FullyQualifiedDynamicallyAccessedMembersAttribute;
+
+ protected static SyntaxNode[] GetAttributeArguments (ISymbol targetSymbol, SyntaxGenerator syntaxGenerator, Diagnostic diagnostic)
+ {
+ object id = Enum.Parse (typeof (DiagnosticId), diagnostic.Id.Substring (2));
+ switch (id) {
+ case DiagnosticId.DynamicallyAccessedMembersMismatchParameterTargetsThisParameter:
+ return new[] { syntaxGenerator.AttributeArgument (syntaxGenerator.TypedConstantExpression (targetSymbol.GetAttributes ().First (attr => attr.AttributeClass?.ToDisplayString () == DynamicallyAccessedMembersAnalyzer.FullyQualifiedDynamicallyAccessedMembersAttribute).ConstructorArguments[0])) };
+ case DiagnosticId.DynamicallyAccessedMembersMismatchFieldTargetsThisParameter:
+ return new[] { syntaxGenerator.AttributeArgument (syntaxGenerator.TypedConstantExpression (targetSymbol.GetAttributes ().First (attr => attr.AttributeClass?.ToDisplayString () == DynamicallyAccessedMembersAnalyzer.FullyQualifiedDynamicallyAccessedMembersAttribute).ConstructorArguments[0])) };
+ default:
+ return Array.Empty<SyntaxNode> ();
+ }
+ }
+
+ public sealed override FixAllProvider GetFixAllProvider ()
+ {
+ // See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/FixAllProvider.md for more information on Fix All Providers
+ return WellKnownFixAllProviders.BatchFixer;
+ }
+
+ public override async Task RegisterCodeFixesAsync (CodeFixContext context)
+ {
+ var document = context.Document;
+ if (await document.GetSyntaxRootAsync (context.CancellationToken).ConfigureAwait (false) is not { } root)
+ return;
+ var diagnostic = context.Diagnostics.First ();
+ SyntaxNode diagnosticNode = root.FindNode (diagnostic.Location.SourceSpan, getInnermostNodeForTie: true);
+ if (await document.GetSemanticModelAsync (context.CancellationToken).ConfigureAwait (false) is not { } model)
+ return;
+ // Note: We get the target symbol from the diagnostic location.
+ // This works when the diagnostic location is a method call, because the target symbol will be the called method with annotations, but won't work in general for other kinds of diagnostics.
+ if (model.GetSymbolInfo (diagnosticNode).Symbol is not { } targetSymbol)
+ return;
+ if (model.Compilation.GetTypeByMetadataName (FullyQualifiedAttributeName) is not { } attributeSymbol)
+ return;
+
+ if (diagnosticNode is not InvocationExpressionSyntax invocationExpression)
+ return;
+
+ var arguments = invocationExpression.ArgumentList.Arguments;
+
+ if (arguments.Count > 1)
+ return;
+
+ if (arguments.Count == 1) {
+ if (arguments[0].Expression is not LiteralExpressionSyntax literalSyntax
+ || literalSyntax.Kind () is not SyntaxKind.StringLiteralExpression) {
+ return;
+ }
+ }
+
+ // N.B. May be null for FieldDeclaration, since field declarations can declare multiple variables
+ var attributableSymbol = (invocationExpression.Expression is MemberAccessExpressionSyntax simpleMember
+ && simpleMember.Expression is IdentifierNameSyntax name) ? model.GetSymbolInfo (name).Symbol : null;
+
+
+ if (attributableSymbol is null)
+ return;
+
+ var attributableNodeList = attributableSymbol.DeclaringSyntaxReferences;
+
+ if (attributableNodeList.Length != 1)
+ return;
+
+ var attributableNode = attributableNodeList[0].GetSyntax ();
+
+ if (attributableNode is null) return;
+
+ var attributeArguments = GetAttributeArguments (targetSymbol, SyntaxGenerator.GetGenerator (document), diagnostic);
+ var codeFixTitle = CodeFixTitle.ToString ();
+
+ context.RegisterCodeFix (CodeAction.Create (
+ title: codeFixTitle,
+ createChangedDocument: ct => AddAttributeAsync (
+ document, attributableNode, attributeArguments, attributeSymbol, ct),
+ equivalenceKey: codeFixTitle), diagnostic);
+ }
+
+ private static async Task<Document> AddAttributeAsync (
+ Document document,
+ SyntaxNode targetNode,
+ SyntaxNode[] attributeArguments,
+ ITypeSymbol attributeSymbol,
+ CancellationToken cancellationToken)
+ {
+ var editor = await DocumentEditor.CreateAsync (document, cancellationToken).ConfigureAwait (false);
+ var generator = editor.Generator;
+ var attribute = generator.Attribute (
+ generator.TypeExpression (attributeSymbol), attributeArguments)
+ .WithAdditionalAnnotations (Simplifier.Annotation, Simplifier.AddImportsAnnotation);
+
+ editor.AddAttribute (targetNode, attribute);
+ return editor.GetChangedDocument ();
+ }
+ }
+}
diff --git a/src/ILLink.CodeFix/Resources.resx b/src/ILLink.CodeFix/Resources.resx
index 6105f1c1e..1e674b631 100644
--- a/src/ILLink.CodeFix/Resources.resx
+++ b/src/ILLink.CodeFix/Resources.resx
@@ -129,4 +129,7 @@
<data name="UconditionalSuppressMessageCodeFixTitle" xml:space="preserve">
<value>Add UnconditionalSuppressMessage attribute to parent method</value>
</data>
+ <data name="DynamicallyAccessedMembersCodeFixTitle" xml:space="preserve">
+ <value>Add DynamicallyAccessedMembers attribute to source of warning</value>
+ </data>
</root> \ No newline at end of file
diff --git a/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs b/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs
index 0c93e3f62..0e095dbc5 100644
--- a/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs
+++ b/src/ILLink.RoslynAnalyzer/DynamicallyAccessedMembersAnalyzer.cs
@@ -22,8 +22,9 @@ namespace ILLink.RoslynAnalyzer
{
internal const string DynamicallyAccessedMembers = nameof (DynamicallyAccessedMembers);
internal const string DynamicallyAccessedMembersAttribute = nameof (DynamicallyAccessedMembersAttribute);
+ public const string FullyQualifiedDynamicallyAccessedMembersAttribute = "System.Diagnostics.CodeAnalysis." + DynamicallyAccessedMembersAttribute;
- static ImmutableArray<DiagnosticDescriptor> GetSupportedDiagnostics ()
+ public static ImmutableArray<DiagnosticDescriptor> GetSupportedDiagnostics ()
{
var diagDescriptorsArrayBuilder = ImmutableArray.CreateBuilder<DiagnosticDescriptor> (26);
diagDescriptorsArrayBuilder.Add (DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.RequiresUnreferencedCode));
@@ -114,12 +115,12 @@ namespace ILLink.RoslynAnalyzer
var symbol = context.SemanticModel.GetSymbolInfo (context.Node).Symbol;
- // Avoid unnecesary execution if not NamedType or Method
+ // Avoid unnecessary execution if not NamedType or Method
if (symbol is not INamedTypeSymbol && symbol is not IMethodSymbol)
return;
// Members inside nameof or cref comments, commonly used to access the string value of a variable, type, or a memeber,
- // can generate diagnostics warnings, which can be noisy and unhelpful.
+ // can generate diagnostics warnings, which can be noisy and unhelpful.
// Walking the node heirarchy to check if the member is inside a nameof/cref to not generate diagnostics
var parentNode = context.Node;
while (parentNode != null) {
@@ -210,11 +211,12 @@ namespace ILLink.RoslynAnalyzer
method.Locations[0], method.GetDisplayName (), overriddenMethod.GetDisplayName ()));
for (int i = 0; i < method.Parameters.Length; i++) {
- if (FlowAnnotations.GetMethodParameterAnnotation (method.Parameters[i]) != FlowAnnotations.GetMethodParameterAnnotation (overriddenMethod.Parameters[i]))
+ if (FlowAnnotations.GetMethodParameterAnnotation (method.Parameters[i]) != FlowAnnotations.GetMethodParameterAnnotation (overriddenMethod.Parameters[i])) {
context.ReportDiagnostic (Diagnostic.Create (
DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.DynamicallyAccessedMembersMismatchOnMethodParameterBetweenOverrides),
method.Parameters[i].Locations[0],
method.Parameters[i].GetDisplayName (), method.GetDisplayName (), overriddenMethod.Parameters[i].GetDisplayName (), overriddenMethod.GetDisplayName ()));
+ }
}
for (int i = 0; i < method.TypeParameters.Length; i++) {