diff options
author | Jackson Schuster <jschuster@microsoft.com> | 2021-12-10 00:23:39 +0300 |
---|---|---|
committer | Jackson Schuster <jschuster@microsoft.com> | 2021-12-10 01:44:46 +0300 |
commit | 012f3c0773c498c8601eb9af806aba1267b40342 (patch) | |
tree | 46965de7068594e8a0042b1d2a3805918d6b1d28 | |
parent | 02d5a8c2fa2ead9bab2d14f00749010a37316802 (diff) |
Use the ISymbol extensions for Requires analyzers
3 files changed, 25 insertions, 430 deletions
diff --git a/src/ILLink.RoslynAnalyzer/RequiresISymbolExtensions.cs b/src/ILLink.RoslynAnalyzer/RequiresISymbolExtensions.cs index efe00031b..e08359149 100644 --- a/src/ILLink.RoslynAnalyzer/RequiresISymbolExtensions.cs +++ b/src/ILLink.RoslynAnalyzer/RequiresISymbolExtensions.cs @@ -6,14 +6,35 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Text; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; namespace ILLink.RoslynAnalyzer { public static class RequiresISymbolExtensions { // TODO: Consider sharing with linker IsMethodInRequiresUnreferencedCodeScope method + /// <summary> + /// True if the source of a call is considered to be annotated with the Requires... attribute + /// </summary> public static bool IsInRequiresScope (this ISymbol member, string requiresAttribute) { + return member.IsInRequiresScope (requiresAttribute, true); + } + + /// <summary> + /// True if member of a call is considered to be annotated with the Requires... attribute. + /// Doesn't check the associated symbol for overrides and virtual methods because the analyzer should warn on mismatched between the property AND the accessors + /// </summary> + /// <param name="containingSymbol"> + /// Symbol that is either an overriding member or an overriden/virtual member + /// </param> + public static bool IsOverrideInRequiresScope(this ISymbol member, string requiresAttribute) + { + return member.IsInRequiresScope(requiresAttribute, false); + } + + private static bool IsInRequiresScope (this ISymbol member, string requiresAttribute, bool checkAssociatedSymbol) + { if (member is ISymbol containingSymbol) { if (containingSymbol.HasAttribute (requiresAttribute) || (containingSymbol is not ITypeSymbol && @@ -21,7 +42,8 @@ namespace ILLink.RoslynAnalyzer return true; } } - if (member is IMethodSymbol { AssociatedSymbol: { } associated } && associated.HasAttribute (requiresAttribute)) + // Only check associated symbol if not override or virtual method + if (checkAssociatedSymbol && member is IMethodSymbol { AssociatedSymbol: { } associated } && associated.HasAttribute (requiresAttribute)) return true; return false; diff --git a/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs b/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs deleted file mode 100644 index d2f8bf64d..000000000 --- a/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs +++ /dev/null @@ -1,428 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using ILLink.RoslynAnalyzer; -using ILLink.Shared; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Testing; -using Microsoft.CodeAnalysis.Text; -using Xunit; -using VerifyCS = ILLink.RoslynAnalyzer.Tests.CSharpCodeFixVerifier< - ILLink.RoslynAnalyzer.RequiresUnreferencedCodeAnalyzer, - ILLink.CodeFix.RequiresUnreferencedCodeCodeFixProvider>; - -namespace ILLink.RoslynAnalyzer.Tests -{ - public class RequiresUnreferencedCodeAnalyzerTests - { - static readonly DiagnosticDescriptor dynamicInvocationDiagnosticDescriptor = DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.RequiresUnreferencedCode, new DiagnosticString ("DynamicTypeInvocation")); - - static Task VerifyRequiresUnreferencedCodeAnalyzer (string source, params DiagnosticResult[] expected) => - VerifyRequiresUnreferencedCodeAnalyzer (source, null, expected); - - static async Task VerifyRequiresUnreferencedCodeAnalyzer (string source, IEnumerable<MetadataReference>? additionalReferences, params DiagnosticResult[] expected) => - await VerifyCS.VerifyAnalyzerAsync (source, - TestCaseUtils.UseMSBuildProperties (MSBuildPropertyOptionNames.EnableTrimAnalyzer), - additionalReferences ?? Array.Empty<MetadataReference> (), - expected); - - static Task VerifyRequiresUnreferencedCodeCodeFix ( - string source, - string fixedSource, - DiagnosticResult[] baselineExpected, - DiagnosticResult[] fixedExpected, - int? numberOfIterations = null) - { - var test = new VerifyCS.Test { - TestCode = source, - FixedCode = fixedSource, - ReferenceAssemblies = TestCaseUtils.Net6PreviewAssemblies - }; - test.ExpectedDiagnostics.AddRange (baselineExpected); - test.TestState.AnalyzerConfigFiles.Add ( - ("/.editorconfig", SourceText.From (@$" -is_global = true -build_property.{MSBuildPropertyOptionNames.EnableTrimAnalyzer} = true"))); - if (numberOfIterations != null) { - test.NumberOfIncrementalIterations = numberOfIterations; - test.NumberOfFixAllIterations = numberOfIterations; - } - test.FixedState.ExpectedDiagnostics.AddRange (fixedExpected); - return test.RunAsync (); - } - - - - [Fact] - public async Task SimpleDiagnosticFix () - { - var test = @" -using System.Diagnostics.CodeAnalysis; - -public class C -{ - [RequiresUnreferencedCodeAttribute(""message"")] - public int M1() => 0; - - int M2() => M1(); -} -class D -{ - public int M3(C c) => c.M1(); - - public class E - { - public int M4(C c) => c.M1(); - } -} -public class E -{ - public class F - { - public int M5(C c) => c.M1(); - } -} -"; - - var fixtest = @" -using System.Diagnostics.CodeAnalysis; - -public class C -{ - [RequiresUnreferencedCodeAttribute(""message"")] - public int M1() => 0; - - [RequiresUnreferencedCode(""Calls C.M1()"")] - int M2() => M1(); -} -class D -{ - [RequiresUnreferencedCode(""Calls C.M1()"")] - public int M3(C c) => c.M1(); - - public class E - { - [RequiresUnreferencedCode(""Calls C.M1()"")] - public int M4(C c) => c.M1(); - } -} -public class E -{ - public class F - { - [RequiresUnreferencedCode()] - public int M5(C c) => c.M1(); - } -} -"; - - await VerifyRequiresUnreferencedCodeCodeFix (test, fixtest, new[] { - // /0/Test0.cs(9,17): warning IL2026: Using member 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message. - VerifyCS.Diagnostic (DiagnosticId.RequiresUnreferencedCode).WithSpan (9, 17, 9, 21).WithArguments ("C.M1()", " message.", ""), - // /0/Test0.cs(13,27): warning IL2026: Using member 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message. - VerifyCS.Diagnostic(DiagnosticId.RequiresUnreferencedCode).WithSpan(13, 27, 13, 33).WithArguments("C.M1()", " message.", ""), - // /0/Test0.cs(17,31): warning IL2026: Using member 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message. - VerifyCS.Diagnostic (DiagnosticId.RequiresUnreferencedCode).WithSpan (17, 31, 17, 37).WithArguments ("C.M1()", " message.", ""), - // /0/Test0.cs(24,31): warning IL2026: Using member 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message. - VerifyCS.Diagnostic (DiagnosticId.RequiresUnreferencedCode).WithSpan (24, 31, 24, 37).WithArguments ("C.M1()", " message.", "") - }, new[] { - // /0/Test0.cs(27,10): error CS7036: There is no argument given that corresponds to the required formal parameter 'message' of 'RequiresUnreferencedCodeAttribute.RequiresUnreferencedCodeAttribute(string)' - DiagnosticResult.CompilerError("CS7036").WithSpan(27, 10, 27, 36).WithArguments("message", "System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute.RequiresUnreferencedCodeAttribute(string)"), - } - ); - } - - [Fact] - public Task FixInLambda () - { - var src = @" -using System; -using System.Diagnostics.CodeAnalysis; - -public class C -{ - [RequiresUnreferencedCodeAttribute(""message"")] - public int M1() => 0; - - Action M2() - { - return () => M1(); - } -}"; - var fix = @" -using System; -using System.Diagnostics.CodeAnalysis; - -public class C -{ - [RequiresUnreferencedCodeAttribute(""message"")] - public int M1() => 0; - - Action M2() - { - return () => M1(); - } -}"; - // No fix available inside a lambda, requries manual code change since attribute cannot - // be applied - return VerifyRequiresUnreferencedCodeCodeFix ( - src, - fix, - baselineExpected: new[] { - // /0/Test0.cs(12,22): warning IL2026: Using member 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message. - VerifyCS.Diagnostic(DiagnosticId.RequiresUnreferencedCode).WithSpan(12, 22, 12, 26).WithArguments("C.M1()", " message.", "") - }, - fixedExpected: Array.Empty<DiagnosticResult> ()); - } - - [Fact] - public Task FixInLocalFunc () - { - var src = @" -using System; -using System.Diagnostics.CodeAnalysis; - -public class C -{ - [RequiresUnreferencedCodeAttribute(""message"")] - public int M1() => 0; - - Action M2() - { - void Wrapper () => M1(); - return Wrapper; - } -}"; - var fix = @" -using System; -using System.Diagnostics.CodeAnalysis; - -public class C -{ - [RequiresUnreferencedCodeAttribute(""message"")] - public int M1() => 0; - - [RequiresUnreferencedCode(""Calls Wrapper()"")] - Action M2() - { - [global::System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute(""Calls C.M1()"")] void Wrapper () => M1(); - return Wrapper; - } -}"; - // Roslyn currently doesn't simplify the attribute name properly, see https://github.com/dotnet/roslyn/issues/52039 - return VerifyRequiresUnreferencedCodeCodeFix ( - src, - fix, - baselineExpected: new[] { - // /0/Test0.cs(12,28): warning IL2026: Using member 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message. - VerifyCS.Diagnostic(DiagnosticId.RequiresUnreferencedCode).WithSpan(12, 28, 12, 32).WithArguments("C.M1()", " message.", "") - }, - fixedExpected: Array.Empty<DiagnosticResult> (), - // The default iterations for the codefix is the number of diagnostics (1 in this case) - // but since the codefixer introduces a new diagnostic in the first iteration, it needs - // to run twice, so we need to set the number of iterations to 2. - numberOfIterations: 2); - } - - [Fact] - public Task FixInCtor () - { - var src = @" -using System; -using System.Diagnostics.CodeAnalysis; - -public class C -{ - [RequiresUnreferencedCodeAttribute(""message"")] - public static int M1() => 0; - - public C() => M1(); -}"; - var fix = @" -using System; -using System.Diagnostics.CodeAnalysis; - -public class C -{ - [RequiresUnreferencedCodeAttribute(""message"")] - public static int M1() => 0; - - [RequiresUnreferencedCode()] - public C() => M1(); -}"; - // Roslyn currently doesn't simplify the attribute name properly, see https://github.com/dotnet/roslyn/issues/52039 - return VerifyRequiresUnreferencedCodeCodeFix ( - src, - fix, - baselineExpected: new[] { - // /0/Test0.cs(10,19): warning IL2026: Using member 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message. - VerifyCS.Diagnostic(DiagnosticId.RequiresUnreferencedCode).WithSpan(10, 19, 10, 23).WithArguments("C.M1()", " message.", "") - }, - fixedExpected: new[] { - // /0/Test0.cs(10,6): error CS7036: There is no argument given that corresponds to the required formal parameter 'message' of 'RequiresUnreferencedCodeAttribute.RequiresUnreferencedCodeAttribute(string)' - DiagnosticResult.CompilerError("CS7036").WithSpan(10, 6, 10, 32).WithArguments("message", "System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute.RequiresUnreferencedCodeAttribute(string)"), - }); - } - - [Fact] - public Task FixInPropertyDecl () - { - var src = @" -using System; -using System.Diagnostics.CodeAnalysis; - -public class C -{ - [RequiresUnreferencedCodeAttribute(""message"")] - public int M1() => 0; - - int M2 => M1(); -}"; - var fix = @" -using System; -using System.Diagnostics.CodeAnalysis; - -public class C -{ - [RequiresUnreferencedCodeAttribute(""message"")] - public int M1() => 0; - - int M2 => M1(); -}"; - // Can't apply RUC on properties at the moment - return VerifyRequiresUnreferencedCodeCodeFix ( - src, - fix, - baselineExpected: new[] { - // /0/Test0.cs(10,15): warning IL2026: Using member 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message. - VerifyCS.Diagnostic(DiagnosticId.RequiresUnreferencedCode).WithSpan(10, 15, 10, 19).WithArguments("C.M1()", " message.", "") - }, - fixedExpected: new[] { - // /0/Test0.cs(10,15): warning IL2026: Using member 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message. - VerifyCS.Diagnostic(DiagnosticId.RequiresUnreferencedCode).WithSpan(10, 15, 10, 19).WithArguments("C.M1()", " message.", "") - }); - } - - [Fact] - public Task InvocationOnDynamicType () - { - var source = @" -using System; -class C -{ - static void M0 () - { - dynamic dynamicField = ""Some string""; - Console.WriteLine (dynamicField); - } - - static void M1 () - { - MethodWithDynamicArgDoNothing (0); - MethodWithDynamicArgDoNothing (""Some string""); - MethodWithDynamicArg(-1); - } - - static void MethodWithDynamicArgDoNothing (dynamic arg) - { - } - - static void MethodWithDynamicArg (dynamic arg) - { - arg.MethodWithDynamicArg (arg); - } -}"; - - return VerifyRequiresUnreferencedCodeAnalyzer (source, - // (8,3): warning IL2026: Invoking members on dynamic types is not trimming safe. Types or members might have been removed by the trimmer. - VerifyCS.Diagnostic (dynamicInvocationDiagnosticDescriptor).WithSpan (8, 3, 8, 35), - // (24,3): warning IL2026: Invoking members on dynamic types is not trimming safe. Types or members might have been removed by the trimmer. - VerifyCS.Diagnostic (dynamicInvocationDiagnosticDescriptor).WithSpan (24, 3, 24, 33)); - } - - [Fact] - public Task InvocationOnDynamicTypeInMethodWithRUCDoesNotWarnTwoTimes () - { - var source = @" -using System; -using System.Diagnostics.CodeAnalysis; -class C -{ - [RequiresUnreferencedCode (""We should only see the warning related to this annotation, and none about the dynamic type."")] - static void M0 () - { - dynamic dynamicField = ""Some string""; - Console.WriteLine (dynamicField); - } -}"; - - return VerifyRequiresUnreferencedCodeAnalyzer (source); - } - - [Fact] - public Task TestMakeGenericMethodUsage () - { - var source = @" -using System.Diagnostics.CodeAnalysis; -using System.Reflection; - -class C -{ - static void M1 (MethodInfo methodInfo) - { - methodInfo.MakeGenericMethod (typeof (C)); - } - - [RequiresUnreferencedCode (""Message from RUC"")] - static void M2 (MethodInfo methodInfo) - { - methodInfo.MakeGenericMethod (typeof (C)); - } -}"; - - return VerifyRequiresUnreferencedCodeAnalyzer (source); - } - - [Fact] - public Task TestMakeGenericTypeUsage () - { - var source = @" -using System; -using System.Diagnostics.CodeAnalysis; - -class C -{ - static void M1 (Type t) - { - typeof (Nullable<>).MakeGenericType (typeof (C)); - } - - [RequiresUnreferencedCode (""Message from RUC"")] - static void M2 (Type t) - { - typeof (Nullable<>).MakeGenericType (typeof (C)); - } -}"; - - return VerifyRequiresUnreferencedCodeAnalyzer (source); - } - - [Fact] - public Task VerifyThatAnalysisOfFieldsDoesNotNullRef () - { - var source = @" -using System.Diagnostics.CodeAnalysis; - -[DynamicallyAccessedMembers (field)] -class C -{ - public const DynamicallyAccessedMemberTypes field = DynamicallyAccessedMemberTypes.PublicMethods; -}"; - - return VerifyRequiresUnreferencedCodeAnalyzer (source); - } - } -} diff --git a/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresCapability.cs b/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresCapability.cs index 6d0d97dd3..0904bb4e4 100644 --- a/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresCapability.cs +++ b/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresCapability.cs @@ -1186,7 +1186,6 @@ namespace Mono.Linker.Tests.Cases.RequiresCapability public string PropertyAnnotationInProperty { get; set; } [ExpectedWarning ("IL3003", "ImplementationClassWithoutRequires.PropertyAnnotationInPropertyAndAccessor", "IBaseWithRequires.PropertyAnnotationInPropertyAndAccessor", ProducedBy = ProducedBy.Analyzer)] - public string PropertyAnnotationInPropertyAndAccessor { [RequiresAssemblyFiles("Message")] [RequiresUnreferencedCode("Message")] @@ -1539,6 +1538,7 @@ namespace Mono.Linker.Tests.Cases.RequiresCapability class DerivedWithRequiresOnType : BaseWithoutRequiresOnType { // Bug https://github.com/dotnet/linker/issues/2379 + //[ExpectedWarning ("IL2046", ProducedBy = ProducedBy.Analyzer)] public override void Method () { } } @@ -1575,6 +1575,7 @@ namespace Mono.Linker.Tests.Cases.RequiresCapability } // Bug https://github.com/dotnet/linker/issues/2379 + //[ExpectedWarning ("IL2046", ProducedBy = ProducedBy.Analyzer)] public int Method (int a) { return a; |