Skip to content

Commit d7e2c12

Browse files
authored
Analyzer warns when Requires... Attribute is on a static constructor (dotnet/linker#2455)
Adds warnings in the analyzer for IL2116 (RUC not allowed on static ctor) and adds IL2117 (RequiresDynamicCode not allowed on static ctor) and IL2118 (RequiresAssemblyFiles not allowed on static ctor). Fixes ExpectedWarning not having effects on constructors by adding a Visit override in the compilation tree walker in the test infra. Adds case in GetDisplayName to differentiate .cctor's and .ctor's. Linker doesn't seem to be checking ctors for calls to methods with RUC. This commit removes the check for the expected warnings in the linker until the bug is fixed. Commit migrated from dotnet/linker@2999a6a
1 parent 2e37dba commit d7e2c12

File tree

9 files changed

+63
-15
lines changed

9 files changed

+63
-15
lines changed

src/tools/illink/src/ILLink.RoslynAnalyzer/ISymbolExtensions.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,12 @@ public static string GetDisplayName (this ISymbol symbol)
5252
case IParameterSymbol parameterSymbol:
5353
sb.Append (parameterSymbol.Name);
5454
break;
55-
5655
default:
57-
sb.Append (symbol.ToDisplayString ());
56+
if (symbol.IsStaticConstructor ()) {
57+
sb.Append (symbol.ContainingType.ToDisplayString ());
58+
sb.Append ("..cctor()");
59+
} else
60+
sb.Append (symbol.ToDisplayString ());
5861
break;
5962
}
6063

src/tools/illink/src/ILLink.RoslynAnalyzer/RequiresAnalyzerBase.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public abstract class RequiresAnalyzerBase : DiagnosticAnalyzer
2525
private protected abstract DiagnosticDescriptor RequiresDiagnosticRule { get; }
2626

2727
private protected abstract DiagnosticDescriptor RequiresAttributeMismatch { get; }
28+
private protected abstract DiagnosticDescriptor RequiresOnStaticCtor { get; }
2829

2930
private protected virtual ImmutableArray<(Action<OperationAnalysisContext> Action, OperationKind[] OperationKind)> ExtraOperationActions { get; } = ImmutableArray<(Action<OperationAnalysisContext> Action, OperationKind[] OperationKind)>.Empty;
3031

@@ -43,6 +44,8 @@ public override void Initialize (AnalysisContext context)
4344
var incompatibleMembers = GetSpecialIncompatibleMembers (compilation);
4445
context.RegisterSymbolAction (symbolAnalysisContext => {
4546
var methodSymbol = (IMethodSymbol) symbolAnalysisContext.Symbol;
47+
if (methodSymbol.IsStaticConstructor () && methodSymbol.HasAttribute (RequiresAttributeName))
48+
ReportRequiresOnStaticCtorDiagnostic (symbolAnalysisContext, methodSymbol);
4649
CheckMatchingAttributesInOverrides (symbolAnalysisContext, methodSymbol);
4750
CheckAttributeInstantiation (symbolAnalysisContext, methodSymbol);
4851
foreach (var typeParameter in methodSymbol.TypeParameters)
@@ -332,6 +335,14 @@ private void ReportRequiresDiagnostic (OperationAnalysisContext operationContext
332335
url));
333336
}
334337

338+
private void ReportRequiresOnStaticCtorDiagnostic (SymbolAnalysisContext symbolAnalysisContext, IMethodSymbol ctor)
339+
{
340+
symbolAnalysisContext.ReportDiagnostic (Diagnostic.Create (
341+
RequiresOnStaticCtor,
342+
ctor.Locations[0],
343+
ctor.GetDisplayName ()));
344+
}
345+
335346
private void ReportMismatchInAttributesDiagnostic (SymbolAnalysisContext symbolAnalysisContext, ISymbol member, ISymbol baseMember, bool isInterface = false)
336347
{
337348
string message = MessageFormat.FormatRequiresAttributeMismatch (member.HasAttribute (RequiresAttributeName), isInterface, RequiresAttributeName, member.GetDisplayName (), baseMember.GetDisplayName ());

src/tools/illink/src/ILLink.RoslynAnalyzer/RequiresAssemblyFilesAnalyzer.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ public sealed class RequiresAssemblyFilesAnalyzer : RequiresAnalyzerBase
2626

2727
static readonly DiagnosticDescriptor s_requiresAssemblyFilesAttributeMismatch = DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.RequiresAssemblyFilesAttributeMismatch);
2828

