Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2ff076e
Init for adding analyzer support to warn on Requires on cctor
jtschuster Dec 15, 2021
500a542
Adds an ExpectedWarning for IL3051 to DerivedClassWithAllWarnings
jtschuster Dec 14, 2021
c35ab2b
Update test to expect RAF and RDC on cctor warnings in analyzer only
jtschuster Dec 14, 2021
f9c57ee
Revert Cecil version Change
jtschuster Dec 15, 2021
db0084b
Add a visit to ConstructorDeclaration in syntax walker test infra
jtschuster Dec 15, 2021
e781898
Test case for ExpectWarning not working as expected in analyzer tests
jtschuster Dec 15, 2021
0835e40
Adds warning on static constructors to analyzers
jtschuster Dec 16, 2021
b95fc44
Remove RequiresDynamicCode ExpectedWarnings
jtschuster Dec 16, 2021
7c03ea5
reset Cecil to correct version
jtschuster Dec 16, 2021
645cee2
Renumber diagnostic IDs, use Roslyn member notation instead of IL not…
jtschuster Dec 16, 2021
a19ffb6
remove ..cctor from static constructor diagnostic messages
jtschuster Dec 16, 2021
da05d3f
uses 'static' modifiers in cctor diagnostic messages
jtschuster Dec 20, 2021
04270d6
uses ..cctor() notation in diagnostic messages
jtschuster Dec 20, 2021
2425bb2
Add test for ExpectedWarning on ctor and cctor
jtschuster Jan 6, 2022
54dbedc
Fix formatting
jtschuster Jan 6, 2022
cb9fc19
Accomodate for potential bug found in linker
jtschuster Jan 6, 2022
aa720df
fix shared string formatting
jtschuster Jan 6, 2022
b26e96a
Remove extra line in DiagnosticId.cs
jtschuster Jan 6, 2022
e6eb758
References the new test class in the test code so it's not trimmed
jtschuster Jan 6, 2022
66f39a8
Merge branch 'RequiresOnCctorWarns' of https://github.com/jtschuster/…
jtschuster Jan 6, 2022
c78a81a
Fix formatting
jtschuster Jan 6, 2022
011dcd2
Merge with main
jtschuster Jan 20, 2022
46f0c68
update cecil and fix extra line
jtschuster Jan 21, 2022
6bdad7a
Remove extra comma in DiagnosticId.cs
jtschuster Jan 21, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/ILLink.RoslynAnalyzer/ILLink.RoslynAnalyzer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="$(MicrosoftCodeAnalysisVersion)" PrivateAssets="all" />
</ItemGroup>

<ItemGroup>
<Reference Include="Mono.Cecil">
<HintPath>..\..\artifacts\bin\Mono.Cecil\Debug\netstandard2.0\Mono.Cecil.dll</HintPath>
Comment thread
tlakollo marked this conversation as resolved.
Outdated
</Reference>
</ItemGroup>

<Import Project="..\ILLink.Shared\ILLink.Shared.projitems" Label="Shared" />

</Project>
94 changes: 94 additions & 0 deletions src/ILLink.RoslynAnalyzer/IMethodSymbolExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// 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.Collections.Immutable;
using System.Text;
using Microsoft.CodeAnalysis;

