diff options
author | Tlakaelel Axayakatl Ceja <tlakaelel.ceja@microsoft.com> | 2021-05-06 00:25:24 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-05-06 00:25:24 +0300 |
commit | 031093e11a60c54f06348208fdf91b67f49b5f1c (patch) | |
tree | a61be95088a673bd2cd4eaca99b4a7f68d7961ee /test/ILLink.RoslynAnalyzer.Tests | |
parent | 4afa1051f5e44560368aacb21429e956ce39cc1a (diff) |
Add UnconditionalSuppressMessage codefixer (#2002)
Add CodeFix for UnconditionalSuppressMessage
Add test for UnconditionalSuppressMessage
Change FindContainingMember to FindAttributableParent and add a list of AttributableParentTargets to values that are possible
Add a base class to remove code duplication
Use net5.0 reference assemblies and define requiresAssemblyFiles manually (reverts #1922)
Diffstat (limited to 'test/ILLink.RoslynAnalyzer.Tests')
4 files changed, 418 insertions, 18 deletions
diff --git a/test/ILLink.RoslynAnalyzer.Tests/RequiresAssemblyFilesAnalyzerTests.cs b/test/ILLink.RoslynAnalyzer.Tests/RequiresAssemblyFilesAnalyzerTests.cs index 4826cf20f..7c3babedc 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/RequiresAssemblyFilesAnalyzerTests.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/RequiresAssemblyFilesAnalyzerTests.cs @@ -11,6 +11,24 @@ namespace ILLink.RoslynAnalyzer.Tests { static Task VerifyRequiresAssemblyFilesAnalyzer (string source, params DiagnosticResult[] expected) { + var attributeDefinition = @" +namespace System.Diagnostics.CodeAnalysis +{ +#nullable enable + [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; } + } +}"; + source = source + attributeDefinition; return VerifyCS.VerifyAnalyzerAsync (source, TestCaseUtils.UseMSBuildProperties (MSBuildPropertyOptionNames.EnableSingleFileAnalyzer), expected); diff --git a/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs b/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs index 543b2a1b3..1712e6e2c 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs @@ -29,22 +29,10 @@ namespace ILLink.RoslynAnalyzer.Tests DiagnosticResult[] fixedExpected, int? numberOfIterations = null) { - const string rucDef = @" -#nullable enable -namespace System.Diagnostics.CodeAnalysis -{ - [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method, Inherited = false)] - public sealed class RequiresUnreferencedCodeAttribute : Attribute - { - public RequiresUnreferencedCodeAttribute(string message) { Message = message; } - public string Message { get; } - public string? Url { get; set; } - } -} -"; var test = new VerifyCS.Test { - TestCode = source + rucDef, - FixedCode = fixedSource + rucDef, + TestCode = source, + FixedCode = fixedSource, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, }; test.ExpectedDiagnostics.AddRange (baselineExpected); test.TestState.AnalyzerConfigFiles.Add ( diff --git a/test/ILLink.RoslynAnalyzer.Tests/UnconditionalSuppressMessageCodeFixTests.cs b/test/ILLink.RoslynAnalyzer.Tests/UnconditionalSuppressMessageCodeFixTests.cs new file mode 100644 index 000000000..0653274ac --- /dev/null +++ b/test/ILLink.RoslynAnalyzer.Tests/UnconditionalSuppressMessageCodeFixTests.cs @@ -0,0 +1,394 @@ +// 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.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; +using ILLink.CodeFix; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.Text; +using Xunit; +using VerifyCSUSMwithRAF = ILLink.RoslynAnalyzer.Tests.CSharpCodeFixVerifier< + ILLink.RoslynAnalyzer.RequiresAssemblyFilesAnalyzer, + ILLink.CodeFix.UnconditionalSuppressMessageCodeFixProvider>; +using VerifyCSUSMwithRUC = ILLink.RoslynAnalyzer.Tests.CSharpCodeFixVerifier< + ILLink.RoslynAnalyzer.RequiresUnreferencedCodeAnalyzer, + ILLink.CodeFix.UnconditionalSuppressMessageCodeFixProvider>; + + +namespace ILLink.RoslynAnalyzer.Tests +{ + public class UnconditionalSuppressMessageCodeFixTests + { + static Task VerifyUnconditionalSuppressMessageCodeFixWithRUC ( + string source, + string fixedSource, + DiagnosticResult[] baselineExpected, + DiagnosticResult[] fixedExpected) + { + var test = new VerifyCSUSMwithRUC.Test { + TestCode = source, + FixedCode = fixedSource, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + }; + test.ExpectedDiagnostics.AddRange (baselineExpected); + test.TestState.AnalyzerConfigFiles.Add ( + ("/.editorconfig", SourceText.From (@$" +is_global = true +build_property.{MSBuildPropertyOptionNames.EnableTrimAnalyzer} = true"))); + test.FixedState.ExpectedDiagnostics.AddRange (fixedExpected); + return test.RunAsync (); + } + + static Task VerifyUnconditionalSuppressMessageCodeFixWithRAF ( + string source, + string fixedSource, + DiagnosticResult[] baselineExpected, + DiagnosticResult[] fixedExpected) + { + var attributeDefinition = @" +namespace System.Diagnostics.CodeAnalysis +{ +#nullable enable + [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; } + } +}"; + var test = new VerifyCSUSMwithRAF.Test { + TestCode = source + attributeDefinition, + FixedCode = fixedSource + attributeDefinition, + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + }; + test.ExpectedDiagnostics.AddRange (baselineExpected); + test.TestState.AnalyzerConfigFiles.Add ( + ("/.editorconfig", SourceText.From (@$" +is_global = true +build_property.{MSBuildPropertyOptionNames.EnableSingleFileAnalyzer} = true"))); + test.FixedState.ExpectedDiagnostics.AddRange (fixedExpected); + return test.RunAsync (); + } + + [Fact] + public Task SuppressRequiresUnreferencedCodeFixer () + { + var test = @" +using System.Diagnostics.CodeAnalysis; +public class C +{ + [RequiresUnreferencedCode(""message"")] + public int M1() => 0; + int M2() => M1(); +}"; + var fixtest = @" +using System.Diagnostics.CodeAnalysis; +public class C +{ + [RequiresUnreferencedCode(""message"")] + public int M1() => 0; + [UnconditionalSuppressMessage(""Trimming"", ""IL2026:Methods annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code"", Justification = ""<Pending>"")] + int M2() => M1(); +}"; + return VerifyUnconditionalSuppressMessageCodeFixWithRUC ( + test, + fixtest, + baselineExpected: new[] { + // /0/Test0.cs(7,17): warning IL2026: Using method 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message. + VerifyCSUSMwithRUC.Diagnostic ().WithSpan (7, 17, 7, 21).WithArguments ("C.M1()", "message", ""), + }, + fixedExpected: Array.Empty<DiagnosticResult> ()); + } + + [Fact] + public Task SuppressRequiresAssemblyFilesFixer () + { + var test = @" +using System.Diagnostics.CodeAnalysis; +public class C +{ + [RequiresAssemblyFiles(Message = ""message"")] + public int M1() => 0; + int M2() => M1(); +}"; + var fixtest = @" +using System.Diagnostics.CodeAnalysis; +public class C +{ + [RequiresAssemblyFiles(Message = ""message"")] + public int M1() => 0; + [UnconditionalSuppressMessage(""SingleFile"", ""IL3002:Avoid calling members marked with 'RequiresAssemblyFilesAttribute' when publishing as a single-file"", Justification = ""<Pending>"")] + int M2() => M1(); +}"; + return VerifyUnconditionalSuppressMessageCodeFixWithRAF ( + test, + fixtest, + baselineExpected: new[] { + // /0/Test0.cs(7,17): warning IL2026: Using method 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message. + VerifyCSUSMwithRAF.Diagnostic (RequiresAssemblyFilesAnalyzer.IL3002).WithSpan (7, 17, 7, 21).WithArguments ("C.M1()", " message.", "") + }, + fixedExpected: Array.Empty<DiagnosticResult> ()); + } + + [Fact] + public Task FixInSingleFileSpecialCases () + { + var test = @" +using System.Reflection; +using System.Diagnostics.CodeAnalysis; +public class C +{ + public static Assembly assembly = Assembly.LoadFrom(""/some/path/not/in/bundle""); + public string M1() => assembly.Location; + public void M2() { + _ = assembly.GetFiles(); + } +} +"; + var fixtest = @" +using System.Reflection; +using System.Diagnostics.CodeAnalysis; +public class C +{ + public static Assembly assembly = Assembly.LoadFrom(""/some/path/not/in/bundle""); + + [UnconditionalSuppressMessage(""SingleFile"", ""IL3000:Avoid accessing Assembly file path when publishing as a single file"", Justification = ""<Pending>"")] + public string M1() => assembly.Location; + + [UnconditionalSuppressMessage(""SingleFile"", ""IL3001:Avoid accessing Assembly file path when publishing as a single file"", Justification = ""<Pending>"")] + public void M2() { + _ = assembly.GetFiles(); + } +} +"; + return VerifyUnconditionalSuppressMessageCodeFixWithRAF ( + test, + fixtest, + baselineExpected: new[] { + // /0/Test0.cs(7,27): warning IL3000: 'System.Reflection.Assembly.Location' always returns an empty string for assemblies embedded in a single-file app. If the path to the app directory is needed, consider calling 'System.AppContext.BaseDirectory'. + VerifyCSUSMwithRAF.Diagnostic(RequiresAssemblyFilesAnalyzer.IL3000).WithSpan (7, 27, 7, 44).WithArguments ("System.Reflection.Assembly.Location", "", ""), + // /0/Test0.cs(9,13): warning IL3001: 'System.Reflection.Assembly.GetFiles()' will throw for assemblies embedded in a single-file app + VerifyCSUSMwithRAF.Diagnostic(RequiresAssemblyFilesAnalyzer.IL3001).WithSpan (9, 13, 9, 32).WithArguments("System.Reflection.Assembly.GetFiles()", "", ""), + }, + fixedExpected: Array.Empty<DiagnosticResult> ()); + } + + [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; + + [UnconditionalSuppressMessage(""Trimming"", ""IL2026:Methods annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code"", Justification = ""<Pending>"")] + int M2 => M1(); +}"; + return VerifyUnconditionalSuppressMessageCodeFixWithRUC ( + src, + fix, + baselineExpected: new[] { + // /0/Test0.cs(10,15): warning IL2026: Using method 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message. + VerifyCSUSMwithRUC.Diagnostic ().WithSpan(10, 15, 10, 19).WithArguments("C.M1()", "message", "") + }, + fixedExpected: Array.Empty<DiagnosticResult> ()); + } + + [Fact] + public Task FixInField () + { + const string src = @" +using System; +using System.Diagnostics.CodeAnalysis; +class C +{ + public static Lazy<C> _default = new Lazy<C>(InitC); + public static C Default => _default.Value; + + [RequiresAssemblyFiles] + public static C InitC() { + C cObject = new C(); + return cObject; + } +}"; + var fixtest = @" +using System; +using System.Diagnostics.CodeAnalysis; +class C +{ + [UnconditionalSuppressMessage(""SingleFile"", ""IL3002:Avoid calling members marked with 'RequiresAssemblyFilesAttribute' when publishing as a single-file"", Justification = ""<Pending>"")] + public static Lazy<C> _default = new Lazy<C>(InitC); + public static C Default => _default.Value; + + [RequiresAssemblyFiles] + public static C InitC() { + C cObject = new C(); + return cObject; + } +}"; + return VerifyUnconditionalSuppressMessageCodeFixWithRAF ( + src, + fixtest, + baselineExpected: new[] { + // /0/Test0.cs(6,50): warning IL3002: Using member 'C.InitC()' which has 'RequiresAssemblyFilesAttribute' can break functionality when embedded in a single-file app. + VerifyCSUSMwithRAF.Diagnostic (RequiresAssemblyFilesAnalyzer.IL3002).WithSpan (6, 50, 6, 55).WithArguments ("C.InitC()", "", ""), + }, + 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; + + Action M2() + { + [global::System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute(""Trimming"", ""IL2026:Methods annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code"", Justification = ""<Pending>"")] void Wrapper () => M1(); + return Wrapper; + } +}"; + // Roslyn currently doesn't simplify the attribute name properly, see https://github.com/dotnet/roslyn/issues/52039 + return VerifyUnconditionalSuppressMessageCodeFixWithRUC ( + src, + fix, + baselineExpected: new[] { + // /0/Test0.cs(12,28): warning IL2026: Using method 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message. + VerifyCSUSMwithRUC.Diagnostic ().WithSpan(12, 28, 12, 32).WithArguments("C.M1()", "message", "") + }, + fixedExpected: Array.Empty<DiagnosticResult> ()); + } + + [Fact] + public Task FixInCtor () + { + var src = @" +using System; +using System.Diagnostics.CodeAnalysis; + +public class C +{ + [RequiresUnreferencedCodeAttribute(""message"")] + public int M1() => 0; + + public C () => M1(); +}"; + var fix = @" +using System; +using System.Diagnostics.CodeAnalysis; + +public class C +{ + [RequiresUnreferencedCodeAttribute(""message"")] + public int M1() => 0; + + [UnconditionalSuppressMessage(""Trimming"", ""IL2026:Methods annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code"", Justification = ""<Pending>"")] + public C () => M1(); +}"; + return VerifyUnconditionalSuppressMessageCodeFixWithRUC ( + src, + fix, + baselineExpected: new[] { + // /0/Test0.cs(10,15): warning IL2026: Using method 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message. + VerifyCSUSMwithRUC.Diagnostic ().WithSpan(10, 20, 10, 24).WithArguments("C.M1()", "message", "") + }, + fixedExpected: Array.Empty<DiagnosticResult> ()); + } + + [Fact] + public Task FixInEvent () + { + var src = @" +using System; +using System.Diagnostics.CodeAnalysis; + +public class C +{ + [RequiresUnreferencedCodeAttribute(""message"")] + public int M1() => 0; + + public event EventHandler E1 + { + add + { + var a = M1(); + } + remove { } + } +}"; + var fix = @" +using System; +using System.Diagnostics.CodeAnalysis; + +public class C +{ + [RequiresUnreferencedCodeAttribute(""message"")] + public int M1() => 0; + + [UnconditionalSuppressMessage(""Trimming"", ""IL2026:Methods annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code"", Justification = ""<Pending>"")] + public event EventHandler E1 + { + add + { + var a = M1(); + } + remove { } + } +}"; + return VerifyUnconditionalSuppressMessageCodeFixWithRUC ( + src, + fix, + baselineExpected: new[] { + // /0/Test0.cs(14,21): warning IL2026: Using method 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message. + VerifyCSUSMwithRUC.Diagnostic ().WithSpan(14, 21, 14, 25).WithArguments("C.M1()", "message", "") + }, + fixedExpected: Array.Empty<DiagnosticResult> ()); + } + } +} diff --git a/test/ILLink.RoslynAnalyzer.Tests/Verifiers/CSharpAnalyzerVerifier`1.cs b/test/ILLink.RoslynAnalyzer.Tests/Verifiers/CSharpAnalyzerVerifier`1.cs index 5eae9acf3..56dae0a44 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/Verifiers/CSharpAnalyzerVerifier`1.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/Verifiers/CSharpAnalyzerVerifier`1.cs @@ -42,12 +42,12 @@ namespace ILLink.RoslynAnalyzer.Tests SyntaxTree src, (string, string)[]? globalAnalyzerOptions = null) { + var mdRef = MetadataReference.CreateFromFile (typeof (Mono.Linker.Tests.Cases.Expectations.Metadata.BaseMetadataAttribute).Assembly.Location); + var comp = CSharpCompilation.Create ( assemblyName: Guid.NewGuid ().ToString ("N"), syntaxTrees: new SyntaxTree[] { src }, - references: await Task.Run (() => new List<MetadataReference> { - MetadataReference.CreateFromFile (typeof (int).Assembly.Location) - }), + references: (await ReferenceAssemblies.Net.Net50.ResolveAsync (null, default)).Add (mdRef), new CSharpCompilationOptions (OutputKind.DynamicallyLinkedLibrary)); var analyzerOptions = new AnalyzerOptions ( |