29-
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create (s_locationRule, s_getFilesRule, s_requiresAssemblyFilesRule, s_requiresAssemblyFilesAttributeMismatch);
29+
static readonly DiagnosticDescriptor s_requiresAssemblyFilesOnStaticCtor = DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.RequiresAssemblyFilesOnStaticConstructor);
30+
31+
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create (s_locationRule, s_getFilesRule, s_requiresAssemblyFilesRule, s_requiresAssemblyFilesAttributeMismatch, s_requiresAssemblyFilesOnStaticCtor);
3032

3133
private protected override string RequiresAttributeName => RequiresAssemblyFilesAttribute;
3234

@@ -38,6 +40,8 @@ public sealed class RequiresAssemblyFilesAnalyzer : RequiresAnalyzerBase
3840

3941
private protected override DiagnosticDescriptor RequiresAttributeMismatch => s_requiresAssemblyFilesAttributeMismatch;
4042

43+
private protected override DiagnosticDescriptor RequiresOnStaticCtor => s_requiresAssemblyFilesOnStaticCtor;
44+
4145
protected override bool IsAnalyzerEnabled (AnalyzerOptions options, Compilation compilation)
4246
{
4347
var isSingleFileAnalyzerEnabled = options.GetMSBuildPropertyValue (MSBuildPropertyOptionNames.EnableSingleFileAnalyzer, compilation);

src/tools/illink/src/ILLink.RoslynAnalyzer/RequiresDynamicCodeAnalyzer.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@ public sealed class RequiresDynamicCodeAnalyzer : RequiresAnalyzerBase
1414
const string RequiresDynamicCodeAttribute = nameof (RequiresDynamicCodeAttribute);
1515
public const string FullyQualifiedRequiresDynamicCodeAttribute = "System.Diagnostics.CodeAnalysis." + RequiresDynamicCodeAttribute;
1616

17+
static readonly DiagnosticDescriptor s_requiresDynaicCodeOnStaticCtor = DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.RequiresDynamicCodeOnStaticConstructor);
1718
static readonly DiagnosticDescriptor s_requiresDynamicCodeRule = DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.RequiresDynamicCode);
1819
static readonly DiagnosticDescriptor s_requiresDynamicCodeAttributeMismatch = DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.RequiresDynamicCodeAttributeMismatch);
1920

2021
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
21-
ImmutableArray.Create (s_requiresDynamicCodeRule, s_requiresDynamicCodeAttributeMismatch);
22+
ImmutableArray.Create (s_requiresDynamicCodeRule, s_requiresDynamicCodeAttributeMismatch, s_requiresDynaicCodeOnStaticCtor);
2223

2324
private protected override string RequiresAttributeName => RequiresDynamicCodeAttribute;
2425

@@ -30,6 +31,8 @@ public sealed class RequiresDynamicCodeAnalyzer : RequiresAnalyzerBase
3031

3132
private protected override DiagnosticDescriptor RequiresAttributeMismatch => s_requiresDynamicCodeAttributeMismatch;
3233

34+
private protected override DiagnosticDescriptor RequiresOnStaticCtor => s_requiresDynaicCodeOnStaticCtor;
35+
3336
protected override bool IsAnalyzerEnabled (AnalyzerOptions options, Compilation compilation) =>
3437
options.IsMSBuildPropertyValueTrue (MSBuildPropertyOptionNames.EnableAOTAnalyzer, compilation);
3538

src/tools/illink/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ public sealed class RequiresUnreferencedCodeAnalyzer : RequiresAnalyzerBase
2222
new LocalizableResourceString (nameof (SharedStrings.DynamicTypeInvocationMessage), SharedStrings.ResourceManager, typeof (SharedStrings)));
2323
static readonly DiagnosticDescriptor s_makeGenericTypeRule = DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.MakeGenericType);
2424
static readonly DiagnosticDescriptor s_makeGenericMethodRule = DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.MakeGenericMethod);
25+
static readonly DiagnosticDescriptor s_requiresUnreferencedCodeOnStaticCtor = DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.RequiresUnreferencedCodeOnStaticConstructor);
2526

