From 61b263c97011826017509d305da09ce51e27c6a5 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Tue, 17 Sep 2024 22:17:41 +0000 Subject: [PATCH 1/3] Support class target in RUC/RDC code fixers --- .../BaseAttributeCodeFixProvider.cs | 17 +++++++- .../RequiresDynamicCodeCodeFixProvider.cs | 2 +- ...RequiresUnreferencedCodeCodeFixProvider.cs | 2 +- .../RequiresDynamicCodeAnalyzerTests.cs | 40 +++++++++++++++++++ .../RequiresUnreferencedCodeAnalyzerTests.cs | 40 +++++++++++++++++++ 5 files changed, 97 insertions(+), 4 deletions(-) diff --git a/src/tools/illink/src/ILLink.CodeFix/BaseAttributeCodeFixProvider.cs b/src/tools/illink/src/ILLink.CodeFix/BaseAttributeCodeFixProvider.cs index cdf73ab1fae43..83bef9fc34aff 100644 --- a/src/tools/illink/src/ILLink.CodeFix/BaseAttributeCodeFixProvider.cs +++ b/src/tools/illink/src/ILLink.CodeFix/BaseAttributeCodeFixProvider.cs @@ -86,6 +86,7 @@ protected enum AttributeableParentTargets Property = 0x0002, Field = 0x0004, Event = 0x0008, + Class = 0x0010, All = MethodOrConstructor | Property | Field | Event } @@ -97,11 +98,23 @@ protected enum AttributeableParentTargets case LambdaExpressionSyntax: return null; - case LocalFunctionStatementSyntax or BaseMethodDeclarationSyntax or AccessorDeclarationSyntax when targets.HasFlag (AttributeableParentTargets.MethodOrConstructor): case PropertyDeclarationSyntax when targets.HasFlag (AttributeableParentTargets.Property): - case FieldDeclarationSyntax when targets.HasFlag (AttributeableParentTargets.Field): case EventDeclarationSyntax when targets.HasFlag (AttributeableParentTargets.Event): return (CSharpSyntaxNode) parentNode; + case PropertyDeclarationSyntax: + case EventDeclarationSyntax: + // If the attribute can be placed on a method but not directly on a property/event, we don't want to keep walking up + // the syntax tree to annotate the class. Instead the correct thing to do is to add accessor methods and annotatet those. + // The code fixer doesn't support doing this automatically, so return null to indicate that the attribute can't be added. + if (targets.HasFlag (AttributeableParentTargets.MethodOrConstructor)) + return null; + + parentNode = parentNode.Parent; + break; + case LocalFunctionStatementSyntax or BaseMethodDeclarationSyntax or AccessorDeclarationSyntax when targets.HasFlag (AttributeableParentTargets.MethodOrConstructor): + case FieldDeclarationSyntax when targets.HasFlag (AttributeableParentTargets.Field): + case ClassDeclarationSyntax when targets.HasFlag (AttributeableParentTargets.Class): + return (CSharpSyntaxNode) parentNode; default: parentNode = parentNode.Parent; diff --git a/src/tools/illink/src/ILLink.CodeFix/RequiresDynamicCodeCodeFixProvider.cs b/src/tools/illink/src/ILLink.CodeFix/RequiresDynamicCodeCodeFixProvider.cs index 62c484d418174..5ab3a0d258657 100644 --- a/src/tools/illink/src/ILLink.CodeFix/RequiresDynamicCodeCodeFixProvider.cs +++ b/src/tools/illink/src/ILLink.CodeFix/RequiresDynamicCodeCodeFixProvider.cs @@ -25,7 +25,7 @@ public class RequiresDynamicCodeCodeFixProvider : BaseAttributeCodeFixProvider private protected override string FullyQualifiedAttributeName => RequiresDynamicCodeAnalyzer.FullyQualifiedRequiresDynamicCodeAttribute; - private protected override AttributeableParentTargets AttributableParentTargets => AttributeableParentTargets.MethodOrConstructor; + private protected override AttributeableParentTargets AttributableParentTargets => AttributeableParentTargets.MethodOrConstructor | AttributeableParentTargets.Class; public sealed override Task RegisterCodeFixesAsync (CodeFixContext context) => BaseRegisterCodeFixesAsync (context); diff --git a/src/tools/illink/src/ILLink.CodeFix/RequiresUnreferencedCodeCodeFixProvider.cs b/src/tools/illink/src/ILLink.CodeFix/RequiresUnreferencedCodeCodeFixProvider.cs index d3a93a0274a5b..99e4592dc80bd 100644 --- a/src/tools/illink/src/ILLink.CodeFix/RequiresUnreferencedCodeCodeFixProvider.cs +++ b/src/tools/illink/src/ILLink.CodeFix/RequiresUnreferencedCodeCodeFixProvider.cs @@ -25,7 +25,7 @@ public sealed class RequiresUnreferencedCodeCodeFixProvider : BaseAttributeCodeF private protected override string FullyQualifiedAttributeName => RequiresUnreferencedCodeAnalyzer.FullyQualifiedRequiresUnreferencedCodeAttribute; - private protected override AttributeableParentTargets AttributableParentTargets => AttributeableParentTargets.MethodOrConstructor; + private protected override AttributeableParentTargets AttributableParentTargets => AttributeableParentTargets.MethodOrConstructor | AttributeableParentTargets.Class; public sealed override Task RegisterCodeFixesAsync (CodeFixContext context) => BaseRegisterCodeFixesAsync (context); diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresDynamicCodeAnalyzerTests.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresDynamicCodeAnalyzerTests.cs index adc38ebb389b2..85039903137a5 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresDynamicCodeAnalyzerTests.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresDynamicCodeAnalyzerTests.cs @@ -348,6 +348,46 @@ private int M2 { return VerifyRequiresDynamicCodeCodeFix (src, fix, diag, Array.Empty ()); } + [Fact] + public Task FixInClass () + { + var src = $$""" + using System; + using System.Diagnostics.CodeAnalysis; + + public class C + { + [RequiresDynamicCodeAttribute("message")] + static int M1() => 0; + + static int Field = M1(); + } + """; + + var fix = $$""" + using System; + using System.Diagnostics.CodeAnalysis; + + [RequiresDynamicCode()] + public class C + { + [RequiresDynamicCodeAttribute("message")] + static int M1() => 0; + + static int Field = M1(); + } + """; + return VerifyRequiresDynamicCodeCodeFix (src, fix, + baselineExpected: new[] { + // /0/Test0.cs(9,21,9,25): warning IL2026: Using member 'C.M1()' which has 'RequiresDynamicCodeAttribute' can break functionality when trimming application code. message. + VerifyCS.Diagnostic(DiagnosticId.RequiresDynamicCode).WithSpan(9, 21, 9, 25).WithArguments("C.M1()", " message.", ""), + }, + fixedExpected: new[] { + // /0/Test0.cs(4,2): error CS7036: There is no argument given that corresponds to the required parameter 'message' of 'RequiresDynamicCodeAttribute.RequiresDynamicCodeAttribute(string)' + DiagnosticResult.CompilerError("CS7036").WithSpan(4, 2, 4, 23).WithArguments("message", "System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute.RequiresDynamicCodeAttribute(string)"), + }); + } + [Fact] public Task MakeGenericTypeWithAllKnownTypes () { diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs index 294b1ea9405df..620e93aae6a86 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs @@ -371,6 +371,46 @@ private int M2 { return VerifyRequiresUnreferencedCodeCodeFix (src, fix, diag, Array.Empty ()); } + [Fact] + public Task FixInClass () + { + var src = $$""" + using System; + using System.Diagnostics.CodeAnalysis; + + public class C + { + [RequiresUnreferencedCodeAttribute("message")] + static int M1() => 0; + + static int Field = M1(); + } + """; + + var fix = $$""" + using System; + using System.Diagnostics.CodeAnalysis; + + [RequiresUnreferencedCode()] + public class C + { + [RequiresUnreferencedCodeAttribute("message")] + static int M1() => 0; + + static int Field = M1(); + } + """; + return VerifyRequiresUnreferencedCodeCodeFix (src, fix, + baselineExpected: new[] { + // /0/Test0.cs(9,21): warning IL2026: Using member 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message. + VerifyCS.Diagnostic(DiagnosticId.RequiresUnreferencedCode).WithSpan(9, 21, 9, 25).WithArguments("C.M1()", " message.", "") + }, + fixedExpected: new[] { + // /0/Test0.cs(4,2): error CS7036: There is no argument given that corresponds to the required parameter 'message' of 'RequiresUnreferencedCodeAttribute.RequiresUnreferencedCodeAttribute(string)' + DiagnosticResult.CompilerError("CS7036").WithSpan(4, 2, 4, 28).WithArguments("message", "System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute.RequiresUnreferencedCodeAttribute(string)"), + }); + } + [Fact] public Task TestMakeGenericMethodUsage () { From 8d0db7d5bfd2dfc04b908ae024e5992b89e25426 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Wed, 18 Sep 2024 07:08:11 -0700 Subject: [PATCH 2/3] Update src/tools/illink/src/ILLink.CodeFix/BaseAttributeCodeFixProvider.cs Co-authored-by: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> --- .../illink/src/ILLink.CodeFix/BaseAttributeCodeFixProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/illink/src/ILLink.CodeFix/BaseAttributeCodeFixProvider.cs b/src/tools/illink/src/ILLink.CodeFix/BaseAttributeCodeFixProvider.cs index 83bef9fc34aff..b19961d951549 100644 --- a/src/tools/illink/src/ILLink.CodeFix/BaseAttributeCodeFixProvider.cs +++ b/src/tools/illink/src/ILLink.CodeFix/BaseAttributeCodeFixProvider.cs @@ -104,7 +104,7 @@ protected enum AttributeableParentTargets case PropertyDeclarationSyntax: case EventDeclarationSyntax: // If the attribute can be placed on a method but not directly on a property/event, we don't want to keep walking up - // the syntax tree to annotate the class. Instead the correct thing to do is to add accessor methods and annotatet those. + // the syntax tree to annotate the class. Instead the correct thing to do is to add accessor methods and annotate those. // The code fixer doesn't support doing this automatically, so return null to indicate that the attribute can't be added. if (targets.HasFlag (AttributeableParentTargets.MethodOrConstructor)) return null; From 452a970e1a5493a29672d93ee8fabf74dc1fe542 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Wed, 18 Sep 2024 14:12:31 +0000 Subject: [PATCH 3/3] PR feedback Include Class in All --- .../illink/src/ILLink.CodeFix/BaseAttributeCodeFixProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/illink/src/ILLink.CodeFix/BaseAttributeCodeFixProvider.cs b/src/tools/illink/src/ILLink.CodeFix/BaseAttributeCodeFixProvider.cs index b19961d951549..b58bd39ed19f8 100644 --- a/src/tools/illink/src/ILLink.CodeFix/BaseAttributeCodeFixProvider.cs +++ b/src/tools/illink/src/ILLink.CodeFix/BaseAttributeCodeFixProvider.cs @@ -87,7 +87,7 @@ protected enum AttributeableParentTargets Field = 0x0004, Event = 0x0008, Class = 0x0010, - All = MethodOrConstructor | Property | Field | Event + All = MethodOrConstructor | Property | Field | Event | Class } private static CSharpSyntaxNode? FindAttributableParent (SyntaxNode node, AttributeableParentTargets targets)