diff --git a/docs/error-codes.md b/docs/error-codes.md index bd30cae42781..8df6aec4e562 100644 --- a/docs/error-codes.md +++ b/docs/error-codes.md @@ -502,9 +502,9 @@ the error code. For example: ``` -#### `IL2026` Trim analysis: Using method 'method' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. [message]. [url] +#### `IL2026` Trim analysis: Using member 'method' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. [message]. [url] -- The linker found a call to a method which is annotated with `RequiresUnreferencedCodeAttribute` which can break functionality of a trimmed application. +- The linker found a call to a member which is annotated with `RequiresUnreferencedCodeAttribute` which can break functionality of a trimmed application. ```C# [RequiresUnreferencedCode("Use 'MethodFriendlyToTrimming' instead", Url="http://help/unreferencedcode")] @@ -514,7 +514,7 @@ the error code. For example: void TestMethod() { - // IL2026: Using method 'MethodWithUnreferencedCodeUsage' which has 'RequiresUnreferencedCodeAttribute' + // IL2026: Using member 'MethodWithUnreferencedCodeUsage' which has 'RequiresUnreferencedCodeAttribute' // can break functionality when trimming application code. Use 'MethodFriendlyToTrimming' instead. http://help/unreferencedcode MethodWithUnreferencedCodeUsage(); } @@ -1798,7 +1798,7 @@ void TestMethod() } ``` -#### `IL2115 ` Trim analysis: 'DynamicallyAccessedMembersAttribute' on 'type' or one of its base types references 'member' which has 'DynamicallyAccessedMembersAttribute' requirements. +#### `IL2115` Trim analysis: 'DynamicallyAccessedMembersAttribute' on 'type' or one of its base types references 'member' which has 'DynamicallyAccessedMembersAttribute' requirements. - A type is annotated with `DynamicallyAccessedMembersAttribute` indicating that the type may dynamically access some members declared on the type or its derived types. This instructs the trimmer to keep the specified members, but a member of one of the base or interface types is annotated with `DynamicallyAccessedMembersAttribute` which can not be statically verified. The `DynamicallyAccessedMembersAttribute` annotation may be directly on the type, or implied by an annotation on one of its base or interface types. This warning originates from the type which has `DynamicallyAccessedMembersAttribute` requirements. @@ -1815,6 +1815,18 @@ void TestMethod() } ``` +#### `IL2116` Trim analysis: 'RequiresUnreferencedCodeAttribute' cannot be placed directly on static constructor 'static constructor', consider placing 'RequiresUnreferencedCodeAttribute' on the type declaration instead. + +- 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. + + ```C# + public class MyClass { + // Trim analysis warning IL2115: 'RequiresUnreferencedCodeAttribute' cannot be placed directly on static constructor 'MyClass..cctor()', consider placing 'RequiresUnreferencedCodeAttribute' on the type declaration instead. + [RequiresUnreferencedCode("Dangerous")] + static MyClass () { } + } + ``` + ## Single-File Warning Codes #### `IL3000`: 'member' 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' diff --git a/src/ILLink.Shared/DiagnosticId.cs b/src/ILLink.Shared/DiagnosticId.cs index 0dd728ed38bd..f586a6b43761 100644 --- a/src/ILLink.Shared/DiagnosticId.cs +++ b/src/ILLink.Shared/DiagnosticId.cs @@ -5,6 +5,7 @@ public enum DiagnosticId // Linker diagnostic ids. RequiresUnreferencedCode = 2026, RequiresUnreferencedCodeAttributeMismatch = 2046, + RequiresUnreferencedCodeOnStaticConstructor = 2116, // Single-file diagnostic ids. AvoidAssemblyLocationInSingleFile = 3000, diff --git a/src/ILLink.Shared/SharedStrings.resx b/src/ILLink.Shared/SharedStrings.resx index 4372e511c75f..70b50dbbf443 100644 --- a/src/ILLink.Shared/SharedStrings.resx +++ b/src/ILLink.Shared/SharedStrings.resx @@ -118,10 +118,10 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Methods annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code + Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code - Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code.{1}{2} + Using member '{0}' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code.{1}{2} Avoid accessing Assembly file path when publishing as a single file @@ -171,4 +171,10 @@ Using dynamic types might cause types or members to be removed by trimmer. + + 'RequiresUnreferencedCodeAttribute' cannot be placed directly on static constructor '{0}', consider placing 'RequiresUnreferencedCodeAttribute' on the type declaration instead. + + + 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. + \ No newline at end of file diff --git a/src/linker/Linker.Steps/MarkStep.cs b/src/linker/Linker.Steps/MarkStep.cs index fdf4453cb395..5d34c9c57e36 100644 --- a/src/linker/Linker.Steps/MarkStep.cs +++ b/src/linker/Linker.Steps/MarkStep.cs @@ -1542,13 +1542,13 @@ static bool IsDeclaredWithinType (IMemberDefinition member, TypeDefinition type) try { var origin = _scopeStack.CurrentScope.Origin; - if (member is MethodDefinition method && Annotations.DoesMethodRequireUnreferencedCode (method, out RequiresUnreferencedCodeAttribute attribute)) { + if (Annotations.DoesMemberRequireUnreferencedCode (member, out RequiresUnreferencedCodeAttribute requiresUnreferencedCodeAttribute)) { var message = string.Format ( "'DynamicallyAccessedMembersAttribute' on '{0}' or one of its base types references '{1}' which requires unreferenced code.{2}{3}", type.GetDisplayName (), - method.GetDisplayName (), - MessageFormat.FormatRequiresAttributeMessageArg (attribute.Message), - MessageFormat.FormatRequiresAttributeMessageArg (attribute.Url)); + ((MemberReference) member).GetDisplayName (), // The cast is valid since it has to be a method or field + MessageFormat.FormatRequiresAttributeMessageArg (requiresUnreferencedCodeAttribute.Message), + MessageFormat.FormatRequiresAttributeMessageArg (requiresUnreferencedCodeAttribute.Url)); var code = reportOnMember ? 2112 : 2113; _context.LogWarning (message, code, origin, MessageSubCategory.TrimAnalysis); } @@ -1579,6 +1579,11 @@ void MarkField (FieldDefinition field, in DependencyInfo reason) Annotations.Mark (field, reason); } + if (reason.Kind != DependencyKind.DynamicallyAccessedMemberOnType && + Annotations.DoesFieldRequireUnreferencedCode (field, out RequiresUnreferencedCodeAttribute requiresUnreferencedCodeAttribute) && + !ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode ()) + ReportRequiresUnreferencedCode (field.GetDisplayName (), requiresUnreferencedCodeAttribute, _scopeStack.CurrentScope.Origin); + switch (reason.Kind) { case DependencyKind.AccessedViaReflection: case DependencyKind.DynamicDependency: @@ -2766,7 +2771,7 @@ void ProcessAnalysisAnnotationsForMethod (MethodDefinition method, DependencyKin switch (dependencyKind) { // DirectCall, VirtualCall and NewObj are handled by ReflectionMethodBodyScanner // This is necessary since the ReflectionMethodBodyScanner has intrinsic handling for some - // of the annotated methods annotated (for example Type.GetType) + // of the annotated methods (for example Type.GetType) // and it knows when it's OK and when it needs a warning. In this place we don't know // and would have to warn every time. case DependencyKind.DirectCall: @@ -2901,10 +2906,9 @@ internal void CheckAndReportRequiresUnreferencedCode (MethodDefinition method) private void ReportRequiresUnreferencedCode (string displayName, RequiresUnreferencedCodeAttribute requiresUnreferencedCode, MessageOrigin currentOrigin) { - string formatString = SharedStrings.RequiresUnreferencedCodeMessage; string arg1 = MessageFormat.FormatRequiresAttributeMessageArg (requiresUnreferencedCode.Message); string arg2 = MessageFormat.FormatRequiresAttributeUrlArg (requiresUnreferencedCode.Url); - string message = string.Format (formatString, displayName, arg1, arg2); + string message = string.Format (SharedStrings.RequiresUnreferencedCodeMessage, displayName, arg1, arg2); _context.LogWarning (message, 2026, currentOrigin, MessageSubCategory.TrimAnalysis); } @@ -2930,7 +2934,6 @@ protected virtual void ProcessMethod (MethodDefinition method, in DependencyInfo if (!_methodReasons.Contains (reason.Kind)) throw new InternalErrorException ($"Unsupported method dependency {reason.Kind}"); #endif - _scopeStack.AssertIsEmpty (); using var parentScope = _scopeStack.PushScope (scope); using var methodScope = _scopeStack.PushScope (new MessageOrigin (method)); @@ -2977,7 +2980,8 @@ protected virtual void ProcessMethod (MethodDefinition method, in DependencyInfo if (method.IsInstanceConstructor ()) { MarkRequirementsForInstantiatedTypes (method.DeclaringType); Tracer.AddDirectDependency (method.DeclaringType, new DependencyInfo (DependencyKind.InstantiatedByCtor, method), marked: false); - } + } else if (method.IsStaticConstructor () && Annotations.HasLinkerAttribute (method)) + _context.LogWarning (new DiagnosticString (DiagnosticId.RequiresUnreferencedCodeOnStaticConstructor).GetMessage (method.GetDisplayName ()), (int) DiagnosticId.RequiresUnreferencedCodeOnStaticConstructor, _scopeStack.CurrentScope.Origin, MessageSubCategory.TrimAnalysis); if (method.IsConstructor) { if (!Annotations.ProcessSatelliteAssemblies && KnownMembers.IsSatelliteAssemblyMarker (method)) diff --git a/src/linker/Linker/Annotations.cs b/src/linker/Linker/Annotations.cs index 37deab5f344e..588d83fb09c7 100644 --- a/src/linker/Linker/Annotations.cs +++ b/src/linker/Linker/Annotations.cs @@ -598,6 +598,10 @@ public bool TryGetLinkerAttribute (IMemberDefinition member, out T attribute) /// and .ctors are reported as requiring unreferenced code when the declaring type has RUC on it. internal bool DoesMethodRequireUnreferencedCode (MethodDefinition method, out RequiresUnreferencedCodeAttribute attribute) { + if (method.IsStaticConstructor ()) { + attribute = null; + return false; + } if (TryGetLinkerAttribute (method, out attribute)) return true; @@ -624,6 +628,26 @@ internal bool IsMethodInRequiresUnreferencedCodeScope (MethodDefinition method) return false; } + internal bool DoesFieldRequireUnreferencedCode (FieldDefinition field, out RequiresUnreferencedCodeAttribute attribute) + { + if (!field.IsStatic || field.DeclaringType is null) { + attribute = null; + return false; + } + + return TryGetLinkerAttribute (field.DeclaringType, out attribute); + } + + internal bool DoesMemberRequireUnreferencedCode (IMemberDefinition member, out RequiresUnreferencedCodeAttribute attribute) + { + attribute = null; + return member switch { + MethodDefinition method => DoesMethodRequireUnreferencedCode (method, out attribute), + FieldDefinition field => DoesFieldRequireUnreferencedCode (field, out attribute), + _ => false + }; + } + public void EnqueueVirtualMethod (MethodDefinition method) { if (!method.IsVirtual) diff --git a/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs b/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs index 763e57764bd0..af9cf087aa01 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs @@ -68,7 +68,7 @@ class C int M2() => M1(); }"; return VerifyRequiresUnreferencedCodeAnalyzer (TestRequiresWithMessageOnlyOnMethod, - // (8,17): warning IL2026: Using method 'C.M1()' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. message. + // (8,17): warning IL2026: Using member 'C.M1()' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. message. VerifyCS.Diagnostic (DiagnosticId.RequiresUnreferencedCode).WithSpan (8, 17, 8, 21).WithArguments ("C.M1()", " message.", "")); } @@ -136,13 +136,13 @@ public class F "; 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. + // /0/Test0.cs(9,17): warning IL2026: Using member 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message. VerifyCS.Diagnostic (DiagnosticId.RequiresUnreferencedCode).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. + // /0/Test0.cs(13,27): warning IL2026: Using member 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message. VerifyCS.Diagnostic(DiagnosticId.RequiresUnreferencedCode).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. + // /0/Test0.cs(17,31): warning IL2026: Using member 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message. VerifyCS.Diagnostic (DiagnosticId.RequiresUnreferencedCode).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. + // /0/Test0.cs(24,31): warning IL2026: Using member 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message. VerifyCS.Diagnostic (DiagnosticId.RequiresUnreferencedCode).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)' @@ -188,7 +188,7 @@ Action M2() 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. + // /0/Test0.cs(12,22): warning IL2026: Using member 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message. VerifyCS.Diagnostic(DiagnosticId.RequiresUnreferencedCode).WithSpan(12, 22, 12, 26).WithArguments("C.M1()", " message.", "") }, fixedExpected: Array.Empty ()); @@ -233,7 +233,7 @@ Action M2() 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. + // /0/Test0.cs(12,28): warning IL2026: Using member 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message. VerifyCS.Diagnostic(DiagnosticId.RequiresUnreferencedCode).WithSpan(12, 28, 12, 32).WithArguments("C.M1()", " message.", "") }, fixedExpected: Array.Empty (), @@ -274,7 +274,7 @@ public class C 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. + // /0/Test0.cs(10,19): warning IL2026: Using member 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message. VerifyCS.Diagnostic(DiagnosticId.RequiresUnreferencedCode).WithSpan(10, 19, 10, 23).WithArguments("C.M1()", " message.", "") }, fixedExpected: new[] { @@ -313,11 +313,11 @@ public class C 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. + // /0/Test0.cs(10,15): warning IL2026: Using member 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message. VerifyCS.Diagnostic(DiagnosticId.RequiresUnreferencedCode).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. + // /0/Test0.cs(10,15): warning IL2026: Using member 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message. VerifyCS.Diagnostic(DiagnosticId.RequiresUnreferencedCode).WithSpan(10, 15, 10, 19).WithArguments("C.M1()", " message.", "") }); } @@ -340,7 +340,7 @@ static void RequiresWithMessageAndUrl () } }"; return VerifyRequiresUnreferencedCodeAnalyzer (MessageAndUrlOnMethod, - // (8,3): warning IL2026: Using method 'C.RequiresWithMessageAndUrl()' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. Message for --RequiresWithMessageAndUrl--. https://helpurl + // (8,3): warning IL2026: Using member 'C.RequiresWithMessageAndUrl()' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. Message for --RequiresWithMessageAndUrl--. https://helpurl VerifyCS.Diagnostic (DiagnosticId.RequiresUnreferencedCode).WithSpan (8, 3, 8, 31).WithArguments ("C.RequiresWithMessageAndUrl()", " Message for --RequiresWithMessageAndUrl--.", " https://helpurl") ); } @@ -394,7 +394,7 @@ static int PropertyRequires { } }"; 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 member 'C.PropertyRequires.get' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. Message for --getter PropertyRequires--. VerifyCS.Diagnostic (DiagnosticId.RequiresUnreferencedCode).WithSpan (8, 7, 8, 23).WithArguments ("C.PropertyRequires.get", " Message for --getter PropertyRequires--.", "") ); } @@ -418,7 +418,7 @@ static int PropertyRequires { } }"; return VerifyRequiresUnreferencedCodeAnalyzer (PropertyRequires, - // (8,3): warning IL2026: Using method 'C.PropertyRequires.set' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. Message for --setter PropertyRequires--. + // (8,3): warning IL2026: Using member 'C.PropertyRequires.set' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. Message for --setter PropertyRequires--. VerifyCS.Diagnostic (DiagnosticId.RequiresUnreferencedCode).WithSpan (8, 3, 8, 19).WithArguments ("C.PropertyRequires.set", " Message for --setter PropertyRequires--.", "") ); } @@ -442,7 +442,7 @@ static void TestStaticCctorRequiresUnreferencedCode () } }"; return VerifyRequiresUnreferencedCodeAnalyzer (src, - // (13,7): warning IL2026: Using method 'StaticCtor.StaticCtor()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. Message for --TestStaticCtor--. + // (13,7): warning IL2026: Using member 'StaticCtor.StaticCtor()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. Message for --TestStaticCtor--. VerifyCS.Diagnostic (DiagnosticId.RequiresUnreferencedCode).WithSpan (13, 7, 13, 24).WithArguments ("StaticCtor.StaticCtor()", " Message for --TestStaticCtor--.", "") ); } @@ -471,7 +471,7 @@ static void TestStaticCtorMarkingIsTriggeredByFieldAccess () } }"; return VerifyRequiresUnreferencedCodeAnalyzer (src, - // (18,11): warning IL2026: Using method 'StaticCtorTriggeredByFieldAccess.StaticCtorTriggeredByFieldAccess()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. Message for --StaticCtorTriggeredByFieldAccess.Cctor--. + // (18,11): warning IL2026: Using member 'StaticCtorTriggeredByFieldAccess.StaticCtorTriggeredByFieldAccess()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. Message for --StaticCtorTriggeredByFieldAccess.Cctor--. VerifyCS.Diagnostic (DiagnosticId.RequiresUnreferencedCode).WithSpan (18, 11, 18, 49).WithArguments ("StaticCtorTriggeredByFieldAccess.StaticCtorTriggeredByFieldAccess()", " Message for --StaticCtorTriggeredByFieldAccess.Cctor--.", "") ); } @@ -503,9 +503,9 @@ static void TestStaticCtorTriggeredByMethodCall () } }"; return VerifyRequiresUnreferencedCodeAnalyzer (src, - // (21,3): warning IL2026: Using method 'StaticCtorTriggeredByMethodCall.TriggerStaticCtorMarking()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. Message for --StaticCtorTriggeredByMethodCall.TriggerStaticCtorMarking--. + // (21,3): warning IL2026: Using member 'StaticCtorTriggeredByMethodCall.TriggerStaticCtorMarking()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. Message for --StaticCtorTriggeredByMethodCall.TriggerStaticCtorMarking--. VerifyCS.Diagnostic (DiagnosticId.RequiresUnreferencedCode).WithSpan (21, 3, 21, 69).WithArguments ("StaticCtorTriggeredByMethodCall.TriggerStaticCtorMarking()", " Message for --StaticCtorTriggeredByMethodCall.TriggerStaticCtorMarking--.", ""), - // (21,3): warning IL2026: Using method 'StaticCtorTriggeredByMethodCall.StaticCtorTriggeredByMethodCall()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. Message for --StaticCtorTriggeredByMethodCall.Cctor--. + // (21,3): warning IL2026: Using member 'StaticCtorTriggeredByMethodCall.StaticCtorTriggeredByMethodCall()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. Message for --StaticCtorTriggeredByMethodCall.Cctor--. VerifyCS.Diagnostic (DiagnosticId.RequiresUnreferencedCode).WithSpan (21, 3, 21, 41).WithArguments ("StaticCtorTriggeredByMethodCall.StaticCtorTriggeredByMethodCall()", " Message for --StaticCtorTriggeredByMethodCall.Cctor--.", "") ); } @@ -532,7 +532,7 @@ static void TestTypeIsBeforeFieldInit () } }"; return VerifyRequiresUnreferencedCodeAnalyzer (TypeIsBeforeFieldInit, - // (8,29): warning IL2026: Using method 'C.TypeIsBeforeFieldInit.AnnotatedMethod()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. Message from --TypeIsBeforeFieldInit.AnnotatedMethod--. + // (8,29): warning IL2026: Using member 'C.TypeIsBeforeFieldInit.AnnotatedMethod()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. Message from --TypeIsBeforeFieldInit.AnnotatedMethod--. VerifyCS.Diagnostic (DiagnosticId.RequiresUnreferencedCode).WithSpan (8, 29, 8, 47).WithArguments ("C.TypeIsBeforeFieldInit.AnnotatedMethod()", " Message from --TypeIsBeforeFieldInit.AnnotatedMethod--.", "") ); } @@ -556,7 +556,7 @@ public static C InitC() { }"; return VerifyRequiresUnreferencedCodeAnalyzer (src, - // (6,50): warning IL2026: Using method 'C.InitC()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. Message from --C.InitC--. + // (6,50): warning IL2026: Using member 'C.InitC()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. Message from --C.InitC--. VerifyCS.Diagnostic (DiagnosticId.RequiresUnreferencedCode).WithSpan (6, 50, 6, 55).WithArguments ("C.InitC()", " Message from --C.InitC--.", "")); } @@ -578,9 +578,9 @@ public static void M2() }"; return VerifyRequiresUnreferencedCodeAnalyzer (src, - // (10,20): warning IL2026: Using method 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. Message from --C.M1--. + // (10,20): warning IL2026: Using member 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. Message from --C.M1--. VerifyCS.Diagnostic (DiagnosticId.RequiresUnreferencedCode).WithSpan (10, 20, 10, 22).WithArguments ("C.M1()", " Message from --C.M1--.", ""), - // (11,26): warning IL2026: Using method 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. Message from --C.M1--. + // (11,26): warning IL2026: Using member 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. Message from --C.M1--. VerifyCS.Diagnostic (DiagnosticId.RequiresUnreferencedCode).WithSpan (11, 26, 11, 30).WithArguments ("C.M1()", " Message from --C.M1--.", "")); } diff --git a/test/ILLink.RoslynAnalyzer.Tests/UnconditionalSuppressMessageCodeFixTests.cs b/test/ILLink.RoslynAnalyzer.Tests/UnconditionalSuppressMessageCodeFixTests.cs index 7d4aa24a6353..8aaa1560c813 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/UnconditionalSuppressMessageCodeFixTests.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/UnconditionalSuppressMessageCodeFixTests.cs @@ -81,14 +81,14 @@ 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 = """")] + [UnconditionalSuppressMessage(""Trimming"", ""IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code"", Justification = """")] 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. + // /0/Test0.cs(7,17): warning IL2026: Using member 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message. VerifyCSUSMwithRUC.Diagnostic (DiagnosticId.RequiresUnreferencedCode).WithSpan (7, 17, 7, 21).WithArguments ("C.M1()", " message.", ""), }, fixedExpected: Array.Empty ()); @@ -118,7 +118,7 @@ public class C 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. + // /0/Test0.cs(7,17): warning IL2026: Using member 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message. VerifyCSUSMwithRAF.Diagnostic (DiagnosticId.RequiresAssemblyFiles).WithSpan (7, 17, 7, 21).WithArguments ("C.M1()", " message.", "") }, fixedExpected: Array.Empty ()); @@ -190,14 +190,14 @@ 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 = """")] + [UnconditionalSuppressMessage(""Trimming"", ""IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code"", Justification = """")] 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. + // /0/Test0.cs(10,15): warning IL2026: Using member 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message. VerifyCSUSMwithRUC.Diagnostic (DiagnosticId.RequiresUnreferencedCode).WithSpan(10, 15, 10, 19).WithArguments("C.M1()", " message.", "") }, fixedExpected: Array.Empty ()); @@ -274,7 +274,7 @@ public class C 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 = """")] void Wrapper () => M1(); + [global::System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute(""Trimming"", ""IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code"", Justification = """")] void Wrapper () => M1(); return Wrapper; } }"; @@ -283,7 +283,7 @@ Action M2() 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. + // /0/Test0.cs(12,28): warning IL2026: Using member 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message. VerifyCSUSMwithRUC.Diagnostic (DiagnosticId.RequiresUnreferencedCode).WithSpan(12, 28, 12, 32).WithArguments("C.M1()", " message.", "") }, fixedExpected: Array.Empty ()); @@ -312,14 +312,14 @@ 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 = """")] + [UnconditionalSuppressMessage(""Trimming"", ""IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code"", Justification = """")] 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. + // /0/Test0.cs(10,15): warning IL2026: Using member 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message. VerifyCSUSMwithRUC.Diagnostic (DiagnosticId.RequiresUnreferencedCode).WithSpan(10, 20, 10, 24).WithArguments("C.M1()", " message.", "") }, fixedExpected: Array.Empty ()); @@ -355,7 +355,7 @@ 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 = """")] + [UnconditionalSuppressMessage(""Trimming"", ""IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code"", Justification = """")] public event EventHandler E1 { add @@ -369,7 +369,7 @@ public event EventHandler E1 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. + // /0/Test0.cs(14,21): warning IL2026: Using member 'C.M1()' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. message. VerifyCSUSMwithRUC.Diagnostic (DiagnosticId.RequiresUnreferencedCode).WithSpan(14, 21, 14, 25).WithArguments("C.M1()", " message.", "") }, fixedExpected: Array.Empty ()); diff --git a/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresUnreferencedCodeCapability.cs b/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresUnreferencedCodeCapability.cs index 856682563d0d..88aa44e5f332 100644 --- a/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresUnreferencedCodeCapability.cs +++ b/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresUnreferencedCodeCapability.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; +using System.Reflection; using System.Runtime.InteropServices; using System.Text; using Mono.Linker.Tests.Cases.Expectations.Assertions; @@ -71,6 +72,7 @@ public static void Main () TestRequiresInDynamicDependency (); TestThatTrailingPeriodIsAddedToMessage (); TestThatTrailingPeriodIsNotDuplicatedInWarningMessage (); + WarnIfRequiresUnreferencedCodeOnStaticConstructor.Test (); RequiresOnAttribute.Test (); RequiresOnGenerics.Test (); CovariantReturnViaLdftn.Test (); @@ -348,13 +350,13 @@ static void TestTypeWhichOverridesVirtualPropertyRequiresUnreferencedCode () class StaticCtor { + [ExpectedWarning ("IL2116", "StaticCtor..cctor()")] [RequiresUnreferencedCode ("Message for --TestStaticCtor--")] static StaticCtor () { } } - [ExpectedWarning ("IL2026", "--TestStaticCtor--")] static void TestStaticCctorRequiresUnreferencedCode () { _ = new StaticCtor (); @@ -362,6 +364,7 @@ static void TestStaticCctorRequiresUnreferencedCode () class StaticCtorTriggeredByFieldAccess { + [ExpectedWarning ("IL2116", "StaticCtorTriggeredByFieldAccess..cctor()")] [RequiresUnreferencedCode ("Message for --StaticCtorTriggeredByFieldAccess.Cctor--")] static StaticCtorTriggeredByFieldAccess () { @@ -371,7 +374,6 @@ static StaticCtorTriggeredByFieldAccess () public static int field; } - [ExpectedWarning ("IL2026", "--StaticCtorTriggeredByFieldAccess.Cctor--")] static void TestStaticCtorMarkingIsTriggeredByFieldAccess () { var x = StaticCtorTriggeredByFieldAccess.field + 1; @@ -379,13 +381,13 @@ static void TestStaticCtorMarkingIsTriggeredByFieldAccess () struct StaticCCtorForFieldAccess { + [ExpectedWarning ("IL2116", "StaticCCtorForFieldAccess..cctor()")] [RequiresUnreferencedCode ("Message for --StaticCCtorForFieldAccess.cctor--")] static StaticCCtorForFieldAccess () { } public static int field; } - [ExpectedWarning ("IL2026", "--StaticCCtorForFieldAccess.cctor--")] static void TestStaticCtorMarkingIsTriggeredByFieldAccessOnExplicitLayout () { StaticCCtorForFieldAccess.field = 0; @@ -400,7 +402,7 @@ class TypeIsBeforeFieldInit } [LogContains ("IL2026: Mono.Linker.Tests.Cases.RequiresCapability.RequiresUnreferencedCodeCapability.TypeIsBeforeFieldInit..cctor():" + - " Using method 'Mono.Linker.Tests.Cases.RequiresCapability.RequiresUnreferencedCodeCapability.TypeIsBeforeFieldInit.AnnotatedMethod()'" + + " Using member 'Mono.Linker.Tests.Cases.RequiresCapability.RequiresUnreferencedCodeCapability.TypeIsBeforeFieldInit.AnnotatedMethod()'" + " which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code." + " Message from --TypeIsBeforeFieldInit.AnnotatedMethod--.")] static void TestTypeIsBeforeFieldInit () @@ -410,6 +412,7 @@ static void TestTypeIsBeforeFieldInit () class StaticCtorTriggeredByMethodCall { + [ExpectedWarning ("IL2116", "StaticCtorTriggeredByMethodCall..cctor()")] [RequiresUnreferencedCode ("Message for --StaticCtorTriggeredByMethodCall.Cctor--")] static StaticCtorTriggeredByMethodCall () { @@ -421,7 +424,6 @@ public void TriggerStaticCtorMarking () } } - [ExpectedWarning ("IL2026", "--StaticCtorTriggeredByMethodCall.Cctor--")] [ExpectedWarning ("IL2026", "--StaticCtorTriggeredByMethodCall.TriggerStaticCtorMarking--")] static void TestStaticCtorTriggeredByMethodCall () { @@ -547,6 +549,21 @@ static void TestThatTrailingPeriodIsNotDuplicatedInWarningMessage () WarningMessageEndsWithPeriod (); } + class WarnIfRequiresUnreferencedCodeOnStaticConstructor + { + class ClassWithRequiresUnreferencedCodeOnStaticConstructor + { + [ExpectedWarning ("IL2116")] + [RequiresUnreferencedCode ("This attribute shouldn't be allowed")] + static ClassWithRequiresUnreferencedCodeOnStaticConstructor () { } + } + + public static void Test () + { + typeof (ClassWithRequiresUnreferencedCodeOnStaticConstructor).RequiresNonPublicConstructors (); + } + } + [ExpectedNoWarnings] class RequiresOnAttribute { @@ -882,6 +899,116 @@ public static void NestedStaticMethod () { } } } + [RequiresUnreferencedCode ("Message for --StaticCtor--")] + class StaticCtor + { + static StaticCtor () + { + } + } + + [ExpectedWarning ("IL2026", "RequiresOnClass.StaticCtor.StaticCtor()", "Message for --StaticCtor--", GlobalAnalysisOnly = true)] + static void TestStaticCctorRequiresUnreferencedCode () + { + _ = new StaticCtor (); + } + + [RequiresUnreferencedCode ("Message for --StaticCtorTriggeredByFieldAccess--")] + class StaticCtorTriggeredByFieldAccess + { + static StaticCtorTriggeredByFieldAccess () + { + field = 0; + } + + public static int field; + } + + [ExpectedWarning ("IL2026", "StaticCtorTriggeredByFieldAccess::field", "Message for --StaticCtorTriggeredByFieldAccess--", GlobalAnalysisOnly = true)] + static void TestStaticCtorMarkingIsTriggeredByFieldAccessWrite () + { + StaticCtorTriggeredByFieldAccess.field = 1; + } + + [ExpectedWarning ("IL2026", "StaticCtorTriggeredByFieldAccess::field", "Message for --StaticCtorTriggeredByFieldAccess--", GlobalAnalysisOnly = true)] + static void TestStaticCtorMarkingTriggeredOnSecondAccessWrite () + { + StaticCtorTriggeredByFieldAccess.field = 2; + } + + [RequiresUnreferencedCode ("--TestStaticRUCFieldAccessSuppressedByRUCOnMethod_Inner--")] + static void TestStaticRUCFieldAccessSuppressedByRUCOnMethod_Inner () + { + StaticCtorTriggeredByFieldAccess.field = 3; + } + + [UnconditionalSuppressMessage ("test", "IL2026")] + static void TestStaticRUCFieldAccessSuppressedByRUCOnMethod () + { + TestStaticRUCFieldAccessSuppressedByRUCOnMethod_Inner (); + } + + [RequiresUnreferencedCode ("Message for --StaticCCtorTriggeredByFieldAccessRead--")] + class StaticCCtorTriggeredByFieldAccessRead + { + public static int field = 42; + } + + [ExpectedWarning ("IL2026", "StaticCCtorTriggeredByFieldAccessRead::field", "Message for --StaticCCtorTriggeredByFieldAccessRead--", GlobalAnalysisOnly = true)] + static void TestStaticCtorMarkingIsTriggeredByFieldAccessRead () + { + var _ = StaticCCtorTriggeredByFieldAccessRead.field; + } + + [RequiresUnreferencedCode ("Message for --StaticCtorTriggeredByCtorCalls--")] + class StaticCtorTriggeredByCtorCalls + { + static StaticCtorTriggeredByCtorCalls () + { + } + + public void TriggerStaticCtorMarking () + { + } + } + + [ExpectedWarning ("IL2026", "StaticCtorTriggeredByCtorCalls.StaticCtorTriggeredByCtorCalls()", GlobalAnalysisOnly = true)] + static void TestStaticCtorTriggeredByCtorCall () + { + new StaticCtorTriggeredByCtorCalls (); + } + + [RequiresUnreferencedCode ("Message for --ClassWithInstanceField--")] + class ClassWithInstanceField + { + public int field = 42; + } + + [ExpectedWarning ("IL2026", "ClassWithInstanceField.ClassWithInstanceField()", GlobalAnalysisOnly = true)] + static void TestInstanceFieldCallDontWarn () + { + ClassWithInstanceField instance = new ClassWithInstanceField (); + var _ = instance.field; + } + + [RequiresUnreferencedCode ("Message for --StaticCtorTriggeredByMethodCall2--")] + class StaticCtorTriggeredByMethodCall2 + { + static StaticCtorTriggeredByMethodCall2 () + { + } + + public void TriggerStaticCtorMarking () + { + } + } + + static void TestNullInstanceTryingToCallMethod () + { + StaticCtorTriggeredByMethodCall2 instance = null; + instance.TriggerStaticCtorMarking (); + } + [RequiresUnreferencedCode ("Message for --DerivedWithRequires--")] private class DerivedWithRequires : ClassWithoutRequiresUnreferencedCode { @@ -977,6 +1104,7 @@ static void TestRequiresInParentClassAccesedByStaticMethod () [ExpectedWarning ("IL2026", "RequiresOnClass.ClassWithRequiresUnreferencedCode.StaticMethod()", "--ClassWithRequiresUnreferencedCode--", GlobalAnalysisOnly = true)] // Although we suppress the warning from RequiresOnMethod.MethodWithRUC () we still get a warning because we call CallRUCMethod() which is an static method on a type with RUC [ExpectedWarning ("IL2026", "RequiresOnClass.ClassWithRequiresUnreferencedCode.CallRUCMethod()", "--ClassWithRequiresUnreferencedCode--", GlobalAnalysisOnly = true)] + [ExpectedWarning ("IL2026", "ClassWithRequiresUnreferencedCode::Instance", "--ClassWithRequiresUnreferencedCode--", GlobalAnalysisOnly = true)] static void TestRequiresOnBaseButNotOnDerived () { DerivedWithoutRequires.StaticMethodInInheritedClass (); @@ -1016,15 +1144,424 @@ static void TestSuppressionsOnClass () TestUnconditionalSuppressMessage.StaticMethodInTestSuppressionClass (); } - static void RequirePublicMethods ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type type) + [RequiresUnreferencedCode ("--StaticMethodOnRUCTypeSuppressedByRUCOnMethod--")] + static void StaticMethodOnRUCTypeSuppressedByRUCOnMethod () + { + DerivedWithRequires.StaticMethodInInheritedClass (); + } + + [UnconditionalSuppressMessage ("test", "IL2026")] + static void TestStaticMethodOnRUCTypeSuppressedByRUCOnMethod () + { + StaticMethodOnRUCTypeSuppressedByRUCOnMethod (); + } + + static void TestStaticConstructorCalls () + { + TestStaticCctorRequiresUnreferencedCode (); + TestStaticCtorMarkingIsTriggeredByFieldAccessWrite (); + TestStaticCtorMarkingTriggeredOnSecondAccessWrite (); + TestStaticRUCFieldAccessSuppressedByRUCOnMethod (); + TestStaticCtorMarkingIsTriggeredByFieldAccessRead (); + TestStaticCtorTriggeredByMethodCall (); + TestStaticCtorTriggeredByCtorCall (); + TestInstanceFieldCallDontWarn (); + } + + [RequiresUnreferencedCode ("--MemberTypesWithRUC--")] + class MemberTypesWithRUC + { + public static int field; + public static int Property { get; set; } + + // These should not be reported https://github.com/mono/linker/issues/2218 + [ExpectedWarning ("IL2026", "add_Event", GlobalAnalysisOnly = true)] + [ExpectedWarning ("IL2026", "remove_Event", GlobalAnalysisOnly = true)] + public static event EventHandler Event; + } + + [ExpectedWarning ("IL2026", "MemberTypesWithRUC::field", GlobalAnalysisOnly = true)] + [ExpectedWarning ("IL2026", "MemberTypesWithRUC.Property.set", GlobalAnalysisOnly = true)] + [ExpectedWarning ("IL2026", "MemberTypesWithRUC.remove_Event", GlobalAnalysisOnly = true)] + static void TestOtherMemberTypesWithRUC () + { + MemberTypesWithRUC.field = 1; + MemberTypesWithRUC.Property = 1; + MemberTypesWithRUC.Event -= null; + } + + class ReflectionAccessOnMethod + { + // Analyzer still dont understand RUC on type + [ExpectedWarning ("IL2026", "BaseWithoutRequiresOnType.Method()", GlobalAnalysisOnly = true)] + [ExpectedWarning ("IL2026", "InterfaceWithoutRequires.Method(Int32)", GlobalAnalysisOnly = true)] + [ExpectedWarning ("IL2026", "InterfaceWithoutRequires.Method()", GlobalAnalysisOnly = true)] + [ExpectedWarning ("IL2026", "ImplementationWithRequiresOnType.Method()", GlobalAnalysisOnly = true)] + static void TestDAMAccess () + { + // Warns because BaseWithoutRequiresOnType.Method as RUC on the method + typeof (BaseWithoutRequiresOnType).RequiresPublicMethods (); + + // Doesn't warn because DerivedWithRequiresOnType doesn't have any static methods + typeof (DerivedWithRequiresOnType).RequiresPublicMethods (); + + // Warns twice since both methods on InterfaceWithoutRequires have RUC on the method + typeof (InterfaceWithoutRequires).RequiresPublicMethods (); + + // Warns because ImplementationWithRequiresOnType.Method is a static public method on a RUC type + typeof (ImplementationWithRequiresOnType).RequiresPublicMethods (); + + // Doesn't warn since BaseWithRequiresOnType has no static methods + typeof (BaseWithRequiresOnType).RequiresPublicMethods (); + + // Doesn't warn since DerivedWithoutRequiresOnType has no static methods + typeof (DerivedWithoutRequiresOnType).RequiresPublicMethods (); + } + + [ExpectedWarning ("IL2026", "BaseWithoutRequiresOnType.Method()", GlobalAnalysisOnly = true)] + [ExpectedWarning ("IL2026", "InterfaceWithoutRequires.Method(Int32)", GlobalAnalysisOnly = true)] + [ExpectedWarning ("IL2026", "InterfaceWithoutRequires.Method()", GlobalAnalysisOnly = true)] + [ExpectedWarning ("IL2026", "ImplementationWithRequiresOnType.Method()", GlobalAnalysisOnly = true)] + static void TestDirectReflectionAccess () + { + // RUC on the method itself + typeof (BaseWithoutRequiresOnType).GetMethod (nameof (BaseWithoutRequiresOnType.Method)); + + // RUC on the method itself + typeof (InterfaceWithoutRequires).GetMethod (nameof (InterfaceWithoutRequires.Method)); + + // Warns because ImplementationWithRequiresOnType.Method is a static public method on a RUC type + typeof (ImplementationWithRequiresOnType).GetMethod (nameof (ImplementationWithRequiresOnType.Method)); + + // Doesn't warn since Method is not static (so it doesn't matter that the type has RUC) + typeof (BaseWithRequiresOnType).GetMethod (nameof (BaseWithRequiresOnType.Method)); + } + + public static void Test () + { + TestDAMAccess (); + TestDirectReflectionAccess (); + } + } + + class ReflectionAccessOnCtor + { + [RequiresUnreferencedCode ("--BaseWithRUC--")] + class BaseWithRUC + { + public BaseWithRUC () { } + } + + [ExpectedWarning ("IL2109", "ReflectionAccessOnCtor/DerivedWithoutRUC", "ReflectionAccessOnCtor.BaseWithRUC", GlobalAnalysisOnly = true)] + class DerivedWithoutRUC : BaseWithRUC + { + [ExpectedWarning ("IL2026", "--BaseWithRUC--")] // The body has direct call to the base.ctor() + public DerivedWithoutRUC () { } + } + + [RequiresUnreferencedCode ("--DerivedWithRUCOnBaseWithRUC--")] + class DerivedWithRUCOnBaseWithRUC : BaseWithRUC + { + // No warning - suppressed by the RUC on this type + private DerivedWithRUCOnBaseWithRUC () { } + } + + class BaseWithoutRUC { } + + [RequiresUnreferencedCode ("--DerivedWithRUCOnBaseWithout--")] + class DerivedWithRUCOnBaseWithoutRuc : BaseWithoutRUC + { + public DerivedWithRUCOnBaseWithoutRuc () { } + } + + [ExpectedWarning ("IL2026", "BaseWithRUC.BaseWithRUC()", GlobalAnalysisOnly = true)] + [ExpectedWarning ("IL2026", "DerivedWithRUCOnBaseWithRUC.DerivedWithRUCOnBaseWithRUC()", GlobalAnalysisOnly = true)] + [ExpectedWarning ("IL2026", "DerivedWithRUCOnBaseWithoutRuc.DerivedWithRUCOnBaseWithoutRuc()", GlobalAnalysisOnly = true)] + static void TestDAMAccess () + { + // Warns because the type has RUC + typeof (BaseWithRUC).RequiresPublicConstructors (); + + // Doesn't warn since there's no RUC on this type + typeof (DerivedWithoutRUC).RequiresPublicParameterlessConstructor (); + + // Warns - RUC on the type + typeof (DerivedWithRUCOnBaseWithRUC).RequiresNonPublicConstructors (); + + // Warns - RUC On the type + typeof (DerivedWithRUCOnBaseWithoutRuc).RequiresPublicConstructors (); + } + + [ExpectedWarning ("IL2026", "BaseWithRUC.BaseWithRUC()", GlobalAnalysisOnly = true)] + [ExpectedWarning ("IL2026", "DerivedWithRUCOnBaseWithRUC.DerivedWithRUCOnBaseWithRUC()", GlobalAnalysisOnly = true)] + [ExpectedWarning ("IL2026", "DerivedWithRUCOnBaseWithoutRuc.DerivedWithRUCOnBaseWithoutRuc()", GlobalAnalysisOnly = true)] + static void TestDirectReflectionAccess () + { + typeof (BaseWithRUC).GetConstructor (Type.EmptyTypes); + typeof (DerivedWithoutRUC).GetConstructor (Type.EmptyTypes); + typeof (DerivedWithRUCOnBaseWithRUC).GetConstructor (BindingFlags.NonPublic, Type.EmptyTypes); + typeof (DerivedWithRUCOnBaseWithoutRuc).GetConstructor (Type.EmptyTypes); + } + + public static void Test () + { + TestDAMAccess (); + TestDirectReflectionAccess (); + } + } + + class ReflectionAccessOnField { + [RequiresUnreferencedCode ("--WithRUC--")] + class WithRUC + { + public int InstanceField; + public static int StaticField; + private static int PrivateStaticField; + } + + [RequiresUnreferencedCode ("--WithRUCOnlyInstanceFields--")] + class WithRUCOnlyInstanceFields + { + public int InstnaceField; + } + + [ExpectedWarning ("IL2109", "ReflectionAccessOnField/DerivedWithoutRUC", "ReflectionAccessOnField.WithRUC", GlobalAnalysisOnly = true)] + class DerivedWithoutRUC : WithRUC + { + public static int DerivedStaticField; + } + + [RequiresUnreferencedCode ("--DerivedWithRUC--")] + class DerivedWithRUC : WithRUC + { + public static int DerivedStaticField; + } + + [ExpectedWarning ("IL2026", "WithRUC::StaticField", GlobalAnalysisOnly = true)] + [ExpectedWarning ("IL2026", "WithRUC::PrivateStaticField", GlobalAnalysisOnly = true)] + [ExpectedWarning ("IL2026", "DerivedWithRUC::DerivedStaticField", GlobalAnalysisOnly = true)] + static void TestDAMAccess () + { + typeof (WithRUC).RequiresPublicFields (); + typeof (WithRUC).RequiresNonPublicFields (); + typeof (WithRUCOnlyInstanceFields).RequiresPublicFields (); + typeof (DerivedWithoutRUC).RequiresPublicFields (); + typeof (DerivedWithRUC).RequiresPublicFields (); + } + + [ExpectedWarning ("IL2026", "WithRUC::StaticField", GlobalAnalysisOnly = true)] + [ExpectedWarning ("IL2026", "WithRUC::PrivateStaticField", GlobalAnalysisOnly = true)] + [ExpectedWarning ("IL2026", "DerivedWithRUC::DerivedStaticField", GlobalAnalysisOnly = true)] + static void TestDirectReflectionAccess () + { + typeof (WithRUC).GetField (nameof (WithRUC.StaticField)); + typeof (WithRUC).GetField (nameof (WithRUC.InstanceField)); // Doesn't warn + typeof (WithRUC).GetField ("PrivateStaticField", BindingFlags.NonPublic); + typeof (WithRUCOnlyInstanceFields).GetField (nameof (WithRUCOnlyInstanceFields.InstnaceField)); // Doesn't warn + typeof (DerivedWithoutRUC).GetField (nameof (DerivedWithRUC.DerivedStaticField)); // Doesn't warn + typeof (DerivedWithRUC).GetField (nameof (DerivedWithRUC.DerivedStaticField)); + } + + [ExpectedWarning ("IL2026", "WithRUC::StaticField", GlobalAnalysisOnly = true)] + [DynamicDependency (nameof (WithRUC.StaticField), typeof (WithRUC))] + [DynamicDependency (nameof (WithRUC.InstanceField), typeof (WithRUC))] // Doesn't warn + [DynamicDependency (DynamicallyAccessedMemberTypes.PublicFields, typeof (DerivedWithoutRUC))] // Doesn't warn + [ExpectedWarning ("IL2026", "DerivedWithRUC::DerivedStaticField", GlobalAnalysisOnly = true)] + [DynamicDependency (DynamicallyAccessedMemberTypes.PublicFields, typeof (DerivedWithRUC))] + static void TestDynamicDependencyAccess () + { + } + + [RequiresUnreferencedCode ("This class is dangerous")] + class BaseForDAMAnnotatedClass + { + public static int baseField; + } + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] + [RequiresUnreferencedCode ("This class is dangerous")] + [ExpectedWarning ("IL2113", "BaseForDAMAnnotatedClass::baseField", GlobalAnalysisOnly = true)] + class DAMAnnotatedClass : BaseForDAMAnnotatedClass + { + [ExpectedWarning ("IL2112", "DAMAnnotatedClass::publicField", GlobalAnalysisOnly = true)] + public static int publicField; + + [ExpectedWarning ("IL2112", "DAMAnnotatedClass::privatefield", GlobalAnalysisOnly = true)] + static int privatefield; + } + + static void TestDAMOnTypeAccess (DAMAnnotatedClass instance) + { + instance.GetType ().GetField ("publicField"); + } + + public static void Test () + { + TestDAMAccess (); + TestDirectReflectionAccess (); + TestDynamicDependencyAccess (); + TestDAMOnTypeAccess (null); + } + } + + class ReflectionAccessOnEvents + { + // Most of the tests in this run into https://github.com/mono/linker/issues/2218 + // So for now keeping just a very simple test + + [RequiresUnreferencedCode ("--WithRUC--")] + class WithRUC + { + // These should be reported only in TestDirectReflectionAccess + // https://github.com/mono/linker/issues/2218 + [ExpectedWarning ("IL2026", "add_StaticEvent", GlobalAnalysisOnly = true)] + [ExpectedWarning ("IL2026", "remove_StaticEvent", GlobalAnalysisOnly = true)] + public static event EventHandler StaticEvent; + } + + [ExpectedWarning ("IL2026", "add_StaticEvent", GlobalAnalysisOnly = true)] + static void TestDirectReflectionAccess () + { + typeof (WithRUC).GetEvent (nameof (WithRUC.StaticEvent)); + } + + public static void Test () + { + TestDirectReflectionAccess (); + } + } + + class ReflectionAccessOnProperties + { + [RequiresUnreferencedCode ("--WithRUC--")] + class WithRUC + { + public int InstanceProperty { get; set; } + public static int StaticProperty { get; set; } + private static int PrivateStaticProperty { get; set; } + } + + [RequiresUnreferencedCode ("--WithRUCOnlyInstanceProperties--")] + class WithRUCOnlyInstanceProperties + { + public int InstnaceProperty { get; set; } + } + + [ExpectedWarning ("IL2109", "ReflectionAccessOnProperties/DerivedWithoutRUC", "ReflectionAccessOnProperties.WithRUC", GlobalAnalysisOnly = true)] + class DerivedWithoutRUC : WithRUC + { + public static int DerivedStaticProperty { get; set; } + } + + [RequiresUnreferencedCode ("--DerivedWithRUC--")] + class DerivedWithRUC : WithRUC + { + public static int DerivedStaticProperty { get; set; } + } + + [ExpectedWarning ("IL2026", "WithRUC.StaticProperty.get", GlobalAnalysisOnly = true)] + [ExpectedWarning ("IL2026", "WithRUC.StaticProperty.set", GlobalAnalysisOnly = true)] + [ExpectedWarning ("IL2026", "WithRUC.PrivateStaticProperty.get", GlobalAnalysisOnly = true)] + [ExpectedWarning ("IL2026", "WithRUC.PrivateStaticProperty.set", GlobalAnalysisOnly = true)] + [ExpectedWarning ("IL2026", "DerivedWithRUC.DerivedStaticProperty.get", GlobalAnalysisOnly = true)] + [ExpectedWarning ("IL2026", "DerivedWithRUC.DerivedStaticProperty.set", GlobalAnalysisOnly = true)] + static void TestDAMAccess () + { + typeof (WithRUC).RequiresPublicProperties (); + typeof (WithRUC).RequiresNonPublicProperties (); + typeof (WithRUCOnlyInstanceProperties).RequiresPublicProperties (); + typeof (DerivedWithoutRUC).RequiresPublicProperties (); + typeof (DerivedWithRUC).RequiresPublicProperties (); + } + + [ExpectedWarning ("IL2026", "WithRUC.StaticProperty.get", GlobalAnalysisOnly = true)] + [ExpectedWarning ("IL2026", "WithRUC.StaticProperty.set", GlobalAnalysisOnly = true)] + [ExpectedWarning ("IL2026", "WithRUC.PrivateStaticProperty.get", GlobalAnalysisOnly = true)] + [ExpectedWarning ("IL2026", "WithRUC.PrivateStaticProperty.set", GlobalAnalysisOnly = true)] + [ExpectedWarning ("IL2026", "DerivedWithRUC.DerivedStaticProperty.get", GlobalAnalysisOnly = true)] + [ExpectedWarning ("IL2026", "DerivedWithRUC.DerivedStaticProperty.set", GlobalAnalysisOnly = true)] + static void TestDirectReflectionAccess () + { + typeof (WithRUC).GetProperty (nameof (WithRUC.StaticProperty)); + typeof (WithRUC).GetProperty (nameof (WithRUC.InstanceProperty)); // Doesn't warn + typeof (WithRUC).GetProperty ("PrivateStaticProperty", BindingFlags.NonPublic); + typeof (WithRUCOnlyInstanceProperties).GetProperty (nameof (WithRUCOnlyInstanceProperties.InstnaceProperty)); // Doesn't warn + typeof (DerivedWithoutRUC).GetProperty (nameof (DerivedWithRUC.DerivedStaticProperty)); // Doesn't warn + typeof (DerivedWithRUC).GetProperty (nameof (DerivedWithRUC.DerivedStaticProperty)); + } + + [ExpectedWarning ("IL2026", "WithRUC.StaticProperty.get", GlobalAnalysisOnly = true)] + [ExpectedWarning ("IL2026", "WithRUC.StaticProperty.set", GlobalAnalysisOnly = true)] + [DynamicDependency (nameof (WithRUC.StaticProperty), typeof (WithRUC))] + [DynamicDependency (nameof (WithRUC.InstanceProperty), typeof (WithRUC))] // Doesn't warn + [DynamicDependency (DynamicallyAccessedMemberTypes.PublicProperties, typeof (DerivedWithoutRUC))] // Doesn't warn + [ExpectedWarning ("IL2026", "DerivedWithRUC.DerivedStaticProperty.get", GlobalAnalysisOnly = true)] + [ExpectedWarning ("IL2026", "DerivedWithRUC.DerivedStaticProperty.set", GlobalAnalysisOnly = true)] + [DynamicDependency (DynamicallyAccessedMemberTypes.PublicProperties, typeof (DerivedWithRUC))] + static void TestDynamicDependencyAccess () + { + } + + [RequiresUnreferencedCode ("This class is dangerous")] + class BaseForDAMAnnotatedClass + { + public static int baseProperty { get; set; } + } + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] + [RequiresUnreferencedCode ("This class is dangerous")] + [ExpectedWarning ("IL2113", "BaseForDAMAnnotatedClass.baseProperty.get", GlobalAnalysisOnly = true)] + [ExpectedWarning ("IL2113", "BaseForDAMAnnotatedClass.baseProperty.set", GlobalAnalysisOnly = true)] + class DAMAnnotatedClass : BaseForDAMAnnotatedClass + { + public static int publicProperty { + [ExpectedWarning ("IL2112", "DAMAnnotatedClass.publicProperty.get", GlobalAnalysisOnly = true)] + get; + [ExpectedWarning ("IL2112", "DAMAnnotatedClass.publicProperty.set", GlobalAnalysisOnly = true)] + set; + } + + static int privateProperty { + [ExpectedWarning ("IL2112", "DAMAnnotatedClass.privateProperty.get", GlobalAnalysisOnly = true)] + get; + [ExpectedWarning ("IL2112", "DAMAnnotatedClass.privateProperty.set", GlobalAnalysisOnly = true)] + set; + } + } + + static void TestDAMOnTypeAccess (DAMAnnotatedClass instance) + { + instance.GetType ().GetProperty ("publicProperty"); + } + + public static void Test () + { + TestDAMAccess (); + TestDirectReflectionAccess (); + TestDynamicDependencyAccess (); + TestDAMOnTypeAccess (null); + } } - // Analyzer still dont understand RUC on type - [ExpectedWarning ("IL2026", "BaseWithoutRequiresOnType.Method()", GlobalAnalysisOnly = true)] - [ExpectedWarning ("IL2026", "InterfaceWithoutRequires.Method(Int32)", GlobalAnalysisOnly = true)] - [ExpectedWarning ("IL2026", "InterfaceWithoutRequires.Method()", GlobalAnalysisOnly = true)] - [ExpectedWarning ("IL2026", "ImplementationWithRequiresOnType.Method()", GlobalAnalysisOnly = true)] + [RequiresUnreferencedCode ("The attribute is dangerous")] + public class AttributeWithRUC : Attribute + { + public static int field; + + // `field` cannot be used as named attribute argument because is static, and if accessed via + // a property the property will be the one generating the warning, but then the warning will + // be suppresed by the RequiresUnreferencedCode on the declaring type + public int PropertyOnAttribute { + get { return field; } + set { field = value; } + } + } + + [AttributeWithRUC (PropertyOnAttribute = 42)] + [ExpectedWarning ("IL2026", "AttributeWithRUC.AttributeWithRUC()", GlobalAnalysisOnly = true)] + static void KeepFieldOnAttribute () { } + public static void Test () { TestRequiresInClassAccessedByStaticMethod (); @@ -1034,12 +1571,15 @@ public static void Test () TestRequiresOnDerivedButNotOnBase (); TestRequiresOnBaseAndDerived (); TestSuppressionsOnClass (); - RequirePublicMethods (typeof (BaseWithoutRequiresOnType)); - RequirePublicMethods (typeof (DerivedWithRequiresOnType)); - RequirePublicMethods (typeof (BaseWithRequiresOnType)); - RequirePublicMethods (typeof (DerivedWithoutRequiresOnType)); - RequirePublicMethods (typeof (InterfaceWithoutRequires)); - RequirePublicMethods (typeof (ImplementationWithRequiresOnType)); + TestStaticMethodOnRUCTypeSuppressedByRUCOnMethod (); + TestStaticConstructorCalls (); + TestOtherMemberTypesWithRUC (); + ReflectionAccessOnMethod.Test (); + ReflectionAccessOnCtor.Test (); + ReflectionAccessOnField.Test (); + ReflectionAccessOnEvents.Test (); + ReflectionAccessOnProperties.Test (); + KeepFieldOnAttribute (); } } }