2627
static readonly DiagnosticDescriptor s_typeDerivesFromRucClassRule = DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.RequiresUnreferencedCodeOnBaseClass);
2728

@@ -53,7 +54,7 @@ private Action<SymbolAnalysisContext> typeDerivesFromRucBase {
5354
}
5455

5556
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
56-
ImmutableArray.Create (s_dynamicTypeInvocationRule, s_makeGenericMethodRule, s_makeGenericTypeRule, s_requiresUnreferencedCodeRule, s_requiresUnreferencedCodeAttributeMismatch, s_typeDerivesFromRucClassRule);
57+
ImmutableArray.Create (s_dynamicTypeInvocationRule, s_makeGenericMethodRule, s_makeGenericTypeRule, s_requiresUnreferencedCodeRule, s_requiresUnreferencedCodeAttributeMismatch, s_typeDerivesFromRucClassRule, s_requiresUnreferencedCodeOnStaticCtor);
5758

5859
private protected override string RequiresAttributeName => RequiresUnreferencedCodeAttribute;
5960

@@ -65,6 +66,8 @@ private Action<SymbolAnalysisContext> typeDerivesFromRucBase {
6566

6667
private protected override DiagnosticDescriptor RequiresAttributeMismatch => s_requiresUnreferencedCodeAttributeMismatch;
6768

69+
private protected override DiagnosticDescriptor RequiresOnStaticCtor => s_requiresUnreferencedCodeOnStaticCtor;
70+
6871
protected override bool IsAnalyzerEnabled (AnalyzerOptions options, Compilation compilation) =>
6972
options.IsMSBuildPropertyValueTrue (MSBuildPropertyOptionNames.EnableTrimAnalyzer, compilation);
7073

src/tools/illink/src/ILLink.Shared/DiagnosticId.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,10 +179,12 @@ public enum DiagnosticId
179179
AvoidAssemblyGetFilesInSingleFile = 3001,
180180
RequiresAssemblyFiles = 3002,
181181
RequiresAssemblyFilesAttributeMismatch = 3003,
182+
RequiresAssemblyFilesOnStaticConstructor = 3004,
182183

183184
// Dynamic code diagnostic ids.
184185
RequiresDynamicCode = 3050,
185-
RequiresDynamicCodeAttributeMismatch = 3051
186+
RequiresDynamicCodeAttributeMismatch = 3051,
187+
RequiresDynamicCodeOnStaticConstructor = 3052
186188
}
187189

188190
public static class DiagnosticIdExtensions

src/tools/illink/src/ILLink.Shared/SharedStrings.resx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1113,4 +1113,22 @@
11131113
<data name="InterfaceRequiresMismatchMessage" xml:space="preserve">
11141114
<value>Interface member '{2}' with '{0}' has an implementation member '{1}' without '{0}'</value>
11151115
</data>
1116-
</root>
1116+
<data name="RequiresOnBaseClassMessage" xml:space="preserve">
1117+
<value>Type '{0}' derives from '{1}' which has 'RequiresUnreferencedCodeAttribute'. {2}{3}</value>
1118+
</data>
1119+
<data name="RequiresOnBaseClassTitle" xml:space="preserve">
1120+
<value>Types that derive from a base class with 'RequiresUnreferencedCodeAttribute' need to explicitly use the 'RequiresUnreferencedCodeAttribute' or suppress this warning</value>
1121+
</data>
1122+
<data name="RequiresDynamicCodeOnStaticConstructorMessage" xml:space="preserve">
1123+
<value>'RequiresDynamicCodeAttribute' cannot be placed directly on static constructor '{0}'.</value>
1124+
</data>
1125+
<data name="RequiresDynamicCodeOnStaticConstructorTitle" xml:space="preserve">
1126+
<value>The use of 'RequiresDynamicCodeAttribute' on static constructors is disallowed since is a method not callable by the user, is only called by the runtime. Placing the attribute directly on the static constructor will have no effect, instead use 'RequiresUnreferencedCodeAttribute' on the type which will handle warning and silencing from the static constructor.</value>
1127+
</data>
1128+
<data name="RequiresAssemblyFilesOnStaticConstructorMessage" xml:space="preserve">
1129+
<value>'RequiresAssemblyFilesAttribute' cannot be placed directly on static constructor '{0}'.</value>
1130+
</data>
1131+
<data name="RequiresAssemblyFilesOnStaticConstructorTitle" xml:space="preserve">
1132+
<value>The use of 'RequiresAssemblyFilesAttribute' on static constructors is disallowed since is a method not callable by the user, is only called by the runtime. Placing the attribute directly on the static constructor will have no effect, instead use 'RequiresUnreferencedCodeAttribute' on the type which will handle warning and silencing from the static constructor.</value>
1133+
</data>
1134+
</root>