namespace ILLink.RoslynAnalyzer
{
static class IMethodSymbolExtensions
{
public static bool IsSetter (this IMethodSymbol method)
{
return method.MethodKind == MethodKind.PropertySet;
}

public static bool IsGetter (this IMethodSymbol method)
{
return method.MethodKind == MethodKind.PropertyGet;
}
public static bool IsEventMethod (this IMethodSymbol method)
{
return method.MethodKind == MethodKind.EventAdd
|| method.MethodKind == MethodKind.EventRaise
|| method.MethodKind == MethodKind.EventRemove;
}
private static void PrependGenericParameters(ImmutableArray<ITypeParameterSymbol> genericParameters, System.Text.StringBuilder sb)
{
sb.Insert (0, '>').Insert (0, genericParameters[genericParameters.Length - 1]);
for (int i = genericParameters.Length - 2; i >= 0; i--)
sb.Insert (0, ',').Insert (0, genericParameters[i]);

sb.Insert (0, '<');
}

public static string GetDisplayName (this IMethodSymbol method)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The GetDisplayName method was made to match C# naming which is what Roslyn uses and most people are used to reading instead of IL namings. Therefore if there is a discrepancy between the way it's represented by the analyzer and linker is likely a fix in the GetDisplayName in the linker not an implementation on the analyzer

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worst case I think you can append directly the string ".cctor" at the end when displaying the diagnostic

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would expect most cases to already be covered by Roslyn. I could understand that we need to do something special for property getter/setter (and events), and possibly even .cctor, but other than that I would expect to simply call Roslyn to do this for us.

@jtschuster jtschuster Dec 16, 2021

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now, would it be best to use the ISymbolExtensions.GetDisplayName and have separate ExpectedWarning's for the analyzer and trimmer, and then later change the trimmer's GetDisplayName to match the Analyzer/Roslyn?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a check in the GetDisplayName in the linker that exists for ctors, but was missing cctors. Now they should look the same.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I was wrong is not a fix on the linker side, because if you fix in the linker then constructor and static constructor are represented equally, there is no way to differentiate them. So in the cases like the BeforeInitField test, I think things can get confusing. Same for RequiresInCompilerGeneratedCode tests, they get more confusing although you don't see the changes because we don't check for the caller name only messages in the attribute.
But yeah I think the best place to fix it is ISymbolExtensions.GetDisplayName an if statement before printing the method name check if the method symbol is a static constructor

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.cctors are weird - so we might need to add a special case unfortunately.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recently pushed a couple commits with different formats for the diagnostic messages. The first one includes the static modifier (e.g. static Mono.Linker.Tests.Cases.RequiresCapability.RequiresCapability.StaticCtor.StaticCtor()). The second uses the ..cctor() notation (e.g. Mono.Linker.Tests.Cases.RequiresCapability.RequiresCapability.StaticCtor..cctor()).

I think the static modifier notation would be best since it still differentiates the constructors and is more familiar to most people.

@tlakollo tlakollo Dec 20, 2021

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just created something in SharpLab to see how a warning would be printed

public class Foo
{
    static Foo (int a) {}
}

This generates error CS0132: 'Foo.Foo(int)': a static constructor must be parameterless also inserting the [Obsolete] attribute will generate a message [deprecated] Foo.Foo() so the compiler warnings at this moment don't show the modifiers nor have a clear distinction between static and instance constructors, so whatever decision we make is going to deviate from whatever the compiler normally prints.

I think is key for the trimmer to have some differentiation but I don't have a preference for which one to choose

  • The first option I have the concern that not a lot of people will even see the static modifier, it's at the beginning of the string and no other member will print it. Although I agree, I like more the readability of it.
  • The second option is a weird notation, but static constructors are weird, we don't even allow to annotate them. Static constructors are handled in a special way. So the fact that the name is weird might even be beneficial. The problem here would be that we already don't name instance constructors as .ctor()

In general I think I feel more inclined for the first approach

{
var sb = new System.Text.StringBuilder ();

// Match C# syntaxis name if setter or getter
if (method != null && (method.IsSetter() || method.IsGetter())) {
// Append property name
string name = method.IsSetter() ? string.Concat (method.Name, ".set") : string.Concat (method.Name, ".get");
sb.Append (name);
// Insert declaring type name and namespace
sb.Insert (0, '.').Insert (0, method.ContainingType.GetDisplayName ());
return sb.ToString ();
}

if (method != null && method.IsEventMethod ()) {
// Append event name
string name = method.MethodKind switch {
MethodKind.EventAdd => string.Concat (method.Name, ".add"),
MethodKind.EventRemove => string.Concat (method.Name, ".remove"),
MethodKind.EventRaise => string.Concat (method.Name, ".raise"),
_ => throw new NotSupportedException (),
};
sb.Append (name);
// Insert declaring type name and namespace
sb.Insert (0, '.').Insert (0, method.ContainingType.GetDisplayName ());
return sb.ToString ();
}

if (method.IsConstructor ())
sb.Append (".ctor");
else if (method.IsStaticConstructor ())
sb.Append (".cctor");

// Append parameters
sb.Append ("(");
if (method?.Parameters.Length > 0) {
for (int i = 0; i < method.Parameters.Length - 1; i++)
sb.Append (method.Parameters[i].GetDisplayName()).Append (',');

sb.Append (method.Parameters[method.Parameters.Length - 1].GetDisplayName ());
}

sb.Append (")");

// Insert generic parameters
if (method is not null && method.IsGenericMethod) {
PrependGenericParameters (method.TypeParameters, sb);
}

// Insert declaring type name and namespace
if (method is not null && method.ContainingType != null)
sb.Insert (0, '.').Insert (0, method.ContainingType.GetDisplayName ());

return sb.ToString ();
}
}
}
11 changes: 11 additions & 0 deletions src/ILLink.RoslynAnalyzer/RequiresAnalyzerBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Operations;
using Mono.Cecil;

