diff options
23 files changed, 166 insertions, 76 deletions
diff --git a/docs/error-codes.md b/docs/error-codes.md index f3e8ad60d..d2081505f 100644 --- a/docs/error-codes.md +++ b/docs/error-codes.md @@ -472,7 +472,7 @@ the error code. For example: </linker> ``` -#### `IL2026` Trim analysis: Calling 'method' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. [message]. [url] +#### `IL2026` Trim analysis: Using method '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. @@ -484,7 +484,7 @@ the error code. For example: void TestMethod() { - // IL2026: Calling 'MethodWithUnreferencedCodeUsage' which has 'RequiresUnreferencedCodeAttribute' + // IL2026: Using method 'MethodWithUnreferencedCodeUsage' which has 'RequiresUnreferencedCodeAttribute' // can break functionality when trimming application code. Use 'MethodFriendlyToTrimming' instead. http://help/unreferencedcode MethodWithUnreferencedCodeUsage(); } diff --git a/src/ILLink.RoslynAnalyzer/Resources.resx b/src/ILLink.RoslynAnalyzer/Resources.resx index ca77c42ba..3652fecb4 100644 --- a/src/ILLink.RoslynAnalyzer/Resources.resx +++ b/src/ILLink.RoslynAnalyzer/Resources.resx @@ -121,7 +121,7 @@ <value>RequiresUnreferencedCodeAnalyzer</value> </data> <data name="RequiresUnreferencedCodeAnalyzerMessage" xml:space="preserve"> - <value>Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' which can break functionality when trimming application code. {1}. {2}</value> + <value>Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. {1}. {2}</value> </data> <data name="AvoidAssemblyLocationInSingleFileTitle" xml:space="preserve"> <value>Avoid accessing Assembly file path when publishing as a single file</value> diff --git a/src/ILLink.RoslynAnalyzer/xlf/Resources.cs.xlf b/src/ILLink.RoslynAnalyzer/xlf/Resources.cs.xlf index fd7064da9..7b2ab7073 100644 --- a/src/ILLink.RoslynAnalyzer/xlf/Resources.cs.xlf +++ b/src/ILLink.RoslynAnalyzer/xlf/Resources.cs.xlf @@ -23,8 +23,8 @@ <note /> </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerMessage"> - <source>Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' which can break functionality when trimming application code. {1}. {2}</source> - <target state="new">Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' which can break functionality when trimming application code. {1}. {2}</target> + <source>Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. {1}. {2}</source> + <target state="new">Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. {1}. {2}</target> <note /> </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerTitle"> diff --git a/src/ILLink.RoslynAnalyzer/xlf/Resources.de.xlf b/src/ILLink.RoslynAnalyzer/xlf/Resources.de.xlf index 15eec32be..3bb6a7498 100644 --- a/src/ILLink.RoslynAnalyzer/xlf/Resources.de.xlf +++ b/src/ILLink.RoslynAnalyzer/xlf/Resources.de.xlf @@ -23,8 +23,8 @@ <note /> </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerMessage"> - <source>Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' which can break functionality when trimming application code. {1}. {2}</source> - <target state="new">Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' which can break functionality when trimming application code. {1}. {2}</target> + <source>Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. {1}. {2}</source> + <target state="new">Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. {1}. {2}</target> <note /> </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerTitle"> diff --git a/src/ILLink.RoslynAnalyzer/xlf/Resources.es.xlf b/src/ILLink.RoslynAnalyzer/xlf/Resources.es.xlf index 80aee6671..e93ede8bc 100644 --- a/src/ILLink.RoslynAnalyzer/xlf/Resources.es.xlf +++ b/src/ILLink.RoslynAnalyzer/xlf/Resources.es.xlf @@ -23,8 +23,8 @@ <note /> </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerMessage"> - <source>Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' which can break functionality when trimming application code. {1}. {2}</source> - <target state="new">Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' which can break functionality when trimming application code. {1}. {2}</target> + <source>Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. {1}. {2}</source> + <target state="new">Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. {1}. {2}</target> <note /> </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerTitle"> diff --git a/src/ILLink.RoslynAnalyzer/xlf/Resources.fr.xlf b/src/ILLink.RoslynAnalyzer/xlf/Resources.fr.xlf index 8b89c1893..d5464557d 100644 --- a/src/ILLink.RoslynAnalyzer/xlf/Resources.fr.xlf +++ b/src/ILLink.RoslynAnalyzer/xlf/Resources.fr.xlf @@ -23,8 +23,8 @@ <note /> </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerMessage"> - <source>Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' which can break functionality when trimming application code. {1}. {2}</source> - <target state="new">Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' which can break functionality when trimming application code. {1}. {2}</target> + <source>Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. {1}. {2}</source> + <target state="new">Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. {1}. {2}</target> <note /> </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerTitle"> diff --git a/src/ILLink.RoslynAnalyzer/xlf/Resources.it.xlf b/src/ILLink.RoslynAnalyzer/xlf/Resources.it.xlf index 1adcfde3b..458cfd36e 100644 --- a/src/ILLink.RoslynAnalyzer/xlf/Resources.it.xlf +++ b/src/ILLink.RoslynAnalyzer/xlf/Resources.it.xlf @@ -23,8 +23,8 @@ <note /> </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerMessage"> - <source>Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' which can break functionality when trimming application code. {1}. {2}</source> - <target state="new">Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' which can break functionality when trimming application code. {1}. {2}</target> + <source>Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. {1}. {2}</source> + <target state="new">Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. {1}. {2}</target> <note /> </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerTitle"> diff --git a/src/ILLink.RoslynAnalyzer/xlf/Resources.ja.xlf b/src/ILLink.RoslynAnalyzer/xlf/Resources.ja.xlf index b1d4cbfab..c7c102e0b 100644 --- a/src/ILLink.RoslynAnalyzer/xlf/Resources.ja.xlf +++ b/src/ILLink.RoslynAnalyzer/xlf/Resources.ja.xlf @@ -23,8 +23,8 @@ <note /> </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerMessage"> - <source>Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' which can break functionality when trimming application code. {1}. {2}</source> - <target state="new">Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' which can break functionality when trimming application code. {1}. {2}</target> + <source>Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. {1}. {2}</source> + <target state="new">Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. {1}. {2}</target> <note /> </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerTitle"> diff --git a/src/ILLink.RoslynAnalyzer/xlf/Resources.ko.xlf b/src/ILLink.RoslynAnalyzer/xlf/Resources.ko.xlf index a1263ca71..3225cc0c1 100644 --- a/src/ILLink.RoslynAnalyzer/xlf/Resources.ko.xlf +++ b/src/ILLink.RoslynAnalyzer/xlf/Resources.ko.xlf @@ -23,8 +23,8 @@ <note /> </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerMessage"> - <source>Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' which can break functionality when trimming application code. {1}. {2}</source> - <target state="new">Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' which can break functionality when trimming application code. {1}. {2}</target> + <source>Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. {1}. {2}</source> + <target state="new">Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. {1}. {2}</target> <note /> </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerTitle"> diff --git a/src/ILLink.RoslynAnalyzer/xlf/Resources.pl.xlf b/src/ILLink.RoslynAnalyzer/xlf/Resources.pl.xlf index d2c5d2f41..489d08c7b 100644 --- a/src/ILLink.RoslynAnalyzer/xlf/Resources.pl.xlf +++ b/src/ILLink.RoslynAnalyzer/xlf/Resources.pl.xlf @@ -23,8 +23,8 @@ <note /> </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerMessage"> - <source>Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' which can break functionality when trimming application code. {1}. {2}</source> - <target state="new">Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' which can break functionality when trimming application code. {1}. {2}</target> + <source>Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. {1}. {2}</source> + <target state="new">Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. {1}. {2}</target> <note /> </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerTitle"> diff --git a/src/ILLink.RoslynAnalyzer/xlf/Resources.pt-BR.xlf b/src/ILLink.RoslynAnalyzer/xlf/Resources.pt-BR.xlf index 8a69d2b4b..010fe0c00 100644 --- a/src/ILLink.RoslynAnalyzer/xlf/Resources.pt-BR.xlf +++ b/src/ILLink.RoslynAnalyzer/xlf/Resources.pt-BR.xlf @@ -23,8 +23,8 @@ <note /> </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerMessage"> - <source>Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' which can break functionality when trimming application code. {1}. {2}</source> - <target state="new">Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' which can break functionality when trimming application code. {1}. {2}</target> + <source>Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. {1}. {2}</source> + <target state="new">Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. {1}. {2}</target> <note /> </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerTitle"> diff --git a/src/ILLink.RoslynAnalyzer/xlf/Resources.ru.xlf b/src/ILLink.RoslynAnalyzer/xlf/Resources.ru.xlf index 99dd29b34..7a49c4841 100644 --- a/src/ILLink.RoslynAnalyzer/xlf/Resources.ru.xlf +++ b/src/ILLink.RoslynAnalyzer/xlf/Resources.ru.xlf @@ -23,8 +23,8 @@ <note /> </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerMessage"> - <source>Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' which can break functionality when trimming application code. {1}. {2}</source> - <target state="new">Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' which can break functionality when trimming application code. {1}. {2}</target> + <source>Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. {1}. {2}</source> + <target state="new">Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. {1}. {2}</target> <note /> </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerTitle"> diff --git a/src/ILLink.RoslynAnalyzer/xlf/Resources.tr.xlf b/src/ILLink.RoslynAnalyzer/xlf/Resources.tr.xlf index b16d27db6..47040d799 100644 --- a/src/ILLink.RoslynAnalyzer/xlf/Resources.tr.xlf +++ b/src/ILLink.RoslynAnalyzer/xlf/Resources.tr.xlf @@ -23,8 +23,8 @@ <note /> </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerMessage"> - <source>Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' which can break functionality when trimming application code. {1}. {2}</source> - <target state="new">Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' which can break functionality when trimming application code. {1}. {2}</target> + <source>Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. {1}. {2}</source> + <target state="new">Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. {1}. {2}</target> <note /> </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerTitle"> diff --git a/src/ILLink.RoslynAnalyzer/xlf/Resources.zh-Hans.xlf b/src/ILLink.RoslynAnalyzer/xlf/Resources.zh-Hans.xlf index 833453551..2affa712e 100644 --- a/src/ILLink.RoslynAnalyzer/xlf/Resources.zh-Hans.xlf +++ b/src/ILLink.RoslynAnalyzer/xlf/Resources.zh-Hans.xlf @@ -23,8 +23,8 @@ <note /> </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerMessage"> - <source>Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' which can break functionality when trimming application code. {1}. {2}</source> - <target state="new">Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' which can break functionality when trimming application code. {1}. {2}</target> + <source>Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. {1}. {2}</source> + <target state="new">Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. {1}. {2}</target> <note /> </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerTitle"> diff --git a/src/ILLink.RoslynAnalyzer/xlf/Resources.zh-Hant.xlf b/src/ILLink.RoslynAnalyzer/xlf/Resources.zh-Hant.xlf index 95a89a65a..d2b45f19c 100644 --- a/src/ILLink.RoslynAnalyzer/xlf/Resources.zh-Hant.xlf +++ b/src/ILLink.RoslynAnalyzer/xlf/Resources.zh-Hant.xlf @@ -23,8 +23,8 @@ <note /> </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerMessage"> - <source>Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' which can break functionality when trimming application code. {1}. {2}</source> - <target state="new">Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' which can break functionality when trimming application code. {1}. {2}</target> + <source>Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. {1}. {2}</source> + <target state="new">Using method '{0}' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. {1}. {2}</target> <note /> </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerTitle"> diff --git a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs index 7b6c28465..448b29d0e 100644 --- a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs +++ b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs @@ -513,8 +513,12 @@ namespace Mono.Linker.Dataflow return false; var callingMethodDefinition = callingMethodBody.Method; - var reflectionContext = new ReflectionPatternContext (_context, - ShouldEnableReflectionPatternReporting (callingMethodDefinition), callingMethodDefinition, calledMethod.Resolve (), operation); + var reflectionContext = new ReflectionPatternContext ( + _context, + ShouldEnableReflectionPatternReporting (callingMethodDefinition), + callingMethodDefinition, + calledMethod.Resolve (), + operation); DynamicallyAccessedMemberTypes returnValueDynamicallyAccessedMemberTypes = 0; @@ -1330,6 +1334,8 @@ namespace Mono.Linker.Dataflow reflectionContext.RecordHandledPattern (); } + _markStep.CheckAndReportRequiresUnreferencedCode (calledMethodDefinition, new MessageOrigin (callingMethodDefinition, operation.Offset)); + // To get good reporting of errors we need to track the origin of the value for all method calls // but except Newobj as those are special. if (calledMethodDefinition.ReturnType.MetadataType != MetadataType.Void) { @@ -1729,7 +1735,8 @@ namespace Mono.Linker.Dataflow void MarkMethod (ref ReflectionPatternContext reflectionContext, MethodDefinition method, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection) { var source = reflectionContext.Source; - reflectionContext.RecordRecognizedPattern (method, () => _markStep.MarkIndirectlyCalledMethod (method, new DependencyInfo (dependencyKind, source), source)); + var offset = reflectionContext.Instruction?.Offset; + reflectionContext.RecordRecognizedPattern (method, () => _markStep.MarkIndirectlyCalledMethod (method, new DependencyInfo (dependencyKind, source), new MessageOrigin (source, offset))); } void MarkNestedType (ref ReflectionPatternContext reflectionContext, TypeDefinition nestedType, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection) diff --git a/src/linker/Linker.Steps/MarkStep.cs b/src/linker/Linker.Steps/MarkStep.cs index e7841af64..e3525995b 100644 --- a/src/linker/Linker.Steps/MarkStep.cs +++ b/src/linker/Linker.Steps/MarkStep.cs @@ -923,7 +923,7 @@ namespace Mono.Linker.Steps continue; if (signature == null) { - MarkIndirectlyCalledMethod (m, reason, sourceLocationMember); + MarkIndirectlyCalledMethod (m, reason, new MessageOrigin (sourceLocationMember)); marked = true; continue; } @@ -943,7 +943,7 @@ namespace Mono.Linker.Steps if (i < 0) continue; - MarkIndirectlyCalledMethod (m, reason, sourceLocationMember); + MarkIndirectlyCalledMethod (m, reason, new MessageOrigin (sourceLocationMember)); marked = true; } @@ -2449,9 +2449,9 @@ namespace Mono.Linker.Steps MarkMethod (method, reason, new MessageOrigin (sourceLocationMember)); } - protected internal void MarkIndirectlyCalledMethod (MethodDefinition method, in DependencyInfo reason, IMemberDefinition sourceLocationMember) + protected internal void MarkIndirectlyCalledMethod (MethodDefinition method, in DependencyInfo reason, in MessageOrigin origin) { - MarkMethod (method, reason, new MessageOrigin (sourceLocationMember)); + MarkMethod (method, reason, origin); Annotations.MarkIndirectlyCalledMethod (method); } @@ -2497,25 +2497,33 @@ namespace Mono.Linker.Steps return method; } - protected internal void ProcessRequiresUnreferencedCode (MethodDefinition method, MessageOrigin origin, DependencyKind dependencyKind) + void ProcessRequiresUnreferencedCode (MethodDefinition method, in MessageOrigin origin, DependencyKind dependencyKind) { switch (dependencyKind) { case DependencyKind.AccessedViaReflection: - case DependencyKind.DirectCall: case DependencyKind.DynamicallyAccessedMember: case DependencyKind.DynamicDependency: case DependencyKind.ElementMethod: case DependencyKind.Ldftn: case DependencyKind.Ldvirtftn: - case DependencyKind.Newobj: case DependencyKind.TriggersCctorForCalledMethod: - case DependencyKind.VirtualCall: break; + // DirectCall, VirtualCall and NewObj are handled by ReflectionMethodBodyScanner + // This is necessary since the ReflectionMethodBodyScanner has intrinsic handling for some + // of the methods annotated with the attribute (for example Type.GetType) + // and it know when it's OK and when it needs a warning. In this place we don't know + // and would have to warn every time. + default: return; } + CheckAndReportRequiresUnreferencedCode (method, origin); + } + + internal void CheckAndReportRequiresUnreferencedCode (MethodDefinition method, in MessageOrigin origin) + { // If the caller of a method is already marked with `RequiresUnreferencedCodeAttribute` a new warning should not // be produced for the callee. if (origin.MemberDefinition != null && @@ -2523,7 +2531,7 @@ namespace Mono.Linker.Steps return; if (Annotations.TryGetLinkerAttribute (method, out RequiresUnreferencedCodeAttribute requiresUnreferencedCode)) { - string message = $"'{method.GetDisplayName ()}' method has 'RequiresUnreferencedCodeAttribute' which can break functionality when trimming application code."; + string message = $"Using method '{method.GetDisplayName ()}' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code."; if (!string.IsNullOrEmpty (requiresUnreferencedCode.Message)) message += $" {requiresUnreferencedCode.Message}."; diff --git a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectedNoWarningsAttribute.cs b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectedNoWarningsAttribute.cs new file mode 100644 index 000000000..e7eff5906 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectedNoWarningsAttribute.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace Mono.Linker.Tests.Cases.Expectations.Assertions +{ + [AttributeUsage ( + AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Constructor | AttributeTargets.Field, + AllowMultiple = false, + Inherited = false)] + public class ExpectedNoWarningsAttribute : EnableLoggerAttribute + { + public ExpectedNoWarningsAttribute () { } + public ExpectedNoWarningsAttribute (string warningCode) { } + } +} diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs b/test/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs index e2d589f7e..f2b635191 100644 --- a/test/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs +++ b/test/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs @@ -10,6 +10,7 @@ using BindingFlags = System.Reflection.BindingFlags; namespace Mono.Linker.Tests.Cases.DataFlow { [SkipKeptItemsValidation] + [ExpectedNoWarnings] public class GenericParameterDataFlow { public static void Main () @@ -95,7 +96,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow [RecognizedReflectionAccessPattern] - [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] public static Type ReturnRequiresPublicFields () { return typeof (T); @@ -235,6 +236,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow } } + [RecognizedReflectionAccessPattern] static void TestBaseTypeGenericRequirements () { new DerivedTypeWithInstantiatedGenericOnBase (); diff --git a/test/Mono.Linker.Tests.Cases/LinkAttributes/LinkAttributeErrorCases.cs b/test/Mono.Linker.Tests.Cases/LinkAttributes/LinkAttributeErrorCases.cs index 6f2e214f7..0be400309 100644 --- a/test/Mono.Linker.Tests.Cases/LinkAttributes/LinkAttributeErrorCases.cs +++ b/test/Mono.Linker.Tests.Cases/LinkAttributes/LinkAttributeErrorCases.cs @@ -21,7 +21,6 @@ namespace Mono.Linker.Tests.Cases.LinkAttributes [ExpectedWarning ("IL2023", "GetTypeMethod", FileName = "LinkAttributeErrorCases.xml")] [ExpectedWarning ("IL2024", "methodParameter", "MethodWithParameter", FileName = "LinkAttributeErrorCases.xml")] [ExpectedWarning ("IL2029", FileName = "LinkAttributeErrorCases.xml")] - [ExpectedWarning ("IL2029", FileName = "LinkAttributeErrorCases.xml")] [ExpectedWarning ("IL2051", FileName = "LinkAttributeErrorCases.xml")] [ExpectedWarning ("IL2052", "NonExistentPropertyName", FileName = "LinkAttributeErrorCases.xml")] [ExpectedWarning ("IL2053", "StringValue", "IntProperty", FileName = "LinkAttributeErrorCases.xml")] diff --git a/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs b/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs index 1a7ed553f..be5ab3118 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/TypeUsedViaReflection.cs @@ -7,6 +7,7 @@ using Mono.Linker.Tests.Cases.Expectations.Metadata; namespace Mono.Linker.Tests.Cases.Reflection { [KeptMember (".cctor()")] + [ExpectedNoWarnings ()] public class TypeUsedViaReflection { public static void Main () @@ -357,6 +358,8 @@ namespace Mono.Linker.Tests.Cases.Reflection } [Kept] + [ExpectedWarning ("IL2026", "'System.Reflection.Assembly.GetType(String,Boolean)'")] + [ExpectedWarning ("IL2057", "'System.Type.GetType(String,Boolean)'")] static Type GetTypeFromAssembly (Assembly assembly, string name, bool caseSensitive) { return assembly == null ? Type.GetType (name, caseSensitive) : assembly.GetType (name, caseSensitive); diff --git a/test/Mono.Linker.Tests/TestCases/Dependencies/SortedWarnings.txt b/test/Mono.Linker.Tests/TestCases/Dependencies/SortedWarnings.txt index ff53d8753..67d635f2f 100644 --- a/test/Mono.Linker.Tests/TestCases/Dependencies/SortedWarnings.txt +++ b/test/Mono.Linker.Tests/TestCases/Dependencies/SortedWarnings.txt @@ -3,13 +3,8 @@ ILLink: Trim analysis warning IL2072: Mono.Linker.Tests.Cases.Warnings.Dependenc ILLink: Trim analysis warning IL2072: Mono.Linker.Tests.Cases.Warnings.Dependencies.TriggerWarnings_Lib.Warning2.get: '#0' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicMethods', 'DynamicallyAccessedMemberTypes.NonPublicMethods' in call to 'System.Linq.Expressions.MethodCallExpression System.Linq.Expressions.Expression::Call(System.Type,System.String,System.Type[],System.Linq.Expressions.Expression[])'. The return value of method 'Mono.Linker.Tests.Cases.Warnings.Dependencies.TriggerWarnings_Lib.TriggerUnrecognizedPattern()' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to. ILLink: Trim analysis warning IL2072: Mono.Linker.Tests.Cases.Warnings.Dependencies.TriggerWarnings_Lib.Main(): '#0' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicMethods', 'DynamicallyAccessedMemberTypes.NonPublicMethods' in call to 'System.Linq.Expressions.MethodCallExpression System.Linq.Expressions.Expression::Call(System.Type,System.String,System.Type[],System.Linq.Expressions.Expression[])'. The return value of method 'Mono.Linker.Tests.Cases.Warnings.Dependencies.TriggerWarnings_Lib.TriggerUnrecognizedPattern()' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to. ILLink: Trim analysis warning IL2072: Mono.Linker.Tests.Cases.Warnings.Dependencies.TriggerWarnings_Lib.Warning1(): '#0' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicMethods', 'DynamicallyAccessedMemberTypes.NonPublicMethods' in call to 'System.Linq.Expressions.MethodCallExpression System.Linq.Expressions.Expression::Call(System.Type,System.String,System.Type[],System.Linq.Expressions.Expression[])'. The return value of method 'Mono.Linker.Tests.Cases.Warnings.Dependencies.TriggerWarnings_Lib.TriggerUnrecognizedPattern()' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to. -ILLink: Trim analysis warning IL2026: Mono.Linker.Tests.Cases.Warnings.Individual.WarningsAreSorted.A.X(): 'System.Type.GetType(String)' method has 'RequiresUnreferencedCodeAttribute' which can break functionality when trimming application code. The type might be removed. ILLink: Trim analysis warning IL2075: Mono.Linker.Tests.Cases.Warnings.Individual.WarningsAreSorted.A.X(): 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicMethods' in call to 'System.Type.GetMethod(String)'. The return value of method 'System.Type.GetType(String)' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to. -ILLink: Trim analysis warning IL2026: Mono.Linker.Tests.Cases.Warnings.Individual.WarningsAreSorted.A.Y(): 'System.Type.GetType(String)' method has 'RequiresUnreferencedCodeAttribute' which can break functionality when trimming application code. The type might be removed. ILLink: Trim analysis warning IL2075: Mono.Linker.Tests.Cases.Warnings.Individual.WarningsAreSorted.A.Y(): 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicMethods' in call to 'System.Type.GetMethod(String)'. The return value of method 'System.Type.GetType(String)' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to. -ILLink: Trim analysis warning IL2026: Mono.Linker.Tests.Cases.Warnings.Individual.WarningsAreSorted.B.X(): 'System.Type.GetType(String)' method has 'RequiresUnreferencedCodeAttribute' which can break functionality when trimming application code. The type might be removed. ILLink: Trim analysis warning IL2075: Mono.Linker.Tests.Cases.Warnings.Individual.WarningsAreSorted.B.X(): 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicMethods' in call to 'System.Type.GetMethod(String)'. The return value of method 'System.Type.GetType(String)' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to. -ILLink: Trim analysis warning IL2026: Mono.Linker.Tests.Cases.Warnings.Individual.WarningsAreSorted.B.Y(): 'System.Type.GetType(String)' method has 'RequiresUnreferencedCodeAttribute' which can break functionality when trimming application code. The type might be removed. ILLink: Trim analysis warning IL2075: Mono.Linker.Tests.Cases.Warnings.Individual.WarningsAreSorted.B.Y(): 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicMethods' in call to 'System.Type.GetMethod(String)'. The return value of method 'System.Type.GetType(String)' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to. -ILLink: Trim analysis warning IL2026: Mono.Linker.Tests.Cases.Warnings.Individual.WarningsAreSorted.B.Z(): 'System.Type.GetType(String)' method has 'RequiresUnreferencedCodeAttribute' which can break functionality when trimming application code. The type might be removed. ILLink: Trim analysis warning IL2075: Mono.Linker.Tests.Cases.Warnings.Individual.WarningsAreSorted.B.Z(): 'this' argument does not satisfy 'DynamicallyAccessedMemberTypes.PublicMethods' in call to 'System.Type.GetMethod(String)'. The return value of method 'System.Type.GetType(String)' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.
\ No newline at end of file diff --git a/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs b/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs index 32177b27c..03fe04760 100644 --- a/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs +++ b/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs @@ -606,6 +606,7 @@ namespace Mono.Linker.Tests.TestCasesRunner void VerifyLoggedMessages (AssemblyDefinition original, LinkerTestLogger logger, bool checkRemainingErrors) { List<MessageContainer> loggedMessages = logger.GetLoggedMessages (); + List<(IMemberDefinition, CustomAttribute)> expectedNoWarningsAttributes = new List<(IMemberDefinition, CustomAttribute)> (); foreach (var testType in original.AllDefinedTypes ()) { foreach (var attrProvider in testType.AllMembers ().Append (testType)) { foreach (var attr in attrProvider.CustomAttributes) { @@ -644,60 +645,117 @@ namespace Mono.Linker.Tests.TestCasesRunner case nameof (ExpectedWarningAttribute): { var expectedWarningCode = (string) attr.GetConstructorArgumentValue (0); if (!expectedWarningCode.StartsWith ("IL")) { - Assert.Fail ($"The warning code specified in ExpectedWarning attribute must start with the 'IL' prefix. Specified value: '{expectedWarningCode}'."); + Assert.Fail ($"The warning code specified in {nameof (ExpectedWarningAttribute)} must start with the 'IL' prefix. Specified value: '{expectedWarningCode}'."); } - - int expectedWarningCodeNumber = int.Parse (expectedWarningCode.Substring (2)); var expectedMessageContains = ((CustomAttributeArgument[]) attr.GetConstructorArgumentValue (1)).Select (a => (string) a.Value).ToArray (); - string fileName = (string) attr.GetPropertyValue ("FileName"); int? sourceLine = (int?) attr.GetPropertyValue ("SourceLine"); int? sourceColumn = (int?) attr.GetPropertyValue ("SourceColumn"); + int expectedWarningCodeNumber = int.Parse (expectedWarningCode.Substring (2)); var actualMethod = attrProvider as MethodDefinition; - Assert.IsTrue ( - logger.GetLoggedMessages ().Any (mc => { - if (mc.Category != MessageCategory.Warning || mc.Code != expectedWarningCodeNumber) + var matchedMessages = loggedMessages.Where (mc => { + if (mc.Category != MessageCategory.Warning || mc.Code != expectedWarningCodeNumber) + return false; + + foreach (var expectedMessage in expectedMessageContains) + if (!mc.Text.Contains (expectedMessage)) return false; - foreach (var expectedMessage in expectedMessageContains) - if (!mc.Text.Contains (expectedMessage)) - return false; + if (fileName != null) { + // Note: string.Compare(string, StringComparison) doesn't exist in .NET Framework API set + if (mc.Origin?.FileName?.IndexOf (fileName, StringComparison.OrdinalIgnoreCase) < 0) + return false; - if (fileName != null) { - // Note: string.Compare(string, StringComparison) doesn't exist in .NET Framework API set - if (mc.Origin?.FileName?.IndexOf (fileName, StringComparison.OrdinalIgnoreCase) < 0) - return false; + if (sourceLine != null && mc.Origin?.SourceLine != sourceLine.Value) + return false; - if (sourceLine != null && mc.Origin?.SourceLine != sourceLine.Value) - return false; + if (sourceColumn != null && mc.Origin?.SourceColumn != sourceColumn.Value) + return false; + } else { + if (mc.Origin?.MemberDefinition?.FullName == attrProvider.FullName) + return true; - if (sourceColumn != null && mc.Origin?.SourceColumn != sourceColumn.Value) - return false; - } else { - if (mc.Origin?.MemberDefinition?.FullName == attrProvider.FullName) - return true; + if (loggedMessages.Any (m => m.Text.Contains (attrProvider.FullName))) + return true; - if (loggedMessages.Any (m => m.Text.Contains (attrProvider.FullName))) - return true; + return false; + } - return false; - } + return true; + }).ToList (); - return true; - }), + Assert.IsTrue ( + matchedMessages.Count > 0, $"Expected to find warning: {(fileName != null ? (fileName + (sourceLine != null ? $"({sourceLine},{sourceColumn})" : "")) + ": " : "")}" + $"warning {expectedWarningCode}: {(fileName == null ? (actualMethod?.GetDisplayName () ?? attrProvider.FullName) + ": " : "")}" + $"and message containing {string.Join (" ", expectedMessageContains.Select (m => "'" + m + "'"))}, " + $"but no such message was found.{Environment.NewLine}Logged messages:{Environment.NewLine}{string.Join (Environment.NewLine, loggedMessages)}"); + + foreach (var matchedMessage in matchedMessages) + loggedMessages.Remove (matchedMessage); + } + break; + + // These are validated in VerifyRecordedReflectionPatterns as well, but we need to remove any warnings these might refer to from the list + // so that we can correctly validate presense of warnings + case nameof (UnrecognizedReflectionAccessPatternAttribute): { + string expectedWarningCode = null; + if (attr.ConstructorArguments.Count >= 5) { + expectedWarningCode = (string) attr.ConstructorArguments[4].Value; + if (expectedWarningCode != null && !expectedWarningCode.StartsWith ("IL")) + Assert.Fail ($"The warning code specified in {nameof (UnrecognizedReflectionAccessPatternAttribute)} must start with the 'IL' prefix. Specified value: '{expectedWarningCode}'"); + } + + if (expectedWarningCode != null) { + int expectedWarningCodeNumber = int.Parse (expectedWarningCode.Substring (2)); + + var matchedMessages = loggedMessages.Where (mc => mc.Category == MessageCategory.Warning && mc.Code == expectedWarningCodeNumber).ToList (); + foreach (var matchedMessage in matchedMessages) + loggedMessages.Remove (matchedMessage); + } } break; + + case nameof (ExpectedNoWarningsAttribute): + // Postpone processing of negative checks, to make it possible to mark some warnings as expected (will be removed from the list above) + // and then do the negative check on the rest. + expectedNoWarningsAttributes.Add ((attrProvider, attr)); + break; } } } } + foreach ((var attrProvider, var attr) in expectedNoWarningsAttributes) { + var unexpectedWarningCode = attr.ConstructorArguments.Count == 0 ? (string) null : (string) attr.GetConstructorArgumentValue (0); + if (unexpectedWarningCode != null && !unexpectedWarningCode.StartsWith ("IL")) { + Assert.Fail ($"The warning code specified in ExpectedWarning attribute must start with the 'IL' prefix. Specified value: '{unexpectedWarningCode}'."); + } + + int? unexpectedWarningCodeNumber = unexpectedWarningCode == null ? null : int.Parse (unexpectedWarningCode.Substring (2)); + + MessageContainer? unexpectedWarningMessage = null; + foreach (var mc in logger.GetLoggedMessages ()) { + if (mc.Category != MessageCategory.Warning) + continue; + + if (unexpectedWarningCodeNumber != null && unexpectedWarningCodeNumber.Value != mc.Code) + continue; + + // This is a hacky way to say anything in the "subtree" of the attrProvider + if (mc.Origin?.MemberDefinition?.FullName.Contains (attrProvider.FullName) != true) + continue; + + unexpectedWarningMessage = mc; + break; + } + + Assert.IsNull (unexpectedWarningMessage, + $"Unexpected warning found: {unexpectedWarningMessage}"); + } + if (checkRemainingErrors) { var remainingErrors = loggedMessages.Where (m => Regex.IsMatch (m.ToString (), @".*(error | warning): \d{4}.*")); Assert.IsEmpty (remainingErrors, $"Found unexpected errors:{Environment.NewLine}{string.Join (Environment.NewLine, remainingErrors)}"); @@ -811,7 +869,7 @@ namespace Mono.Linker.Tests.TestCasesRunner var codeString = (string) attr.ConstructorArguments[4].Value; if (codeString != null) { if (!codeString.StartsWith ("IL")) - throw new InvalidOperationException ($"invalid message code {codeString}"); + Assert.Fail ($"The warning code specified in {nameof (UnrecognizedReflectionAccessPatternAttribute)} must start with the 'IL' prefix. Specified value: '{codeString}'"); expectedMessageCode = int.Parse (codeString.Substring (2)); } } |