src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/TestChecker.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ public override void VisitClassDeclaration (ClassDeclarationSyntax node)
7171
CheckMember (node);
7272
}
7373

74+
public override void VisitConstructorDeclaration (ConstructorDeclarationSyntax node)
75+
{
76+
base.VisitConstructorDeclaration (node);
77+
CheckMember (node);
78+
}
79+
7480
public override void VisitInterfaceDeclaration (InterfaceDeclarationSyntax node)
7581
{
7682
base.VisitInterfaceDeclaration (node);

src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnStaticConstructor.cs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public static void Main ()
2828

2929
class StaticCtor
3030
{
31-
[ExpectedWarning ("IL2116", "StaticCtor..cctor()", ProducedBy = ProducedBy.Trimmer)]
31+
[ExpectedWarning ("IL2116", "StaticCtor..cctor()")]
3232
[RequiresUnreferencedCode ("Message for --TestStaticCtor--")]
3333
static StaticCtor ()
3434
{
@@ -42,7 +42,7 @@ static void TestStaticCctorRequires ()
4242

4343
class StaticCtorTriggeredByFieldAccess
4444
{
45-
[ExpectedWarning ("IL2116", "StaticCtorTriggeredByFieldAccess..cctor()", ProducedBy = ProducedBy.Trimmer)]
45+
[ExpectedWarning ("IL2116", "StaticCtorTriggeredByFieldAccess..cctor()")]
4646
[RequiresUnreferencedCode ("Message for --StaticCtorTriggeredByFieldAccess.Cctor--")]
4747
static StaticCtorTriggeredByFieldAccess ()
4848
{
@@ -59,9 +59,7 @@ static void TestStaticCtorMarkingIsTriggeredByFieldAccess ()
5959

6060
struct StaticCCtorForFieldAccess
6161
{
62-
// TODO: Analyzer still allows RUC/RAF on static constructor with no warning
63-
// https://github.com/dotnet/linker/issues/2347
64-
[ExpectedWarning ("IL2116", "StaticCCtorForFieldAccess..cctor()", ProducedBy = ProducedBy.Trimmer)]
62+
[ExpectedWarning ("IL2116", "StaticCCtorForFieldAccess..cctor()")]
6563
[RequiresUnreferencedCode ("Message for --StaticCCtorForFieldAccess.cctor--")]
6664
static StaticCCtorForFieldAccess () { }
6765

@@ -75,9 +73,9 @@ static void TestStaticCtorMarkingIsTriggeredByFieldAccessOnExplicitLayout ()
7573

7674
class StaticCtorTriggeredByMethodCall
7775
{
78-
// TODO: Analyzer still allows RUC/RAF on static constructor with no warning
79-
// https://github.com/dotnet/linker/issues/2347
80-
[ExpectedWarning ("IL2116", "StaticCtorTriggeredByMethodCall..cctor()", ProducedBy = ProducedBy.Trimmer)]
76+
[ExpectedWarning ("IL2116", "StaticCtorTriggeredByMethodCall..cctor()")]
77+
[ExpectedWarning ("IL3004", "StaticCtorTriggeredByMethodCall..cctor()", ProducedBy = ProducedBy.Analyzer)]
78+
[ExpectedWarning ("IL3052", "StaticCtorTriggeredByMethodCall..cctor()", ProducedBy = ProducedBy.Analyzer)]
8179
[RequiresUnreferencedCode ("Message for --StaticCtorTriggeredByMethodCall.Cctor--")]
8280
[RequiresAssemblyFiles ("Message for --StaticCtorTriggeredByMethodCall.Cctor--")]
8381
[RequiresDynamicCode ("Message for --StaticCtorTriggeredByMethodCall.Cctor--")]

0 commit comments

Comments
 (0)