namespace ILLink.RoslynAnalyzer
{
Expand All @@ -25,6 +26,7 @@ public abstract class RequiresAnalyzerBase : DiagnosticAnalyzer
private protected abstract DiagnosticDescriptor RequiresDiagnosticRule { get; }

private protected abstract DiagnosticDescriptor RequiresAttributeMismatch { get; }
private protected abstract DiagnosticDescriptor RequiresOnStaticCtor { get; }

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

Expand All @@ -43,6 +45,8 @@ public override void Initialize (AnalysisContext context)
var incompatibleMembers = GetSpecialIncompatibleMembers (compilation);
context.RegisterSymbolAction (symbolAnalysisContext => {
var methodSymbol = (IMethodSymbol) symbolAnalysisContext.Symbol;
if (methodSymbol.IsStaticConstructor() && methodSymbol.HasAttribute(RequiresAttributeName))
ReportRequiresOnStaticCtorDiagnostic(symbolAnalysisContext, methodSymbol);
CheckMatchingAttributesInOverrides (symbolAnalysisContext, methodSymbol);
CheckAttributeInstantiation (symbolAnalysisContext, methodSymbol);
foreach (var typeParameter in methodSymbol.TypeParameters)
Expand Down Expand Up @@ -332,6 +336,13 @@ private void ReportRequiresDiagnostic (OperationAnalysisContext operationContext
url));
}

private void ReportRequiresOnStaticCtorDiagnostic(SymbolAnalysisContext symbolAnalysisContext, IMethodSymbol ctor) {
symbolAnalysisContext.ReportDiagnostic (Diagnostic.Create (
RequiresOnStaticCtor,
ctor.Locations[0],
ctor.GetDisplayName() ));
}

private void ReportMismatchInAttributesDiagnostic (SymbolAnalysisContext symbolAnalysisContext, ISymbol member, ISymbol baseMember, bool isInterface = false)
{
string message = MessageFormat.FormatRequiresAttributeMismatch (member.HasAttribute (RequiresAttributeName), isInterface, RequiresAttributeName, member.GetDisplayName (), baseMember.GetDisplayName ());
Expand Down
6 changes: 5 additions & 1 deletion src/ILLink.RoslynAnalyzer/RequiresAssemblyFilesAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ public sealed class RequiresAssemblyFilesAnalyzer : RequiresAnalyzerBase

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

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

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create (s_locationRule, s_getFilesRule, s_requiresAssemblyFilesRule, s_requiresAssemblyFilesAttributeMismatch, s_requiresAssemblyFilesOnStaticCtor);

private protected override string RequiresAttributeName => RequiresAssemblyFilesAttribute;

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

private protected override DiagnosticDescriptor RequiresAttributeMismatch => s_requiresAssemblyFilesAttributeMismatch;

private protected override DiagnosticDescriptor RequiresOnStaticCtor => s_requiresAssemblyFilesOnStaticCtor;

protected override bool IsAnalyzerEnabled (AnalyzerOptions options, Compilation compilation)
{
var isSingleFileAnalyzerEnabled = options.GetMSBuildPropertyValue (MSBuildPropertyOptionNames.EnableSingleFileAnalyzer, compilation);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public sealed class RequiresUnreferencedCodeAnalyzer : RequiresAnalyzerBase
new LocalizableResourceString (nameof (SharedStrings.DynamicTypeInvocationMessage), SharedStrings.ResourceManager, typeof (SharedStrings)));
static readonly DiagnosticDescriptor s_makeGenericTypeRule = DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.MakeGenericType);
static readonly DiagnosticDescriptor s_makeGenericMethodRule = DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.MakeGenericMethod);
static readonly DiagnosticDescriptor s_requiresUnreferencedCodeOnStaticCtor = DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.RequiresUnreferencedCodeOnStaticConstructor);

