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

github.com/mono/linker.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndy Gocke <andy@commentout.net>2021-03-31 09:43:23 +0300
committerGitHub <noreply@github.com>2021-03-31 09:43:23 +0300
commitdd7d70118b7146125781c830bbff47a8cb953f39 (patch)
tree44497e9560353b92d24512d110d596370b6554bb /test/ILLink.RoslynAnalyzer.Tests
parent28d5013b48f1684cc305baa017b2b716230a1377 (diff)
Add codefix for RequiresUnreferencedCode (#1865)
Simple codefix that just adds RequiresUnreferencedCode to the containing method if a RequiresUnreferencedCode analyzer warning fires.
Diffstat (limited to 'test/ILLink.RoslynAnalyzer.Tests')
-rw-r--r--test/ILLink.RoslynAnalyzer.Tests/ILLink.RoslynAnalyzer.Tests.csproj2
-rw-r--r--test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs286
-rw-r--r--test/ILLink.RoslynAnalyzer.Tests/Verifiers/CSharpCodeFixVerifier`2.cs99
3 files changed, 384 insertions, 3 deletions
diff --git a/test/ILLink.RoslynAnalyzer.Tests/ILLink.RoslynAnalyzer.Tests.csproj b/test/ILLink.RoslynAnalyzer.Tests/ILLink.RoslynAnalyzer.Tests.csproj
index 03d9d5f59..f1aafa616 100644
--- a/test/ILLink.RoslynAnalyzer.Tests/ILLink.RoslynAnalyzer.Tests.csproj
+++ b/test/ILLink.RoslynAnalyzer.Tests/ILLink.RoslynAnalyzer.Tests.csproj
@@ -6,7 +6,9 @@
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit" Version="$(MicrosoftCodeAnalysisCSharpAnalyzerTestingXunitVersion)" />
+ <PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit" Version="$(MicrosoftCodeAnalysisCSharpAnalyzerTestingXunitVersion)" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="$(MicrosoftCodeAnalysisCSharpWorkspacesVersion)" />
+ <ProjectReference Include="..\..\src\ILLink.CodeFix\ILLink.CodeFixProvider.csproj" />
<ProjectReference Include="..\..\src\ILLink.RoslynAnalyzer\ILLink.RoslynAnalyzer.csproj" />
<ProjectReference Include="../Mono.Linker.Tests.Cases\Mono.Linker.Tests.Cases.csproj" />
</ItemGroup>
diff --git a/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs b/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs
index 34faf97fc..4819defdd 100644
--- a/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs
+++ b/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs
@@ -2,11 +2,14 @@
// 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.Threading.Tasks;
using Microsoft.CodeAnalysis.Testing;
+using Microsoft.CodeAnalysis.Text;
using Xunit;
-using VerifyCS = ILLink.RoslynAnalyzer.Tests.CSharpAnalyzerVerifier<
- ILLink.RoslynAnalyzer.RequiresUnreferencedCodeAnalyzer>;
+using VerifyCS = ILLink.RoslynAnalyzer.Tests.CSharpCodeFixVerifier<
+ ILLink.RoslynAnalyzer.RequiresUnreferencedCodeAnalyzer,
+ ILLink.CodeFix.RequiresUnreferencedCodeCodeFixProvider>;
namespace ILLink.RoslynAnalyzer.Tests
{
@@ -19,6 +22,38 @@ namespace ILLink.RoslynAnalyzer.Tests
expected);
}
+ static Task VerifyRequiresUnreferencedCodeCodeFix (
+ string source,
+ string fixedSource,
+ DiagnosticResult[] baselineExpected,
+ DiagnosticResult[] fixedExpected)
+ {
+ 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,
+ };
+ 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 ();
+ }
+
[Fact]
public Task SimpleDiagnostic ()
{
@@ -37,6 +72,251 @@ class C
}
[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 M1"")]
+ int M2() => M1();
+}
+class D
+{
+ [RequiresUnreferencedCode(""Calls M1"")]
+ public int M3(C c) => c.M1();
+
+ public class E
+ {
+ [RequiresUnreferencedCode(""Calls 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 method 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message.
+ VerifyCS.Diagnostic ().WithSpan (9, 17, 9, 21).WithArguments ("C.M1()", "message", ""),
+ // /0/Test0.cs(13,27): warning IL2026: Using method 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message.
+ VerifyCS.Diagnostic().WithSpan(13, 27, 13, 33).WithArguments("C.M1()", "message", ""),
+ // /0/Test0.cs(17,31): warning IL2026: Using method 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message.
+ VerifyCS.Diagnostic ().WithSpan (17, 31, 17, 37).WithArguments ("C.M1()", "message", ""),
+ // /0/Test0.cs(24,31): warning IL2026: Using method 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message.
+ VerifyCS.Diagnostic ().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 method 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message.
+ VerifyCS.Diagnostic().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;
+
+ Action M2()
+ {
+ [global::System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute(""Calls 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 method 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message.
+ VerifyCS.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 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 method 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message.
+ VerifyCS.Diagnostic().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 method 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message.
+ VerifyCS.Diagnostic().WithSpan(10, 15, 10, 19).WithArguments("C.M1()", "message", "")
+ },
+ fixedExpected: new[] {
+ // /0/Test0.cs(10,15): warning IL2026: Using method 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message.
+ VerifyCS.Diagnostic().WithSpan(10, 15, 10, 19).WithArguments("C.M1()", "message", "")
+ });
+ }
+
+ [Fact]
public Task TestRequiresWithMessageAndUrlOnMethod ()
{
var MessageAndUrlOnMethod = @"
@@ -78,7 +358,7 @@ class C
}
}";
return VerifyRequiresUnreferencedCodeAnalyzer (PropertyRequires,
- // (8,7): warning IL2026: Using method 'C.PropertyRequires.get' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. Message for --getter PropertyRequires--.
+ // (8,7): warning IL2026: Using method '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--", "")
);
}
diff --git a/test/ILLink.RoslynAnalyzer.Tests/Verifiers/CSharpCodeFixVerifier`2.cs b/test/ILLink.RoslynAnalyzer.Tests/Verifiers/CSharpCodeFixVerifier`2.cs
new file mode 100644
index 000000000..6ba811280
--- /dev/null
+++ b/test/ILLink.RoslynAnalyzer.Tests/Verifiers/CSharpCodeFixVerifier`2.cs
@@ -0,0 +1,99 @@
+// 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.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeFixes;
+using Microsoft.CodeAnalysis.CSharp.Testing;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.Testing;
+using Microsoft.CodeAnalysis.Testing.Verifiers;
+
+namespace ILLink.RoslynAnalyzer.Tests
+{
+ /// <summary>
+ /// A default verifier for diagnostic analyzers with code fixes.
+ /// </summary>
+ /// <typeparam name="TAnalyzer">The <see cref="DiagnosticAnalyzer"/> to test.</typeparam>
+ /// <typeparam name="TCodeFix">The <see cref="CodeFixProvider"/> to test.</typeparam>
+ /// <typeparam name="TTest">The test implementation to use.</typeparam>
+ public partial class CSharpCodeFixVerifier<TAnalyzer, TCodeFix>
+ where TAnalyzer : DiagnosticAnalyzer, new()
+ where TCodeFix : CodeFixProvider, new()
+ {
+ public class Test : CSharpCodeFixTest<TAnalyzer, TCodeFix, XUnitVerifier>
+ {
+ public Test ()
+ {
+ SolutionTransforms.Add ((solution, projectId) => {
+ var compilationOptions = solution.GetProject (projectId)!.CompilationOptions;
+ compilationOptions = compilationOptions!.WithSpecificDiagnosticOptions (
+ compilationOptions.SpecificDiagnosticOptions.SetItems (CSharpVerifierHelper.NullableWarnings));
+ solution = solution.WithProjectCompilationOptions (projectId, compilationOptions);
+
+ return solution;
+ });
+ }
+ }
+
+ /// <inheritdoc cref="AnalyzerVerifier{TAnalyzer}.Diagnostic()"/>
+ public static DiagnosticResult Diagnostic ()
+ => CSharpAnalyzerVerifier<TAnalyzer>.Diagnostic ();
+
+ /// <inheritdoc cref="AnalyzerVerifier{TAnalyzer, TTest}.Diagnostic(string)"/>
+ public static DiagnosticResult Diagnostic (string diagnosticId)
+ => CSharpAnalyzerVerifier<TAnalyzer>.Diagnostic (diagnosticId);
+
+ /// <inheritdoc cref="AnalyzerVerifier{TAnalyzer, TTest}.Diagnostic(DiagnosticDescriptor)"/>
+ public static DiagnosticResult Diagnostic (DiagnosticDescriptor descriptor)
+ => CSharpAnalyzerVerifier<TAnalyzer>.Diagnostic (descriptor);
+
+ /// <inheritdoc cref="AnalyzerVerifier{TAnalyzer, TTest}.VerifyAnalyzerAsync(string, DiagnosticResult[])"/>
+ public static Task VerifyAnalyzerAsync (string source, (string, string)[]? analyzerOptions = null, params DiagnosticResult[] expected)
+ => CSharpAnalyzerVerifier<TAnalyzer>.VerifyAnalyzerAsync (source, analyzerOptions, expected);
+
+ /// <summary>
+ /// Verifies the analyzer provides diagnostics which, in combination with the code fix, produce the expected
+ /// fixed code.
+ /// </summary>
+ /// <param name="source">The source text to test. Any diagnostics are defined in markup.</param>
+ /// <param name="fixedSource">The expected fixed source text. Any remaining diagnostics are defined in markup.</param>
+ /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
+ public static Task VerifyCodeFixAsync (string source, string fixedSource)
+ => VerifyCodeFixAsync (source, DiagnosticResult.EmptyDiagnosticResults, fixedSource);
+
+ /// <summary>
+ /// Verifies the analyzer provides diagnostics which, in combination with the code fix, produce the expected
+ /// fixed code.
+ /// </summary>
+ /// <param name="source">The source text to test, which may include markup syntax.</param>
+ /// <param name="expected">The expected diagnostic. This diagnostic is in addition to any diagnostics defined in
+ /// markup.</param>
+ /// <param name="fixedSource">The expected fixed source text. Any remaining diagnostics are defined in markup.</param>
+ /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
+ public static Task VerifyCodeFixAsync (string source, DiagnosticResult expected, string fixedSource)
+ => VerifyCodeFixAsync (source, new[] { expected }, fixedSource);
+
+ /// <summary>
+ /// Verifies the analyzer provides diagnostics which, in combination with the code fix, produce the expected
+ /// fixed code.
+ /// </summary>
+ /// <param name="source">The source text to test, which may include markup syntax.</param>
+ /// <param name="expected">The expected diagnostics. These diagnostics are in addition to any diagnostics
+ /// defined in markup.</param>
+ /// <param name="fixedSource">The expected fixed source text. Any remaining diagnostics are defined in markup.</param>
+ /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
+ public static Task VerifyCodeFixAsync (string source, DiagnosticResult[] expected, string fixedSource)
+ {
+ var test = new Test {
+ TestCode = source,
+ FixedCode = fixedSource,
+ };
+
+ test.ExpectedDiagnostics.AddRange (expected);
+ return test.RunAsync (CancellationToken.None);
+ }
+ }
+}