From 78ff6ec73be75dcfcc7550bf0c486ece3e1d926c Mon Sep 17 00:00:00 2001 From: Mateo Torres Ruiz Date: Tue, 15 Dec 2020 23:12:18 -0800 Subject: [PATCH 1/9] Add analyzer support for SingleFileUnsupportedAttribute --- .../INamedTypeSymbolExtensions.cs | 33 +++++++++ .../RequiresUnreferencedCodeAnalyzer.cs | 24 +------ src/ILLink.RoslynAnalyzer/Resources.resx | 6 ++ .../SingleFileUnsupportedAnalyzer.cs | 68 +++++++++++++++++++ .../SingleFileUnsupportedAttribute.cs | 16 +++++ .../SingleFileUnsupportedAnalyzerTests.cs | 42 ++++++++++++ 6 files changed, 167 insertions(+), 22 deletions(-) create mode 100644 src/ILLink.RoslynAnalyzer/INamedTypeSymbolExtensions.cs create mode 100644 src/ILLink.RoslynAnalyzer/SingleFileUnsupportedAnalyzer.cs create mode 100644 test/ILLink.RoslynAnalyzer.Tests/Dependencies/SingleFileUnsupportedAttribute.cs create mode 100644 test/ILLink.RoslynAnalyzer.Tests/SingleFileUnsupportedAnalyzerTests.cs diff --git a/src/ILLink.RoslynAnalyzer/INamedTypeSymbolExtensions.cs b/src/ILLink.RoslynAnalyzer/INamedTypeSymbolExtensions.cs new file mode 100644 index 000000000000..01a56ddd68aa --- /dev/null +++ b/src/ILLink.RoslynAnalyzer/INamedTypeSymbolExtensions.cs @@ -0,0 +1,33 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Microsoft.CodeAnalysis; + +namespace ILLink.RoslynAnalyzer +{ + internal static class INamedTypeSymbolExtensions + { + /// + /// Returns true if has the same name as + /// + internal static bool HasName (this INamedTypeSymbol type, string typeName) + { + var roSpan = typeName.AsSpan (); + INamespaceOrTypeSymbol? currentType = type; + while (roSpan.Length > 0) { + var dot = roSpan.LastIndexOf ('.'); + var currentName = dot < 0 ? roSpan : roSpan.Slice (dot + 1); + if (currentType is null || + !currentName.Equals (currentType.Name.AsSpan (), StringComparison.Ordinal)) { + return false; + } + currentType = (INamespaceOrTypeSymbol?) currentType.ContainingType ?? currentType.ContainingNamespace; + roSpan = roSpan.Slice (0, dot > 0 ? dot : 0); + } + + return true; + } + } +} diff --git a/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs b/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs index 1b93e3cf3971..0c8e72bad819 100644 --- a/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs +++ b/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs @@ -82,6 +82,7 @@ void CheckMethodOrCtorCall ( if (operationContext.ContainingSymbol is IMethodSymbol && TryGetRequiresUnreferencedCodeAttribute (operationContext.ContainingSymbol.GetAttributes (), out requiresUnreferencedCode)) return; + if (TryGetRequiresUnreferencedCodeAttribute (method.GetAttributes (), out requiresUnreferencedCode)) { operationContext.ReportDiagnostic (Diagnostic.Create ( s_rule, @@ -94,27 +95,6 @@ void CheckMethodOrCtorCall ( }); } - /// - /// Returns true if has the same name as - /// - internal static bool IsNamedType (INamedTypeSymbol type, string typeName) - { - var roSpan = typeName.AsSpan (); - INamespaceOrTypeSymbol? currentType = type; - while (roSpan.Length > 0) { - var dot = roSpan.LastIndexOf ('.'); - var currentName = dot < 0 ? roSpan : roSpan.Slice (dot + 1); - if (currentType is null || - !currentName.Equals (currentType.Name.AsSpan (), StringComparison.Ordinal)) { - return false; - } - currentType = (INamespaceOrTypeSymbol?) currentType.ContainingType ?? currentType.ContainingNamespace; - roSpan = roSpan.Slice (0, dot > 0 ? dot : 0); - } - - return true; - } - /// /// Returns a RequiresUnreferencedCodeAttribute if found /// @@ -123,7 +103,7 @@ static bool TryGetRequiresUnreferencedCodeAttribute (ImmutableArray '{0}' will throw for assemblies embedded in a single-file app + + + + + + \ No newline at end of file diff --git a/src/ILLink.RoslynAnalyzer/SingleFileUnsupportedAnalyzer.cs b/src/ILLink.RoslynAnalyzer/SingleFileUnsupportedAnalyzer.cs new file mode 100644 index 000000000000..e8dbb909023b --- /dev/null +++ b/src/ILLink.RoslynAnalyzer/SingleFileUnsupportedAnalyzer.cs @@ -0,0 +1,68 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Immutable; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +namespace ILLink.RoslynAnalyzer +{ + public sealed class SingleFileUnsupportedAnalyzer : DiagnosticAnalyzer + { + public const string IL3002 = nameof (IL3002); + + private static readonly DiagnosticDescriptor SingleFileUnsupportedRule = new DiagnosticDescriptor ( + IL3002, + new LocalizableResourceString (nameof (Resources.SingleFileUnsupportedTitle), + Resources.ResourceManager, typeof (Resources)), + new LocalizableResourceString (nameof (Resources.SingleFileUnsupportedMessage), + Resources.ResourceManager, typeof (Resources)), + DiagnosticCategory.SingleFile, + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create (SingleFileUnsupportedRule); + + public override void Initialize (AnalysisContext context) + { + context.EnableConcurrentExecution (); + context.ConfigureGeneratedCodeAnalysis (GeneratedCodeAnalysisFlags.ReportDiagnostics); + + context.RegisterCompilationStartAction (context => { + var compilation = context.Compilation; + + var isSingleFilePublish = context.Options.GetMSBuildPropertyValue (MSBuildPropertyOptionNames.PublishSingleFile, compilation); + if (!string.Equals (isSingleFilePublish?.Trim (), "true", StringComparison.OrdinalIgnoreCase)) + return; + + var includesAllContent = context.Options.GetMSBuildPropertyValue (MSBuildPropertyOptionNames.IncludeAllContentForSelfExtract, compilation); + if (string.Equals (includesAllContent?.Trim (), "true", StringComparison.OrdinalIgnoreCase)) + return; + + context.RegisterOperationAction (operationContext => { + var methodInvocation = (IInvocationOperation) operationContext.Operation; + var targetMethod = methodInvocation.TargetMethod; + var attributes = targetMethod.GetAttributes (); + + if (attributes.FirstOrDefault (attr => attr.AttributeClass is { } attrClass && + attrClass.HasName ("System.Diagnostics.CodeAnalysis.SingleFileUnsupportedAttribute")) is var singleFileUnsupportedAttr && + singleFileUnsupportedAttr != null) { + string? messageArgument = singleFileUnsupportedAttr.ConstructorArguments[0].Value as string; + string? urlArgument = singleFileUnsupportedAttr.NamedArguments.FirstOrDefault (na => na.Key == "Url").Value.Value?.ToString (); + + operationContext.ReportDiagnostic (Diagnostic.Create ( + SingleFileUnsupportedRule, + methodInvocation.Syntax.GetLocation (), + targetMethod.OriginalDefinition.ToString (), + messageArgument, + urlArgument)); + } + }, OperationKind.Invocation); + }); + } + } +} diff --git a/test/ILLink.RoslynAnalyzer.Tests/Dependencies/SingleFileUnsupportedAttribute.cs b/test/ILLink.RoslynAnalyzer.Tests/Dependencies/SingleFileUnsupportedAttribute.cs new file mode 100644 index 000000000000..1f87cba8d2d8 --- /dev/null +++ b/test/ILLink.RoslynAnalyzer.Tests/Dependencies/SingleFileUnsupportedAttribute.cs @@ -0,0 +1,16 @@ +namespace System.Diagnostics.CodeAnalysis +{ +#nullable enable + [AttributeUsage (AttributeTargets.Method | AttributeTargets.Constructor, Inherited = false, AllowMultiple = false)] + public sealed class SingleFileUnsupportedAttribute : Attribute + { + public SingleFileUnsupportedAttribute (string message) + { + Message = message; + } + + public string Message { get; } + + public string? Url { get; set; } + } +} \ No newline at end of file diff --git a/test/ILLink.RoslynAnalyzer.Tests/SingleFileUnsupportedAnalyzerTests.cs b/test/ILLink.RoslynAnalyzer.Tests/SingleFileUnsupportedAnalyzerTests.cs new file mode 100644 index 000000000000..7af21b87e8fc --- /dev/null +++ b/test/ILLink.RoslynAnalyzer.Tests/SingleFileUnsupportedAnalyzerTests.cs @@ -0,0 +1,42 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Testing; +using Xunit; +using VerifyCS = ILLink.RoslynAnalyzer.Tests.CSharpAnalyzerVerifier< + ILLink.RoslynAnalyzer.SingleFileUnsupportedAnalyzer>; + +namespace ILLink.RoslynAnalyzer.Tests +{ + public class SingleFileUnsupportedAnalyzerTests + { + static Task VerifySingleFileUnsupportedAnalyzer (string source, params DiagnosticResult[] expected) + { + var singleFileUnsupportedAttribute = File.ReadAllText ("../../../../../test/ILLink.RoslynAnalyzer.Tests/Dependencies/SingleFileUnsupportedAttribute.cs"); + source = singleFileUnsupportedAttribute + Environment.NewLine + source; + return VerifyCS.VerifyAnalyzerAsync (source, + TestCaseUtils.UseMSBuildProperties (MSBuildPropertyOptionNames.PublishSingleFile), + expected); + } + + [Fact] + public Task SimpleDiagnostic () + { + var TestSingleFileUnsupportedOnMethod = @" +class C +{ + [System.Diagnostics.CodeAnalysis.SingleFileUnsupported (""Message from attribute"")] + void M1() + { + } + + void M2() + { + M1(); + } +}"; + return VerifySingleFileUnsupportedAnalyzer (TestSingleFileUnsupportedOnMethod, + VerifyCS.Diagnostic ().WithSpan (27, 3, 27, 7).WithArguments ("C.M2()", "Message from attribute")); + } + } +} From 9cc5221497b71a410759462a9f32533add8a12df Mon Sep 17 00:00:00 2001 From: Mateo Torres Ruiz Date: Mon, 21 Dec 2020 15:41:50 -0800 Subject: [PATCH 2/9] Add tests --- .../INamedTypeSymbolExtensions.cs | 2 +- .../ISymbolExtensions.cs | 17 ++++ .../SingleFileUnsupportedAnalyzer.cs | 8 +- .../SingleFileUnsupportedAttribute.cs | 16 ---- .../SingleFileUnsupportedAnalyzerTests.cs | 78 ++++++++++++++++++- .../TestCaseUtils.cs | 1 - 6 files changed, 100 insertions(+), 22 deletions(-) create mode 100644 src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs delete mode 100644 test/ILLink.RoslynAnalyzer.Tests/Dependencies/SingleFileUnsupportedAttribute.cs diff --git a/src/ILLink.RoslynAnalyzer/INamedTypeSymbolExtensions.cs b/src/ILLink.RoslynAnalyzer/INamedTypeSymbolExtensions.cs index 01a56ddd68aa..bdccec06903a 100644 --- a/src/ILLink.RoslynAnalyzer/INamedTypeSymbolExtensions.cs +++ b/src/ILLink.RoslynAnalyzer/INamedTypeSymbolExtensions.cs @@ -7,7 +7,7 @@ namespace ILLink.RoslynAnalyzer { - internal static class INamedTypeSymbolExtensions + static class INamedTypeSymbolExtensions { /// /// Returns true if has the same name as diff --git a/src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs b/src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs new file mode 100644 index 000000000000..d271896c26a3 --- /dev/null +++ b/src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Linq; +using Microsoft.CodeAnalysis; + +namespace ILLink.RoslynAnalyzer +{ + static class ISymbolExtensions + { + internal static bool HasAttribute (this ISymbol symbol, string attributeName) + { + return symbol.GetAttributes ().Where (a => a?.AttributeClass?.Name == attributeName).Count () > 0; + } + } +} diff --git a/src/ILLink.RoslynAnalyzer/SingleFileUnsupportedAnalyzer.cs b/src/ILLink.RoslynAnalyzer/SingleFileUnsupportedAnalyzer.cs index e8dbb909023b..9235081cf3b8 100644 --- a/src/ILLink.RoslynAnalyzer/SingleFileUnsupportedAnalyzer.cs +++ b/src/ILLink.RoslynAnalyzer/SingleFileUnsupportedAnalyzer.cs @@ -11,9 +11,11 @@ namespace ILLink.RoslynAnalyzer { + [DiagnosticAnalyzer (LanguageNames.CSharp)] public sealed class SingleFileUnsupportedAnalyzer : DiagnosticAnalyzer { public const string IL3002 = nameof (IL3002); + const string SingleFileUnsupportedAttribute = nameof (SingleFileUnsupportedAttribute); private static readonly DiagnosticDescriptor SingleFileUnsupportedRule = new DiagnosticDescriptor ( IL3002, @@ -44,12 +46,16 @@ public override void Initialize (AnalysisContext context) return; context.RegisterOperationAction (operationContext => { + // Do not emit any diagnostic if caller is annotated with the attribute too. + if (operationContext.ContainingSymbol.HasAttribute (SingleFileUnsupportedAttribute)) + return; + var methodInvocation = (IInvocationOperation) operationContext.Operation; var targetMethod = methodInvocation.TargetMethod; var attributes = targetMethod.GetAttributes (); if (attributes.FirstOrDefault (attr => attr.AttributeClass is { } attrClass && - attrClass.HasName ("System.Diagnostics.CodeAnalysis.SingleFileUnsupportedAttribute")) is var singleFileUnsupportedAttr && + attrClass.HasName ("System.Diagnostics.CodeAnalysis." + SingleFileUnsupportedAttribute)) is var singleFileUnsupportedAttr && singleFileUnsupportedAttr != null) { string? messageArgument = singleFileUnsupportedAttr.ConstructorArguments[0].Value as string; string? urlArgument = singleFileUnsupportedAttr.NamedArguments.FirstOrDefault (na => na.Key == "Url").Value.Value?.ToString (); diff --git a/test/ILLink.RoslynAnalyzer.Tests/Dependencies/SingleFileUnsupportedAttribute.cs b/test/ILLink.RoslynAnalyzer.Tests/Dependencies/SingleFileUnsupportedAttribute.cs deleted file mode 100644 index 1f87cba8d2d8..000000000000 --- a/test/ILLink.RoslynAnalyzer.Tests/Dependencies/SingleFileUnsupportedAttribute.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace System.Diagnostics.CodeAnalysis -{ -#nullable enable - [AttributeUsage (AttributeTargets.Method | AttributeTargets.Constructor, Inherited = false, AllowMultiple = false)] - public sealed class SingleFileUnsupportedAttribute : Attribute - { - public SingleFileUnsupportedAttribute (string message) - { - Message = message; - } - - public string Message { get; } - - public string? Url { get; set; } - } -} \ No newline at end of file diff --git a/test/ILLink.RoslynAnalyzer.Tests/SingleFileUnsupportedAnalyzerTests.cs b/test/ILLink.RoslynAnalyzer.Tests/SingleFileUnsupportedAnalyzerTests.cs index 7af21b87e8fc..ed0cf6548a0e 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/SingleFileUnsupportedAnalyzerTests.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/SingleFileUnsupportedAnalyzerTests.cs @@ -1,5 +1,4 @@ using System; -using System.IO; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Testing; using Xunit; @@ -12,8 +11,23 @@ public class SingleFileUnsupportedAnalyzerTests { static Task VerifySingleFileUnsupportedAnalyzer (string source, params DiagnosticResult[] expected) { - var singleFileUnsupportedAttribute = File.ReadAllText ("../../../../../test/ILLink.RoslynAnalyzer.Tests/Dependencies/SingleFileUnsupportedAttribute.cs"); - source = singleFileUnsupportedAttribute + Environment.NewLine + source; + // TODO: Remove this once we have the new attribute in the runtime. + source = @"namespace System.Diagnostics.CodeAnalysis +{ +#nullable enable + [AttributeUsage (AttributeTargets.Method | AttributeTargets.Constructor, Inherited = false, AllowMultiple = false)] + public sealed class SingleFileUnsupportedAttribute : Attribute + { + public SingleFileUnsupportedAttribute (string message) + { + Message = message; + } + + public string Message { get; } + + public string? Url { get; set; } + } +}" + Environment.NewLine + source; return VerifyCS.VerifyAnalyzerAsync (source, TestCaseUtils.UseMSBuildProperties (MSBuildPropertyOptionNames.PublishSingleFile), expected); @@ -38,5 +52,63 @@ void M2() return VerifySingleFileUnsupportedAnalyzer (TestSingleFileUnsupportedOnMethod, VerifyCS.Diagnostic ().WithSpan (27, 3, 27, 7).WithArguments ("C.M2()", "Message from attribute")); } + + [Fact] + public Task SingleFileUnsupportedWithMessageAndUrl () + { + var TestSingleFileUnsupportedWithMessageAndUrl = @" +class C +{ + [System.Diagnostics.CodeAnalysis.SingleFileUnsupported (""Message from attribute"", Url = ""https://helpurl"")] + void M1() + { + } + + void M2() + { + M1(); + } +}"; + return VerifySingleFileUnsupportedAnalyzer (TestSingleFileUnsupportedWithMessageAndUrl, + VerifyCS.Diagnostic ().WithSpan (27, 3, 27, 7).WithArguments ("C.M2()", "Message from attribute", "https://helpurl")); + } + + [Fact] + public Task NoDiagnosticIfMethodNotCalled () + { + var TestNoDiagnosticIfMethodNotCalled = @" +class C +{ + [System.Diagnostics.CodeAnalysis.SingleFileUnsupported ("""")] + void M() { } +}"; + return VerifySingleFileUnsupportedAnalyzer (TestNoDiagnosticIfMethodNotCalled); + } + + [Fact] + public Task NoDiagnosticIsProducedIfCallerIsAnnotated () + { + var TestNoDiagnosticIsProducedIfCallerIsAnnotated = @" +class C +{ + void M1() + { + M2(); + } + + [System.Diagnostics.CodeAnalysis.SingleFileUnsupported (""Warn from M2"")] + void M2() + { + M3(); + } + + [System.Diagnostics.CodeAnalysis.SingleFileUnsupported (""Warn from M3"")] + void M3() + { + } +}"; + return VerifySingleFileUnsupportedAnalyzer (TestNoDiagnosticIsProducedIfCallerIsAnnotated, + VerifyCS.Diagnostic ().WithSpan (22, 3, 22, 7).WithArguments ("C.M2()", "Warn from M2")); + } } } diff --git a/test/ILLink.RoslynAnalyzer.Tests/TestCaseUtils.cs b/test/ILLink.RoslynAnalyzer.Tests/TestCaseUtils.cs index c62c2d762004..458d0b34635b 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/TestCaseUtils.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/TestCaseUtils.cs @@ -11,7 +11,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Mono.Linker.Tests.Cases.Expectations.Assertions; using Xunit; namespace ILLink.RoslynAnalyzer.Tests From 7bb7c407e137781bbfdc54b9550822f441680bdb Mon Sep 17 00:00:00 2001 From: Mateo Torres Ruiz Date: Mon, 21 Dec 2020 17:32:39 -0800 Subject: [PATCH 3/9] Add summary --- src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs b/src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs index d271896c26a3..782bc877496a 100644 --- a/src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs +++ b/src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs @@ -9,6 +9,9 @@ namespace ILLink.RoslynAnalyzer { static class ISymbolExtensions { + /// + /// Returns true if symbol has an attribute with name . + /// internal static bool HasAttribute (this ISymbol symbol, string attributeName) { return symbol.GetAttributes ().Where (a => a?.AttributeClass?.Name == attributeName).Count () > 0; From 7af6e63ebcde32c48e418ad0b46fdb44451ff1a6 Mon Sep 17 00:00:00 2001 From: Mateo Torres Ruiz Date: Mon, 4 Jan 2021 12:40:28 -0800 Subject: [PATCH 4/9] Add helper method and refactor --- .../ISymbolExtensions.cs | 14 +++++ .../RequiresUnreferencedCodeAnalyzer.cs | 59 ++++++------------- .../SingleFileUnsupportedAnalyzer.cs | 12 ++-- 3 files changed, 35 insertions(+), 50 deletions(-) diff --git a/src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs b/src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs index 782bc877496a..76689441b06a 100644 --- a/src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs +++ b/src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs @@ -16,5 +16,19 @@ internal static bool HasAttribute (this ISymbol symbol, string attributeName) { return symbol.GetAttributes ().Where (a => a?.AttributeClass?.Name == attributeName).Count () > 0; } + + internal static bool TryGetAttributeWithMessageOnCtor (this ISymbol symbol, string qualifiedAttributeName, out AttributeData? attribute) + { + attribute = null; + if (symbol.GetAttributes ().FirstOrDefault (attr => attr.AttributeClass is { } attrClass && + attrClass.HasName (qualifiedAttributeName)) is var _attribute && + _attribute != null && _attribute.ConstructorArguments.Length >= 1 && + _attribute.ConstructorArguments[0] is { Type: { SpecialType: SpecialType.System_String } } ctorArg) { + attribute = _attribute; + return true; + } + + return false; + } } } diff --git a/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs b/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs index 0c8e72bad819..85fb1389faa9 100644 --- a/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs +++ b/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs @@ -15,8 +15,10 @@ namespace ILLink.RoslynAnalyzer public class RequiresUnreferencedCodeAnalyzer : DiagnosticAnalyzer { public const string DiagnosticId = "IL2026"; + const string RequiresUnreferencedCodeAttribute = nameof (RequiresUnreferencedCodeAttribute); + const string FullyQualifiedRequiresUnreferencedCodeAttribute = "System.Diagnostics.CodeAnalysis." + RequiresUnreferencedCodeAttribute; - private static readonly DiagnosticDescriptor s_rule = new DiagnosticDescriptor ( + private static readonly DiagnosticDescriptor RequiresUnreferencedCodeRule = new DiagnosticDescriptor ( DiagnosticId, new LocalizableResourceString (nameof (Resources.RequiresUnreferencedCodeAnalyzerTitle), Resources.ResourceManager, typeof (Resources)), @@ -26,7 +28,7 @@ public class RequiresUnreferencedCodeAnalyzer : DiagnosticAnalyzer DiagnosticSeverity.Warning, isEnabledByDefault: true); - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create (s_rule); + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create (RequiresUnreferencedCodeRule); public override void Initialize (AnalysisContext context) { @@ -46,47 +48,38 @@ public override void Initialize (AnalysisContext context) if (call.IsVirtual && call.TargetMethod.OverriddenMethod != null) return; - CheckMethodOrCtorCall (operationContext, call.TargetMethod, call.Syntax.GetLocation ()); + CheckMethodOrCtorCall (operationContext, call.TargetMethod); }, OperationKind.Invocation); context.RegisterOperationAction (operationContext => { var call = (IObjectCreationOperation) operationContext.Operation; - CheckMethodOrCtorCall (operationContext, call.Constructor, call.Syntax.GetLocation ()); + CheckMethodOrCtorCall (operationContext, call.Constructor); }, OperationKind.ObjectCreation); context.RegisterOperationAction (operationContext => { var propAccess = (IPropertyReferenceOperation) operationContext.Operation; var prop = propAccess.Property; var usageInfo = propAccess.GetValueUsageInfo (prop); - if (usageInfo.HasFlag (ValueUsageInfo.Read) && prop.GetMethod != null) { - CheckMethodOrCtorCall ( - operationContext, - prop.GetMethod, - propAccess.Syntax.GetLocation ()); - } - if (usageInfo.HasFlag (ValueUsageInfo.Write) && prop.SetMethod != null) { - CheckMethodOrCtorCall ( - operationContext, - prop.SetMethod, - propAccess.Syntax.GetLocation ()); - } + if (usageInfo.HasFlag (ValueUsageInfo.Read) && prop.GetMethod != null) + CheckMethodOrCtorCall (operationContext, prop.GetMethod); + + if (usageInfo.HasFlag (ValueUsageInfo.Write) && prop.SetMethod != null) + CheckMethodOrCtorCall (operationContext, prop.SetMethod); }, OperationKind.PropertyReference); - void CheckMethodOrCtorCall ( + static void CheckMethodOrCtorCall ( OperationAnalysisContext operationContext, - IMethodSymbol method, - Location location) + IMethodSymbol method) { - AttributeData? requiresUnreferencedCode; // If parent method contains RequiresUnreferencedCodeAttribute then we shouldn't report diagnostics for this method if (operationContext.ContainingSymbol is IMethodSymbol && - TryGetRequiresUnreferencedCodeAttribute (operationContext.ContainingSymbol.GetAttributes (), out requiresUnreferencedCode)) + operationContext.ContainingSymbol.TryGetAttributeWithMessageOnCtor (FullyQualifiedRequiresUnreferencedCodeAttribute, out AttributeData? requiresUnreferencedCode)) return; - if (TryGetRequiresUnreferencedCodeAttribute (method.GetAttributes (), out requiresUnreferencedCode)) { + if (method.TryGetAttributeWithMessageOnCtor (FullyQualifiedRequiresUnreferencedCodeAttribute, out requiresUnreferencedCode)) { operationContext.ReportDiagnostic (Diagnostic.Create ( - s_rule, - location, + RequiresUnreferencedCodeRule, + operationContext.Operation.Syntax.GetLocation (), method.OriginalDefinition.ToString (), (string) requiresUnreferencedCode!.ConstructorArguments[0].Value!, requiresUnreferencedCode!.NamedArguments.FirstOrDefault (na => na.Key == "Url").Value.Value?.ToString ())); @@ -94,23 +87,5 @@ void CheckMethodOrCtorCall ( } }); } - - /// - /// Returns a RequiresUnreferencedCodeAttribute if found - /// - static bool TryGetRequiresUnreferencedCodeAttribute (ImmutableArray attributes, out AttributeData? requiresUnreferencedCode) - { - requiresUnreferencedCode = null; - foreach (var attr in attributes) { - if (attr.AttributeClass is { } attrClass && - attrClass.HasName ("System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute") && - attr.ConstructorArguments.Length == 1 && - attr.ConstructorArguments[0] is { Type: { SpecialType: SpecialType.System_String } } ctorArg) { - requiresUnreferencedCode = attr; - return true; - } - } - return false; - } } } diff --git a/src/ILLink.RoslynAnalyzer/SingleFileUnsupportedAnalyzer.cs b/src/ILLink.RoslynAnalyzer/SingleFileUnsupportedAnalyzer.cs index 9235081cf3b8..d92a07901d59 100644 --- a/src/ILLink.RoslynAnalyzer/SingleFileUnsupportedAnalyzer.cs +++ b/src/ILLink.RoslynAnalyzer/SingleFileUnsupportedAnalyzer.cs @@ -16,6 +16,7 @@ public sealed class SingleFileUnsupportedAnalyzer : DiagnosticAnalyzer { public const string IL3002 = nameof (IL3002); const string SingleFileUnsupportedAttribute = nameof (SingleFileUnsupportedAttribute); + const string FullyQualifiedSingleFileUnsupportedAttribute = "System.Diagnostics.CodeAnalysis." + SingleFileUnsupportedAttribute; private static readonly DiagnosticDescriptor SingleFileUnsupportedRule = new DiagnosticDescriptor ( IL3002, @@ -54,18 +55,13 @@ public override void Initialize (AnalysisContext context) var targetMethod = methodInvocation.TargetMethod; var attributes = targetMethod.GetAttributes (); - if (attributes.FirstOrDefault (attr => attr.AttributeClass is { } attrClass && - attrClass.HasName ("System.Diagnostics.CodeAnalysis." + SingleFileUnsupportedAttribute)) is var singleFileUnsupportedAttr && - singleFileUnsupportedAttr != null) { - string? messageArgument = singleFileUnsupportedAttr.ConstructorArguments[0].Value as string; - string? urlArgument = singleFileUnsupportedAttr.NamedArguments.FirstOrDefault (na => na.Key == "Url").Value.Value?.ToString (); - + if (targetMethod.TryGetAttributeWithMessageOnCtor (FullyQualifiedSingleFileUnsupportedAttribute, out AttributeData? singleFileUnsupportedAttribute)) { operationContext.ReportDiagnostic (Diagnostic.Create ( SingleFileUnsupportedRule, methodInvocation.Syntax.GetLocation (), targetMethod.OriginalDefinition.ToString (), - messageArgument, - urlArgument)); + (string) singleFileUnsupportedAttribute?.ConstructorArguments[0].Value!, + singleFileUnsupportedAttribute?.NamedArguments.FirstOrDefault (na => na.Key == "Url").Value.Value?.ToString ())); } }, OperationKind.Invocation); }); From a872e2cd9123167601ec50f10b948ffd97451a6e Mon Sep 17 00:00:00 2001 From: Mateo Torres Ruiz Date: Mon, 4 Jan 2021 13:57:48 -0800 Subject: [PATCH 5/9] Fix whitespace formatting --- src/ILLink.RoslynAnalyzer/SingleFileUnsupportedAnalyzer.cs | 4 ++-- .../SingleFileUnsupportedAnalyzerTests.cs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ILLink.RoslynAnalyzer/SingleFileUnsupportedAnalyzer.cs b/src/ILLink.RoslynAnalyzer/SingleFileUnsupportedAnalyzer.cs index d92a07901d59..9d6232c04b0d 100644 --- a/src/ILLink.RoslynAnalyzer/SingleFileUnsupportedAnalyzer.cs +++ b/src/ILLink.RoslynAnalyzer/SingleFileUnsupportedAnalyzer.cs @@ -41,7 +41,7 @@ public override void Initialize (AnalysisContext context) var isSingleFilePublish = context.Options.GetMSBuildPropertyValue (MSBuildPropertyOptionNames.PublishSingleFile, compilation); if (!string.Equals (isSingleFilePublish?.Trim (), "true", StringComparison.OrdinalIgnoreCase)) return; - + var includesAllContent = context.Options.GetMSBuildPropertyValue (MSBuildPropertyOptionNames.IncludeAllContentForSelfExtract, compilation); if (string.Equals (includesAllContent?.Trim (), "true", StringComparison.OrdinalIgnoreCase)) return; @@ -50,7 +50,7 @@ public override void Initialize (AnalysisContext context) // Do not emit any diagnostic if caller is annotated with the attribute too. if (operationContext.ContainingSymbol.HasAttribute (SingleFileUnsupportedAttribute)) return; - + var methodInvocation = (IInvocationOperation) operationContext.Operation; var targetMethod = methodInvocation.TargetMethod; var attributes = targetMethod.GetAttributes (); diff --git a/test/ILLink.RoslynAnalyzer.Tests/SingleFileUnsupportedAnalyzerTests.cs b/test/ILLink.RoslynAnalyzer.Tests/SingleFileUnsupportedAnalyzerTests.cs index ed0cf6548a0e..c4d137d171ec 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/SingleFileUnsupportedAnalyzerTests.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/SingleFileUnsupportedAnalyzerTests.cs @@ -7,8 +7,8 @@ namespace ILLink.RoslynAnalyzer.Tests { - public class SingleFileUnsupportedAnalyzerTests - { + public class SingleFileUnsupportedAnalyzerTests + { static Task VerifySingleFileUnsupportedAnalyzer (string source, params DiagnosticResult[] expected) { // TODO: Remove this once we have the new attribute in the runtime. @@ -110,5 +110,5 @@ void M3() return VerifySingleFileUnsupportedAnalyzer (TestNoDiagnosticIsProducedIfCallerIsAnnotated, VerifyCS.Diagnostic ().WithSpan (22, 3, 22, 7).WithArguments ("C.M2()", "Warn from M2")); } - } + } } From 1c79e1d7f51ec68b15de1e2b3f54f759d10725df Mon Sep 17 00:00:00 2001 From: Mateo Torres Ruiz Date: Tue, 5 Jan 2021 10:04:39 -0800 Subject: [PATCH 6/9] PR feedback --- src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs | 4 ++-- src/ILLink.RoslynAnalyzer/SingleFileUnsupportedAnalyzer.cs | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs b/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs index 85fb1389faa9..a21461c5c23f 100644 --- a/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs +++ b/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs @@ -73,10 +73,10 @@ static void CheckMethodOrCtorCall ( { // If parent method contains RequiresUnreferencedCodeAttribute then we shouldn't report diagnostics for this method if (operationContext.ContainingSymbol is IMethodSymbol && - operationContext.ContainingSymbol.TryGetAttributeWithMessageOnCtor (FullyQualifiedRequiresUnreferencedCodeAttribute, out AttributeData? requiresUnreferencedCode)) + operationContext.ContainingSymbol.HasAttribute (RequiresUnreferencedCodeAttribute)) return; - if (method.TryGetAttributeWithMessageOnCtor (FullyQualifiedRequiresUnreferencedCodeAttribute, out requiresUnreferencedCode)) { + if (method.TryGetAttributeWithMessageOnCtor (FullyQualifiedRequiresUnreferencedCodeAttribute, out AttributeData? requiresUnreferencedCode)) { operationContext.ReportDiagnostic (Diagnostic.Create ( RequiresUnreferencedCodeRule, operationContext.Operation.Syntax.GetLocation (), diff --git a/src/ILLink.RoslynAnalyzer/SingleFileUnsupportedAnalyzer.cs b/src/ILLink.RoslynAnalyzer/SingleFileUnsupportedAnalyzer.cs index 9d6232c04b0d..e114d35fd310 100644 --- a/src/ILLink.RoslynAnalyzer/SingleFileUnsupportedAnalyzer.cs +++ b/src/ILLink.RoslynAnalyzer/SingleFileUnsupportedAnalyzer.cs @@ -53,7 +53,6 @@ public override void Initialize (AnalysisContext context) var methodInvocation = (IInvocationOperation) operationContext.Operation; var targetMethod = methodInvocation.TargetMethod; - var attributes = targetMethod.GetAttributes (); if (targetMethod.TryGetAttributeWithMessageOnCtor (FullyQualifiedSingleFileUnsupportedAttribute, out AttributeData? singleFileUnsupportedAttribute)) { operationContext.ReportDiagnostic (Diagnostic.Create ( From 103b4ad7e8b8068cbd2f46076b407fbed4d48c37 Mon Sep 17 00:00:00 2001 From: Mateo Torres Ruiz Date: Fri, 8 Jan 2021 11:15:23 -0800 Subject: [PATCH 7/9] Rename attribute Stop using Linq in HasName --- .../ISymbolExtensions.cs | 6 +++- ...er.cs => RequiresAssemblyFilesAnalyzer.cs} | 24 ++++++------- .../RequiresUnreferencedCodeAnalyzer.cs | 3 ++ src/ILLink.RoslynAnalyzer/Resources.resx | 4 +-- ... => RequiresAssemblyFilesAnalyzerTests.cs} | 34 +++++++++---------- 5 files changed, 39 insertions(+), 32 deletions(-) rename src/ILLink.RoslynAnalyzer/{SingleFileUnsupportedAnalyzer.cs => RequiresAssemblyFilesAnalyzer.cs} (70%) rename test/ILLink.RoslynAnalyzer.Tests/{SingleFileUnsupportedAnalyzerTests.cs => RequiresAssemblyFilesAnalyzerTests.cs} (63%) diff --git a/src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs b/src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs index 76689441b06a..97d515de4fc9 100644 --- a/src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs +++ b/src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs @@ -14,7 +14,11 @@ static class ISymbolExtensions /// internal static bool HasAttribute (this ISymbol symbol, string attributeName) { - return symbol.GetAttributes ().Where (a => a?.AttributeClass?.Name == attributeName).Count () > 0; + foreach (var attr in symbol.GetAttributes ()) + if (attr.AttributeClass?.Name == attributeName) + return true; + + return false; } internal static bool TryGetAttributeWithMessageOnCtor (this ISymbol symbol, string qualifiedAttributeName, out AttributeData? attribute) diff --git a/src/ILLink.RoslynAnalyzer/SingleFileUnsupportedAnalyzer.cs b/src/ILLink.RoslynAnalyzer/RequiresAssemblyFilesAnalyzer.cs similarity index 70% rename from src/ILLink.RoslynAnalyzer/SingleFileUnsupportedAnalyzer.cs rename to src/ILLink.RoslynAnalyzer/RequiresAssemblyFilesAnalyzer.cs index e114d35fd310..bf207a202d93 100644 --- a/src/ILLink.RoslynAnalyzer/SingleFileUnsupportedAnalyzer.cs +++ b/src/ILLink.RoslynAnalyzer/RequiresAssemblyFilesAnalyzer.cs @@ -12,23 +12,23 @@ namespace ILLink.RoslynAnalyzer { [DiagnosticAnalyzer (LanguageNames.CSharp)] - public sealed class SingleFileUnsupportedAnalyzer : DiagnosticAnalyzer + public sealed class RequiresAssemblyFilesAnalyzer : DiagnosticAnalyzer { public const string IL3002 = nameof (IL3002); - const string SingleFileUnsupportedAttribute = nameof (SingleFileUnsupportedAttribute); - const string FullyQualifiedSingleFileUnsupportedAttribute = "System.Diagnostics.CodeAnalysis." + SingleFileUnsupportedAttribute; + const string RequiresAssemblyFilesAttribute = nameof (RequiresAssemblyFilesAttribute); + const string FullyQualifiedRequiresAssemblyFilesAttribute = "System.Diagnostics.CodeAnalysis." + RequiresAssemblyFilesAttribute; - private static readonly DiagnosticDescriptor SingleFileUnsupportedRule = new DiagnosticDescriptor ( + private static readonly DiagnosticDescriptor RequiresAssemblyFilesRule = new DiagnosticDescriptor ( IL3002, - new LocalizableResourceString (nameof (Resources.SingleFileUnsupportedTitle), + new LocalizableResourceString (nameof (Resources.RequiresAssemblyFilesTitle), Resources.ResourceManager, typeof (Resources)), - new LocalizableResourceString (nameof (Resources.SingleFileUnsupportedMessage), + new LocalizableResourceString (nameof (Resources.RequiresAssemblyFilesMessage), Resources.ResourceManager, typeof (Resources)), DiagnosticCategory.SingleFile, DiagnosticSeverity.Warning, isEnabledByDefault: true); - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create (SingleFileUnsupportedRule); + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create (RequiresAssemblyFilesRule); public override void Initialize (AnalysisContext context) { @@ -48,19 +48,19 @@ public override void Initialize (AnalysisContext context) context.RegisterOperationAction (operationContext => { // Do not emit any diagnostic if caller is annotated with the attribute too. - if (operationContext.ContainingSymbol.HasAttribute (SingleFileUnsupportedAttribute)) + if (operationContext.ContainingSymbol.HasAttribute (RequiresAssemblyFilesAttribute)) return; var methodInvocation = (IInvocationOperation) operationContext.Operation; var targetMethod = methodInvocation.TargetMethod; - if (targetMethod.TryGetAttributeWithMessageOnCtor (FullyQualifiedSingleFileUnsupportedAttribute, out AttributeData? singleFileUnsupportedAttribute)) { + if (targetMethod.TryGetAttributeWithMessageOnCtor (FullyQualifiedRequiresAssemblyFilesAttribute, out AttributeData? requiresAssemblyFilesAttribute)) { operationContext.ReportDiagnostic (Diagnostic.Create ( - SingleFileUnsupportedRule, + RequiresAssemblyFilesRule, methodInvocation.Syntax.GetLocation (), targetMethod.OriginalDefinition.ToString (), - (string) singleFileUnsupportedAttribute?.ConstructorArguments[0].Value!, - singleFileUnsupportedAttribute?.NamedArguments.FirstOrDefault (na => na.Key == "Url").Value.Value?.ToString ())); + (string) requiresAssemblyFilesAttribute?.ConstructorArguments[0].Value!, + requiresAssemblyFilesAttribute?.NamedArguments.FirstOrDefault (na => na.Key == "Url").Value.Value?.ToString ())); } }, OperationKind.Invocation); }); diff --git a/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs b/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs index a21461c5c23f..8da4e1baa453 100644 --- a/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs +++ b/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs @@ -76,6 +76,9 @@ static void CheckMethodOrCtorCall ( operationContext.ContainingSymbol.HasAttribute (RequiresUnreferencedCodeAttribute)) return; + if (!method.HasAttribute (RequiresUnreferencedCodeAttribute)) + return; + if (method.TryGetAttributeWithMessageOnCtor (FullyQualifiedRequiresUnreferencedCodeAttribute, out AttributeData? requiresUnreferencedCode)) { operationContext.ReportDiagnostic (Diagnostic.Create ( RequiresUnreferencedCodeRule, diff --git a/src/ILLink.RoslynAnalyzer/Resources.resx b/src/ILLink.RoslynAnalyzer/Resources.resx index f10bc1101ae1..86ac84a05afd 100644 --- a/src/ILLink.RoslynAnalyzer/Resources.resx +++ b/src/ILLink.RoslynAnalyzer/Resources.resx @@ -135,10 +135,10 @@ '{0}' will throw for assemblies embedded in a single-file app - + - + \ No newline at end of file diff --git a/test/ILLink.RoslynAnalyzer.Tests/SingleFileUnsupportedAnalyzerTests.cs b/test/ILLink.RoslynAnalyzer.Tests/RequiresAssemblyFilesAnalyzerTests.cs similarity index 63% rename from test/ILLink.RoslynAnalyzer.Tests/SingleFileUnsupportedAnalyzerTests.cs rename to test/ILLink.RoslynAnalyzer.Tests/RequiresAssemblyFilesAnalyzerTests.cs index c4d137d171ec..c4c1fc9fad6b 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/SingleFileUnsupportedAnalyzerTests.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/RequiresAssemblyFilesAnalyzerTests.cs @@ -3,22 +3,22 @@ using Microsoft.CodeAnalysis.Testing; using Xunit; using VerifyCS = ILLink.RoslynAnalyzer.Tests.CSharpAnalyzerVerifier< - ILLink.RoslynAnalyzer.SingleFileUnsupportedAnalyzer>; + ILLink.RoslynAnalyzer.RequiresAssemblyFilesAnalyzer>; namespace ILLink.RoslynAnalyzer.Tests { - public class SingleFileUnsupportedAnalyzerTests + public class RequiresAssemblyFilesAnalyzerTests { - static Task VerifySingleFileUnsupportedAnalyzer (string source, params DiagnosticResult[] expected) + static Task VerifyRequiresAssemblyFilesAnalyzer (string source, params DiagnosticResult[] expected) { // TODO: Remove this once we have the new attribute in the runtime. source = @"namespace System.Diagnostics.CodeAnalysis { #nullable enable [AttributeUsage (AttributeTargets.Method | AttributeTargets.Constructor, Inherited = false, AllowMultiple = false)] - public sealed class SingleFileUnsupportedAttribute : Attribute + public sealed class RequiresAssemblyFilesAttribute : Attribute { - public SingleFileUnsupportedAttribute (string message) + public RequiresAssemblyFilesAttribute (string message) { Message = message; } @@ -36,10 +36,10 @@ public SingleFileUnsupportedAttribute (string message) [Fact] public Task SimpleDiagnostic () { - var TestSingleFileUnsupportedOnMethod = @" + var TestRequiresAssemblyFilesOnMethod = @" class C { - [System.Diagnostics.CodeAnalysis.SingleFileUnsupported (""Message from attribute"")] + [System.Diagnostics.CodeAnalysis.RequiresAssemblyFiles (""Message from attribute"")] void M1() { } @@ -49,17 +49,17 @@ void M2() M1(); } }"; - return VerifySingleFileUnsupportedAnalyzer (TestSingleFileUnsupportedOnMethod, + return VerifyRequiresAssemblyFilesAnalyzer (TestRequiresAssemblyFilesOnMethod, VerifyCS.Diagnostic ().WithSpan (27, 3, 27, 7).WithArguments ("C.M2()", "Message from attribute")); } [Fact] - public Task SingleFileUnsupportedWithMessageAndUrl () + public Task RequiresAssemblyFilesWithMessageAndUrl () { - var TestSingleFileUnsupportedWithMessageAndUrl = @" + var TestRequiresAssemblyFilesWithMessageAndUrl = @" class C { - [System.Diagnostics.CodeAnalysis.SingleFileUnsupported (""Message from attribute"", Url = ""https://helpurl"")] + [System.Diagnostics.CodeAnalysis.RequiresAssemblyFiles (""Message from attribute"", Url = ""https://helpurl"")] void M1() { } @@ -69,7 +69,7 @@ void M2() M1(); } }"; - return VerifySingleFileUnsupportedAnalyzer (TestSingleFileUnsupportedWithMessageAndUrl, + return VerifyRequiresAssemblyFilesAnalyzer (TestRequiresAssemblyFilesWithMessageAndUrl, VerifyCS.Diagnostic ().WithSpan (27, 3, 27, 7).WithArguments ("C.M2()", "Message from attribute", "https://helpurl")); } @@ -79,10 +79,10 @@ public Task NoDiagnosticIfMethodNotCalled () var TestNoDiagnosticIfMethodNotCalled = @" class C { - [System.Diagnostics.CodeAnalysis.SingleFileUnsupported ("""")] + [System.Diagnostics.CodeAnalysis.RequiresAssemblyFiles ("""")] void M() { } }"; - return VerifySingleFileUnsupportedAnalyzer (TestNoDiagnosticIfMethodNotCalled); + return VerifyRequiresAssemblyFilesAnalyzer (TestNoDiagnosticIfMethodNotCalled); } [Fact] @@ -96,18 +96,18 @@ void M1() M2(); } - [System.Diagnostics.CodeAnalysis.SingleFileUnsupported (""Warn from M2"")] + [System.Diagnostics.CodeAnalysis.RequiresAssemblyFiles (""Warn from M2"")] void M2() { M3(); } - [System.Diagnostics.CodeAnalysis.SingleFileUnsupported (""Warn from M3"")] + [System.Diagnostics.CodeAnalysis.RequiresAssemblyFiles (""Warn from M3"")] void M3() { } }"; - return VerifySingleFileUnsupportedAnalyzer (TestNoDiagnosticIsProducedIfCallerIsAnnotated, + return VerifyRequiresAssemblyFilesAnalyzer (TestNoDiagnosticIsProducedIfCallerIsAnnotated, VerifyCS.Diagnostic ().WithSpan (22, 3, 22, 7).WithArguments ("C.M2()", "Warn from M2")); } } From e35953731659eab6384ebdae98151eb46c29db64 Mon Sep 17 00:00:00 2001 From: Mateo Torres Ruiz Date: Mon, 11 Jan 2021 15:23:06 -0800 Subject: [PATCH 8/9] Update tests and CA --- .../ISymbolExtensions.cs | 13 ++++ .../RequiresAssemblyFilesAnalyzer.cs | 48 +++++++++--- .../RequiresAssemblyFilesAnalyzerTests.cs | 74 ++++++++++++++----- .../RequiresUnreferencedCodeAnalyzerTests.cs | 4 - 4 files changed, 107 insertions(+), 32 deletions(-) diff --git a/src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs b/src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs index 97d515de4fc9..5930bc5ba74d 100644 --- a/src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs +++ b/src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs @@ -21,6 +21,19 @@ internal static bool HasAttribute (this ISymbol symbol, string attributeName) return false; } + internal static bool TryGetRequiresAssemblyFileAttribute (this ISymbol symbol, out AttributeData? attribute) + { + attribute = null; + if (symbol.GetAttributes ().FirstOrDefault (attr => attr.AttributeClass is { } attrClass && + attrClass.HasName (RequiresAssemblyFilesAnalyzer.FullyQualifiedRequiresAssemblyFilesAttribute)) is var _attribute && + _attribute != null && _attribute.ConstructorArguments.Length == 0) { + attribute = _attribute; + return true; + } + + return false; + } + internal static bool TryGetAttributeWithMessageOnCtor (this ISymbol symbol, string qualifiedAttributeName, out AttributeData? attribute) { attribute = null; diff --git a/src/ILLink.RoslynAnalyzer/RequiresAssemblyFilesAnalyzer.cs b/src/ILLink.RoslynAnalyzer/RequiresAssemblyFilesAnalyzer.cs index bf207a202d93..a102f03b12ed 100644 --- a/src/ILLink.RoslynAnalyzer/RequiresAssemblyFilesAnalyzer.cs +++ b/src/ILLink.RoslynAnalyzer/RequiresAssemblyFilesAnalyzer.cs @@ -15,8 +15,8 @@ namespace ILLink.RoslynAnalyzer public sealed class RequiresAssemblyFilesAnalyzer : DiagnosticAnalyzer { public const string IL3002 = nameof (IL3002); - const string RequiresAssemblyFilesAttribute = nameof (RequiresAssemblyFilesAttribute); - const string FullyQualifiedRequiresAssemblyFilesAttribute = "System.Diagnostics.CodeAnalysis." + RequiresAssemblyFilesAttribute; + internal const string RequiresAssemblyFilesAttribute = nameof (RequiresAssemblyFilesAttribute); + internal const string FullyQualifiedRequiresAssemblyFilesAttribute = "System.Diagnostics.CodeAnalysis." + RequiresAssemblyFilesAttribute; private static readonly DiagnosticDescriptor RequiresAssemblyFilesRule = new DiagnosticDescriptor ( IL3002, @@ -47,22 +47,50 @@ public override void Initialize (AnalysisContext context) return; context.RegisterOperationAction (operationContext => { + var methodInvocation = (IInvocationOperation) operationContext.Operation; + CheckCalledMember (operationContext, methodInvocation.TargetMethod); + }, OperationKind.Invocation); + + context.RegisterOperationAction (operationContext => { + var objectCreation = (IObjectCreationOperation) operationContext.Operation; + CheckCalledMember (operationContext, objectCreation.Constructor); + }, OperationKind.ObjectCreation); + + context.RegisterOperationAction (operationContext => { + var propAccess = (IPropertyReferenceOperation) operationContext.Operation; + var prop = propAccess.Property; + var usageInfo = propAccess.GetValueUsageInfo (prop); + if (usageInfo.HasFlag (ValueUsageInfo.Read) && prop.GetMethod != null) + CheckCalledMember (operationContext, prop.GetMethod); + + if (usageInfo.HasFlag (ValueUsageInfo.Write) && prop.SetMethod != null) + CheckCalledMember (operationContext, prop.SetMethod); + + CheckCalledMember (operationContext, prop); + }, OperationKind.PropertyReference); + + context.RegisterOperationAction (operationContext => { + var eventRef = (IEventReferenceOperation) operationContext.Operation; + CheckCalledMember (operationContext, eventRef.Member); + }, OperationKind.EventReference); + + static void CheckCalledMember ( + OperationAnalysisContext operationContext, + ISymbol member) + { // Do not emit any diagnostic if caller is annotated with the attribute too. if (operationContext.ContainingSymbol.HasAttribute (RequiresAssemblyFilesAttribute)) return; - var methodInvocation = (IInvocationOperation) operationContext.Operation; - var targetMethod = methodInvocation.TargetMethod; - - if (targetMethod.TryGetAttributeWithMessageOnCtor (FullyQualifiedRequiresAssemblyFilesAttribute, out AttributeData? requiresAssemblyFilesAttribute)) { + if (member.TryGetRequiresAssemblyFileAttribute (out AttributeData? requiresAssemblyFilesAttribute)) { operationContext.ReportDiagnostic (Diagnostic.Create ( RequiresAssemblyFilesRule, - methodInvocation.Syntax.GetLocation (), - targetMethod.OriginalDefinition.ToString (), - (string) requiresAssemblyFilesAttribute?.ConstructorArguments[0].Value!, + operationContext.Operation.Syntax.GetLocation (), + member.OriginalDefinition.ToString (), + requiresAssemblyFilesAttribute?.NamedArguments.FirstOrDefault (na => na.Key == "Message").Value.Value?.ToString (), requiresAssemblyFilesAttribute?.NamedArguments.FirstOrDefault (na => na.Key == "Url").Value.Value?.ToString ())); } - }, OperationKind.Invocation); + } }); } } diff --git a/test/ILLink.RoslynAnalyzer.Tests/RequiresAssemblyFilesAnalyzerTests.cs b/test/ILLink.RoslynAnalyzer.Tests/RequiresAssemblyFilesAnalyzerTests.cs index c4c1fc9fad6b..466f81db88be 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/RequiresAssemblyFilesAnalyzerTests.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/RequiresAssemblyFilesAnalyzerTests.cs @@ -15,17 +15,17 @@ static Task VerifyRequiresAssemblyFilesAnalyzer (string source, params Diagnosti source = @"namespace System.Diagnostics.CodeAnalysis { #nullable enable - [AttributeUsage (AttributeTargets.Method | AttributeTargets.Constructor, Inherited = false, AllowMultiple = false)] - public sealed class RequiresAssemblyFilesAttribute : Attribute - { - public RequiresAssemblyFilesAttribute (string message) - { - Message = message; - } - - public string Message { get; } - - public string? Url { get; set; } + [AttributeUsage(AttributeTargets.Constructor | + AttributeTargets.Event | + AttributeTargets.Method | + AttributeTargets.Property, + Inherited = false, + AllowMultiple = false)] + public sealed class RequiresAssemblyFilesAttribute : Attribute + { + public RequiresAssemblyFilesAttribute() { } + public string? Message { get; set; } + public string? Url { get; set; } } }" + Environment.NewLine + source; return VerifyCS.VerifyAnalyzerAsync (source, @@ -34,12 +34,30 @@ public RequiresAssemblyFilesAttribute (string message) } [Fact] - public Task SimpleDiagnostic () + public Task SimpleDiagnosticOnEvent () + { + var TestRequiresAssemblyFieldsOnEvent = @" +class C +{ + [System.Diagnostics.CodeAnalysis.RequiresAssemblyFiles] + event System.EventHandler? E; + + void M() + { + var handler = E; + } +}"; + return VerifyRequiresAssemblyFilesAnalyzer (TestRequiresAssemblyFieldsOnEvent, + VerifyCS.Diagnostic ().WithSpan (25, 17, 25, 18).WithArguments ("C.E")); + } + + [Fact] + public Task SimpleDiagnosticOnMethod () { var TestRequiresAssemblyFilesOnMethod = @" class C { - [System.Diagnostics.CodeAnalysis.RequiresAssemblyFiles (""Message from attribute"")] + [System.Diagnostics.CodeAnalysis.RequiresAssemblyFiles] void M1() { } @@ -50,7 +68,27 @@ void M2() } }"; return VerifyRequiresAssemblyFilesAnalyzer (TestRequiresAssemblyFilesOnMethod, - VerifyCS.Diagnostic ().WithSpan (27, 3, 27, 7).WithArguments ("C.M2()", "Message from attribute")); + VerifyCS.Diagnostic ().WithSpan (27, 3, 27, 7).WithArguments ("C.M2()")); + } + + [Fact] + public Task SimpleDiagnosticOnProperty () + { + var TestRequiresAssemblyFilesOnProperty = @" +class C +{ + [System.Diagnostics.CodeAnalysis.RequiresAssemblyFiles] + bool P { get; set; } + + void M() + { + P = false; + bool b = P; + } +}"; + return VerifyRequiresAssemblyFilesAnalyzer (TestRequiresAssemblyFilesOnProperty, + VerifyCS.Diagnostic ().WithSpan (25, 3, 25, 4).WithArguments ("C.P"), + VerifyCS.Diagnostic ().WithSpan (26, 12, 26, 13).WithArguments ("C.P")); } [Fact] @@ -59,7 +97,7 @@ public Task RequiresAssemblyFilesWithMessageAndUrl () var TestRequiresAssemblyFilesWithMessageAndUrl = @" class C { - [System.Diagnostics.CodeAnalysis.RequiresAssemblyFiles (""Message from attribute"", Url = ""https://helpurl"")] + [System.Diagnostics.CodeAnalysis.RequiresAssemblyFiles (Message = ""Message from attribute"", Url = ""https://helpurl"")] void M1() { } @@ -79,7 +117,7 @@ public Task NoDiagnosticIfMethodNotCalled () var TestNoDiagnosticIfMethodNotCalled = @" class C { - [System.Diagnostics.CodeAnalysis.RequiresAssemblyFiles ("""")] + [System.Diagnostics.CodeAnalysis.RequiresAssemblyFiles] void M() { } }"; return VerifyRequiresAssemblyFilesAnalyzer (TestNoDiagnosticIfMethodNotCalled); @@ -96,13 +134,13 @@ void M1() M2(); } - [System.Diagnostics.CodeAnalysis.RequiresAssemblyFiles (""Warn from M2"")] + [System.Diagnostics.CodeAnalysis.RequiresAssemblyFiles (Message = ""Warn from M2"")] void M2() { M3(); } - [System.Diagnostics.CodeAnalysis.RequiresAssemblyFiles (""Warn from M3"")] + [System.Diagnostics.CodeAnalysis.RequiresAssemblyFiles (Message = ""Warn from M3"")] void M3() { } diff --git a/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs b/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs index 81899e8906d0..25050d50721a 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs @@ -32,7 +32,6 @@ class C int M2() => M1(); }"; return VerifyRequiresUnreferencedCodeAnalyzer (TestRequiresWithMessageOnlyOnMethod, - // (8,17): warning IL2026: Calling 'System.Int32 C::M1()' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. message. VerifyCS.Diagnostic ().WithSpan (8, 17, 8, 21).WithArguments ("C.M1()", "message", "")); } @@ -54,7 +53,6 @@ static void RequiresWithMessageAndUrl () } }"; return VerifyRequiresUnreferencedCodeAnalyzer (MessageAndUrlOnMethod, - // (8,3): warning IL2026: Calling 'C.RequiresWithMessageAndUrl()' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. Message for --RequiresWithMessageAndUrl--. VerifyCS.Diagnostic ().WithSpan (8, 3, 8, 31).WithArguments ("C.RequiresWithMessageAndUrl()", "Message for --RequiresWithMessageAndUrl--", "https://helpurl") ); } @@ -78,7 +76,6 @@ static int PropertyRequires { } }"; return VerifyRequiresUnreferencedCodeAnalyzer (PropertyRequires, - // (8,7): warning IL2026: Calling 'C.PropertyRequires.get' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. Message for --getter PropertyRequires--. VerifyCS.Diagnostic ().WithSpan (8, 7, 8, 23).WithArguments ("C.PropertyRequires.get", "Message for --getter PropertyRequires--", "") ); } @@ -102,7 +99,6 @@ static int PropertyRequires { } }"; return VerifyRequiresUnreferencedCodeAnalyzer (PropertyRequires, - // (8,3): warning IL2026: Calling 'C.PropertyRequires.set' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. Message for --setter PropertyRequires--. VerifyCS.Diagnostic ().WithSpan (8, 3, 8, 19).WithArguments ("C.PropertyRequires.set", "Message for --setter PropertyRequires--", "") ); } From fffafb214b5b03eb5a9a96fad4d60cabbaeff977 Mon Sep 17 00:00:00 2001 From: Mateo Torres Ruiz Date: Mon, 11 Jan 2021 17:43:13 -0800 Subject: [PATCH 9/9] Don't use Linq in ISymbolExtensions --- .../ISymbolExtensions.cs | 26 ++++++++++--------- .../RequiresAssemblyFilesAnalyzer.cs | 6 ++--- .../RequiresUnreferencedCodeAnalyzer.cs | 6 ++--- .../SingleFileAnalyzer.cs | 10 +++---- 4 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs b/src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs index 5930bc5ba74d..f458cc1fecb0 100644 --- a/src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs +++ b/src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Linq; using Microsoft.CodeAnalysis; namespace ILLink.RoslynAnalyzer @@ -24,11 +23,13 @@ internal static bool HasAttribute (this ISymbol symbol, string attributeName) internal static bool TryGetRequiresAssemblyFileAttribute (this ISymbol symbol, out AttributeData? attribute) { attribute = null; - if (symbol.GetAttributes ().FirstOrDefault (attr => attr.AttributeClass is { } attrClass && - attrClass.HasName (RequiresAssemblyFilesAnalyzer.FullyQualifiedRequiresAssemblyFilesAttribute)) is var _attribute && - _attribute != null && _attribute.ConstructorArguments.Length == 0) { - attribute = _attribute; - return true; + foreach (var _attribute in symbol.GetAttributes ()) { + if (_attribute.AttributeClass is var attrClass && attrClass != null && + attrClass.HasName (RequiresAssemblyFilesAnalyzer.FullyQualifiedRequiresAssemblyFilesAttribute) && + _attribute.ConstructorArguments.Length == 0) { + attribute = _attribute; + return true; + } } return false; @@ -37,12 +38,13 @@ internal static bool TryGetRequiresAssemblyFileAttribute (this ISymbol symbol, o internal static bool TryGetAttributeWithMessageOnCtor (this ISymbol symbol, string qualifiedAttributeName, out AttributeData? attribute) { attribute = null; - if (symbol.GetAttributes ().FirstOrDefault (attr => attr.AttributeClass is { } attrClass && - attrClass.HasName (qualifiedAttributeName)) is var _attribute && - _attribute != null && _attribute.ConstructorArguments.Length >= 1 && - _attribute.ConstructorArguments[0] is { Type: { SpecialType: SpecialType.System_String } } ctorArg) { - attribute = _attribute; - return true; + foreach (var _attribute in symbol.GetAttributes ()) { + if (_attribute.AttributeClass is var attrClass && attrClass != null && + attrClass.HasName (qualifiedAttributeName) && _attribute.ConstructorArguments.Length >= 1 && + _attribute.ConstructorArguments[0] is { Type: { SpecialType: SpecialType.System_String } } ctorArg) { + attribute = _attribute; + return true; + } } return false; diff --git a/src/ILLink.RoslynAnalyzer/RequiresAssemblyFilesAnalyzer.cs b/src/ILLink.RoslynAnalyzer/RequiresAssemblyFilesAnalyzer.cs index a102f03b12ed..65006691c3c9 100644 --- a/src/ILLink.RoslynAnalyzer/RequiresAssemblyFilesAnalyzer.cs +++ b/src/ILLink.RoslynAnalyzer/RequiresAssemblyFilesAnalyzer.cs @@ -18,7 +18,7 @@ public sealed class RequiresAssemblyFilesAnalyzer : DiagnosticAnalyzer internal const string RequiresAssemblyFilesAttribute = nameof (RequiresAssemblyFilesAttribute); internal const string FullyQualifiedRequiresAssemblyFilesAttribute = "System.Diagnostics.CodeAnalysis." + RequiresAssemblyFilesAttribute; - private static readonly DiagnosticDescriptor RequiresAssemblyFilesRule = new DiagnosticDescriptor ( + static readonly DiagnosticDescriptor s_requiresAssemblyFilesRule = new DiagnosticDescriptor ( IL3002, new LocalizableResourceString (nameof (Resources.RequiresAssemblyFilesTitle), Resources.ResourceManager, typeof (Resources)), @@ -28,7 +28,7 @@ public sealed class RequiresAssemblyFilesAnalyzer : DiagnosticAnalyzer DiagnosticSeverity.Warning, isEnabledByDefault: true); - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create (RequiresAssemblyFilesRule); + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create (s_requiresAssemblyFilesRule); public override void Initialize (AnalysisContext context) { @@ -84,7 +84,7 @@ static void CheckCalledMember ( if (member.TryGetRequiresAssemblyFileAttribute (out AttributeData? requiresAssemblyFilesAttribute)) { operationContext.ReportDiagnostic (Diagnostic.Create ( - RequiresAssemblyFilesRule, + s_requiresAssemblyFilesRule, operationContext.Operation.Syntax.GetLocation (), member.OriginalDefinition.ToString (), requiresAssemblyFilesAttribute?.NamedArguments.FirstOrDefault (na => na.Key == "Message").Value.Value?.ToString (), diff --git a/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs b/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs index 8da4e1baa453..50744cbb7884 100644 --- a/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs +++ b/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs @@ -18,7 +18,7 @@ public class RequiresUnreferencedCodeAnalyzer : DiagnosticAnalyzer const string RequiresUnreferencedCodeAttribute = nameof (RequiresUnreferencedCodeAttribute); const string FullyQualifiedRequiresUnreferencedCodeAttribute = "System.Diagnostics.CodeAnalysis." + RequiresUnreferencedCodeAttribute; - private static readonly DiagnosticDescriptor RequiresUnreferencedCodeRule = new DiagnosticDescriptor ( + static readonly DiagnosticDescriptor s_requiresUnreferencedCodeRule = new DiagnosticDescriptor ( DiagnosticId, new LocalizableResourceString (nameof (Resources.RequiresUnreferencedCodeAnalyzerTitle), Resources.ResourceManager, typeof (Resources)), @@ -28,7 +28,7 @@ public class RequiresUnreferencedCodeAnalyzer : DiagnosticAnalyzer DiagnosticSeverity.Warning, isEnabledByDefault: true); - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create (RequiresUnreferencedCodeRule); + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create (s_requiresUnreferencedCodeRule); public override void Initialize (AnalysisContext context) { @@ -81,7 +81,7 @@ static void CheckMethodOrCtorCall ( if (method.TryGetAttributeWithMessageOnCtor (FullyQualifiedRequiresUnreferencedCodeAttribute, out AttributeData? requiresUnreferencedCode)) { operationContext.ReportDiagnostic (Diagnostic.Create ( - RequiresUnreferencedCodeRule, + s_requiresUnreferencedCodeRule, operationContext.Operation.Syntax.GetLocation (), method.OriginalDefinition.ToString (), (string) requiresUnreferencedCode!.ConstructorArguments[0].Value!, diff --git a/src/ILLink.RoslynAnalyzer/SingleFileAnalyzer.cs b/src/ILLink.RoslynAnalyzer/SingleFileAnalyzer.cs index 536cd82f266c..15f17caf3cac 100644 --- a/src/ILLink.RoslynAnalyzer/SingleFileAnalyzer.cs +++ b/src/ILLink.RoslynAnalyzer/SingleFileAnalyzer.cs @@ -20,7 +20,7 @@ public sealed class AvoidAssemblyLocationInSingleFile : DiagnosticAnalyzer public const string IL3000 = nameof (IL3000); public const string IL3001 = nameof (IL3001); - private static readonly DiagnosticDescriptor LocationRule = new DiagnosticDescriptor ( + static readonly DiagnosticDescriptor s_locationRule = new DiagnosticDescriptor ( IL3000, new LocalizableResourceString (nameof (Resources.AvoidAssemblyLocationInSingleFileTitle), Resources.ResourceManager, typeof (Resources)), @@ -30,7 +30,7 @@ public sealed class AvoidAssemblyLocationInSingleFile : DiagnosticAnalyzer DiagnosticSeverity.Warning, isEnabledByDefault: true); - private static readonly DiagnosticDescriptor GetFilesRule = new DiagnosticDescriptor ( + static readonly DiagnosticDescriptor s_getFilesRule = new DiagnosticDescriptor ( IL3001, new LocalizableResourceString (nameof (Resources.AvoidAssemblyGetFilesInSingleFileTitle), Resources.ResourceManager, typeof (Resources)), @@ -40,7 +40,7 @@ public sealed class AvoidAssemblyLocationInSingleFile : DiagnosticAnalyzer DiagnosticSeverity.Warning, isEnabledByDefault: true); - public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create (LocationRule, GetFilesRule); + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create (s_locationRule, s_getFilesRule); public override void Initialize (AnalysisContext context) { @@ -88,7 +88,7 @@ public override void Initialize (AnalysisContext context) return; } - operationContext.ReportDiagnostic (Diagnostic.Create (LocationRule, access.Syntax.GetLocation (), property)); + operationContext.ReportDiagnostic (Diagnostic.Create (s_locationRule, access.Syntax.GetLocation (), property)); }, OperationKind.PropertyReference); context.RegisterOperationAction (operationContext => { @@ -98,7 +98,7 @@ public override void Initialize (AnalysisContext context) return; } - operationContext.ReportDiagnostic (Diagnostic.Create (GetFilesRule, invocation.Syntax.GetLocation (), targetMethod)); + operationContext.ReportDiagnostic (Diagnostic.Create (s_getFilesRule, invocation.Syntax.GetLocation (), targetMethod)); }, OperationKind.Invocation); return;