static readonly DiagnosticDescriptor s_typeDerivesFromRucClassRule = DiagnosticDescriptors.GetDiagnosticDescriptor (DiagnosticId.RequiresOnBaseClass);

Expand Down Expand Up @@ -53,7 +54,7 @@ private Action<SymbolAnalysisContext> typeDerivesFromRucBase {
}

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
ImmutableArray.Create (s_dynamicTypeInvocationRule, s_makeGenericMethodRule, s_makeGenericTypeRule, s_requiresUnreferencedCodeRule, s_requiresUnreferencedCodeAttributeMismatch, s_typeDerivesFromRucClassRule);
ImmutableArray.Create (s_dynamicTypeInvocationRule, s_makeGenericMethodRule, s_makeGenericTypeRule, s_requiresUnreferencedCodeRule, s_requiresUnreferencedCodeAttributeMismatch, s_typeDerivesFromRucClassRule, s_requiresUnreferencedCodeOnStaticCtor);

private protected override string RequiresAttributeName => RequiresUnreferencedCodeAttribute;

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

private protected override DiagnosticDescriptor RequiresAttributeMismatch => s_requiresUnreferencedCodeAttributeMismatch;

private protected override DiagnosticDescriptor RequiresOnStaticCtor => s_requiresUnreferencedCodeOnStaticCtor;

protected override bool IsAnalyzerEnabled (AnalyzerOptions options, Compilation compilation) =>
options.IsMSBuildPropertyValueTrue (MSBuildPropertyOptionNames.EnableTrimAnalyzer, compilation);

Expand Down
2 changes: 2 additions & 0 deletions src/ILLink.Shared/DiagnosticId.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ public enum DiagnosticId
MakeGenericMethod = 2060,
RequiresOnBaseClass = 2109,
RequiresUnreferencedCodeOnStaticConstructor = 2116,
RequiresDynamicCodeOnStaticConstructor = 2117,
RequiresAssemblyFilesOnStaticConstructor = 2118,

Comment thread
tlakollo marked this conversation as resolved.
// Single-file diagnostic ids.
AvoidAssemblyLocationInSingleFile = 3000,
Expand Down
11 changes: 11 additions & 0 deletions src/ILLink.Shared/SharedStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,17 @@
<data name="RequiresUnreferencedCodeOnStaticConstructorTitle" xml:space="preserve">
<value>The use of 'RequiresUnreferencedCodeAttribute' 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>
</data>
<data name="RequiresDynamicCodeOnStaticConstructorMessage" xml:space="preserve">
<value>'RequiresDynamicCodeAttribute' cannot be placed directly on static constructor '{0}'.</value>
</data>
<data name="RequiresDynamicCodeOnStaticConstructorTitle" xml:space="preserve">
<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>
</data><data name="RequiresAssemblyFilesOnStaticConstructorMessage" xml:space="preserve">
Comment thread
tlakollo marked this conversation as resolved.
Outdated
<value>'RequiresAssemblyFilesAttribute' cannot be placed directly on static constructor '{0}'.</value>
</data>
<data name="RequiresAssemblyFilesOnStaticConstructorTitle" xml:space="preserve">
<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>
</data>
<data name="CorrectnessOfCOMCannotBeGuaranteedMessage" xml:space="preserve">
<value>P/invoke method '{0}' declares a parameter with COM marshalling. Correctness of COM interop cannot be guaranteed after trimming. Interfaces and interface members might be removed.</value>
</data>
Expand Down
6 changes: 6 additions & 0 deletions test/ILLink.RoslynAnalyzer.Tests/TestChecker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ public override void VisitClassDeclaration (ClassDeclarationSyntax node)
CheckMember (node);
}

public override void VisitConstructorDeclaration (ConstructorDeclarationSyntax node)

@jtschuster jtschuster Jan 4, 2022

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix for #2446 to make sure we account for ExpectedWarning on constructors.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a test for this? Something that Inside the static constructor calls a method annotated with RUC and just verify that we can use the ExpectedWarning attribute

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good thing you mentioned this, I think I found a bug that the linker doesn't check in constructors for calls to methods with RUC. The test class for this is WarningsInCtor in RequiresCapability.cs. It calls a method annotated with RUC inside the constructors, but the linker doesn't seem to be producing a warning. I filed #2484 to track the issue.

@tlakollo tlakollo Jan 6, 2022

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's because nothing references that code in the main method, since it's not referenced Trimmer will get rid of that piece of code. Notice that one of the differences between analyzer and trimmer is that analyzer will produce diagnostics even for code that will be trimmed.

{
base.VisitConstructorDeclaration (node);
CheckMember (node);
}

public override void VisitInterfaceDeclaration (InterfaceDeclarationSyntax node)
{
base.VisitInterfaceDeclaration (node);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ static void TestTypeWhichOverridesVirtualPropertyRequires ()

class StaticCtor
{
[ExpectedWarning ("IL2116", "StaticCtor..cctor()", ProducedBy = ProducedBy.Trimmer)]
[ExpectedWarning ("IL2116", "StaticCtor..cctor()")]
[RequiresUnreferencedCode ("Message for --TestStaticCtor--")]
static StaticCtor ()
{
Expand All @@ -403,7 +403,7 @@ static void TestStaticCctorRequires ()

class StaticCtorTriggeredByFieldAccess
{
[ExpectedWarning ("IL2116", "StaticCtorTriggeredByFieldAccess..cctor()", ProducedBy = ProducedBy.Trimmer)]
[ExpectedWarning ("IL2116", "StaticCtorTriggeredByFieldAccess..cctor()")]
[RequiresUnreferencedCode ("Message for --StaticCtorTriggeredByFieldAccess.Cctor--")]
static StaticCtorTriggeredByFieldAccess ()
{
Expand All @@ -420,9 +420,7 @@ static void TestStaticCtorMarkingIsTriggeredByFieldAccess ()

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

Expand Down Expand Up @@ -458,9 +456,8 @@ static void TestTypeIsBeforeFieldInit ()

class StaticCtorTriggeredByMethodCall
{
// TODO: Analyzer still allows RUC/RAF on static constructor with no warning
// https://github.com/dotnet/linker/issues/2347
[ExpectedWarning ("IL2116", "StaticCtorTriggeredByMethodCall..cctor()", ProducedBy = ProducedBy.Trimmer)]
[ExpectedWarning ("IL2116", "StaticCtorTriggeredByMethodCall..cctor()")]
[ExpectedWarning ("IL2118", "StaticCtorTriggeredByMethodCall..cctor()", ProducedBy = ProducedBy.Analyzer)]
[RequiresUnreferencedCode ("Message for --StaticCtorTriggeredByMethodCall.Cctor--")]
[RequiresAssemblyFiles ("Message for --StaticCtorTriggeredByMethodCall.Cctor--")]
static StaticCtorTriggeredByMethodCall ()
Expand Down Expand Up @@ -614,7 +611,7 @@ class WarnIfRequiresOnStaticConstructor
{
class ClassWithRequiresOnStaticConstructor
{
[ExpectedWarning ("IL2116", ProducedBy = ProducedBy.Trimmer)]
[ExpectedWarning ("IL2116")]
[RequiresUnreferencedCode ("This attribute shouldn't be allowed")]
static ClassWithRequiresOnStaticConstructor () { }
}
Expand Down