diff --git a/docs/error-codes.md b/docs/error-codes.md index ba6d39f93c5b..cafb2a506cd2 100644 --- a/docs/error-codes.md +++ b/docs/error-codes.md @@ -1689,7 +1689,7 @@ The only scopes supported on global unconditional suppressions are 'module', 'ty it is assumed that the suppression is put on the module. Global unconditional suppressions using invalid scopes are ignored. ```C# -// Invalid scope 'method' used in 'UnconditionalSuppressMessageAttribute' on module 'Warning' with target 'MyTarget'. +// IL2108: Invalid scope 'method' used in 'UnconditionalSuppressMessageAttribute' on module 'Warning' with target 'MyTarget'. [module: UnconditionalSuppressMessage ("Test suppression with invalid scope", "IL2026", Scope = "method", Target = "MyTarget")] class Warning @@ -1722,6 +1722,37 @@ class Warning class Derived : UnsafeClass {} ``` +#### `IL2110`: Trim analysis: Field 'field' with 'DynamicallyAccessedMembersAttribute' is accessed via reflection. Trimmer can't guarantee availability of the requirements of the field. + +- Trimmer currently can't guarantee that all requirements of the `DynamicallyAccessedMembersAttribute` are fulfilled if the field is accessed via reflection. + +```C# +[DynamicallyAccessedMembers(DynamicallyAccessedMemeberTypes.PublicMethods)] +Type _field; + +void TestMethod() +{ + // IL2110: Field '_field' with 'DynamicallyAccessedMembersAttribute' is accessed via reflection. Trimmer can't guarantee availability of the requirements of the field. + typeof(Test).GetField("_field"); +} +``` + +#### `IL2111`: Trim analysis: Method 'method' with parameters or return value with `DynamicallyAccessedMembersAttribute` is accessed via reflection. Trimmer can't guarantee availability of the requirements of the field. + +- Trimmer currently can't guarantee that all requirements of the `DynamicallyAccessedMembersAttribute` are fulfilled if the method is accessed via reflection. + +```C# +void MethodWithRequirements([DynamicallyAccessedMembers(DynamicallyAccessedMemeberTypes.PublicMethods)] Type type) +{ +} + +void TestMethod() +{ + // IL2111: Method 'MethodWithRequirements' with parameters or return value with `DynamicallyAccessedMembersAttribute` is accessed via reflection. Trimmer can't guarantee availability of the requirements of the field. + typeof(Test).GetMethod("MethodWithRequirements"); +} +``` + ## 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/linker/Linker.Dataflow/FlowAnnotations.cs b/src/linker/Linker.Dataflow/FlowAnnotations.cs index 898c7074b92a..dc8b5129100d 100644 --- a/src/linker/Linker.Dataflow/FlowAnnotations.cs +++ b/src/linker/Linker.Dataflow/FlowAnnotations.cs @@ -23,20 +23,14 @@ public FlowAnnotations (LinkContext context) _hierarchyInfo = new TypeHierarchyCache (context); } - public bool RequiresDataFlowAnalysis (MethodDefinition method) - { - return GetAnnotations (method.DeclaringType).TryGetAnnotation (method, out _); - } + public bool RequiresDataFlowAnalysis (MethodDefinition method) => + GetAnnotations (method.DeclaringType).TryGetAnnotation (method, out _); - public bool RequiresDataFlowAnalysis (FieldDefinition field) - { - return GetAnnotations (field.DeclaringType).TryGetAnnotation (field, out _); - } + public bool RequiresDataFlowAnalysis (FieldDefinition field) => + GetAnnotations (field.DeclaringType).TryGetAnnotation (field, out _); - public bool RequiresDataFlowAnalysis (GenericParameter genericParameter) - { - return GetGenericParameterAnnotation (genericParameter) != DynamicallyAccessedMemberTypes.None; - } + public bool RequiresDataFlowAnalysis (GenericParameter genericParameter) => + GetGenericParameterAnnotation (genericParameter) != DynamicallyAccessedMemberTypes.None; /// /// Retrieves the annotations for the given parameter. @@ -45,35 +39,31 @@ public bool RequiresDataFlowAnalysis (GenericParameter genericParameter) /// public DynamicallyAccessedMemberTypes GetParameterAnnotation (MethodDefinition method, int parameterIndex) { - if (GetAnnotations (method.DeclaringType).TryGetAnnotation (method, out var annotation) && annotation.ParameterAnnotations != null) { + if (GetAnnotations (method.DeclaringType).TryGetAnnotation (method, out var annotation) && + annotation.ParameterAnnotations != null) return annotation.ParameterAnnotations[parameterIndex]; - } return DynamicallyAccessedMemberTypes.None; } public DynamicallyAccessedMemberTypes GetReturnParameterAnnotation (MethodDefinition method) { - if (GetAnnotations (method.DeclaringType).TryGetAnnotation (method, out var annotation)) { + if (GetAnnotations (method.DeclaringType).TryGetAnnotation (method, out var annotation)) return annotation.ReturnParameterAnnotation; - } return DynamicallyAccessedMemberTypes.None; } public DynamicallyAccessedMemberTypes GetFieldAnnotation (FieldDefinition field) { - if (GetAnnotations (field.DeclaringType).TryGetAnnotation (field, out var annotation)) { + if (GetAnnotations (field.DeclaringType).TryGetAnnotation (field, out var annotation)) return annotation.Annotation; - } return DynamicallyAccessedMemberTypes.None; } - public DynamicallyAccessedMemberTypes GetTypeAnnotation (TypeDefinition type) - { - return GetAnnotations (type).TypeAnnotation; - } + public DynamicallyAccessedMemberTypes GetTypeAnnotation (TypeDefinition type) => + GetAnnotations (type).TypeAnnotation; public DynamicallyAccessedMemberTypes GetGenericParameterAnnotation (GenericParameter genericParameter) { @@ -93,6 +83,17 @@ public DynamicallyAccessedMemberTypes GetGenericParameterAnnotation (GenericPara return DynamicallyAccessedMemberTypes.None; } + public bool ShouldWarnWhenAccessedForReflection (MethodDefinition method) => + GetAnnotations (method.DeclaringType).TryGetAnnotation (method, out var annotation) && + (annotation.ParameterAnnotations != null || annotation.ReturnParameterAnnotation != DynamicallyAccessedMemberTypes.None); + + public bool MethodHasNoAnnotatedParameters (MethodDefinition method) => + GetAnnotations (method.DeclaringType).TryGetAnnotation (method, out var annotation) && + annotation.ParameterAnnotations == null; + + public bool ShouldWarnWhenAccessedForReflection (FieldDefinition field) => + GetAnnotations (field.DeclaringType).TryGetAnnotation (field, out _); + TypeAnnotations GetAnnotations (TypeDefinition type) { if (!_annotations.TryGetValue (type, out TypeAnnotations value)) { diff --git a/src/linker/Linker.Steps/MarkStep.cs b/src/linker/Linker.Steps/MarkStep.cs index 39f70f6225c5..3b779d6bf2ba 100644 --- a/src/linker/Linker.Steps/MarkStep.cs +++ b/src/linker/Linker.Steps/MarkStep.cs @@ -1557,6 +1557,23 @@ void MarkField (FieldDefinition field, in DependencyInfo reason) Annotations.Mark (field, reason); } + switch (reason.Kind) { + case DependencyKind.AccessedViaReflection: + case DependencyKind.DynamicDependency: + case DependencyKind.DynamicallyAccessedMember: + case DependencyKind.InteropMethodDependency: + if (_context.Annotations.FlowAnnotations.ShouldWarnWhenAccessedForReflection (field) && + !ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode ()) + _context.LogWarning ( + $"Field '{field.GetDisplayName ()}' with 'DynamicallyAccessedMembersAttribute' is accessed via reflection. Trimmer can't guarantee availability of the requirements of the field.", + 2110, + _scopeStack.CurrentScope.Origin, + MessageSubCategory.TrimAnalysis); + + break; + } + + if (CheckProcessed (field)) return; @@ -2711,12 +2728,12 @@ protected virtual MethodDefinition MarkMethod (MethodReference reference, Depend // Use the original reason as it's important to correctly generate warnings // the updated reason is only useful for better tracking of dependencies. - ProcessRequiresUnreferencedCode (method, originalReasonKind); + ProcessAnalysisAnnotationsForMethod (method, originalReasonKind); return method; } - void ProcessRequiresUnreferencedCode (MethodDefinition method, DependencyKind dependencyKind) + void ProcessAnalysisAnnotationsForMethod (MethodDefinition method, DependencyKind dependencyKind) { switch (dependencyKind) { // DirectCall, VirtualCall and NewObj are handled by ReflectionMethodBodyScanner @@ -2773,24 +2790,85 @@ void ProcessRequiresUnreferencedCode (MethodDefinition method, DependencyKind de return; default: - // 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) - // 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 - // All other cases have the potential of us missing a warning if we don't report it // It is possible that in some cases we may report the same warning twice, but that's better than not reporting it. break; } - // All override methods should have the same annotations as their base methods (else we will produce warning IL2046.) - // When marking override methods with RequiresUnreferencedCode on a type annotated with DynamicallyAccessedMembers, - // we should only issue a warning for the base method. - if (dependencyKind != DependencyKind.DynamicallyAccessedMember || - !method.IsVirtual || - Annotations.GetBaseMethods (method) == null) - CheckAndReportRequiresUnreferencedCode (method); + // All override methods should have the same annotations as their base methods + // (else we will produce warning IL2046 or IL2092 or some other warning). + // When marking override methods via DynamicallyAccessedMembers, we should only issue a warning for the base method. + if (dependencyKind == DependencyKind.DynamicallyAccessedMember && + method.IsVirtual && + Annotations.GetBaseMethods (method) != null) + return; + + CheckAndReportRequiresUnreferencedCode (method); + + if (_context.Annotations.FlowAnnotations.ShouldWarnWhenAccessedForReflection (method)) { + // If the current scope has analysis warnings suppressed, don't generate any + if (ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode ()) + return; + + // ReflectionMethodBodyScanner handles more cases for data flow annotations + // so don't warn for those. + switch (dependencyKind) { + case DependencyKind.AttributeConstructor: + case DependencyKind.AttributeProperty: + return; + + default: + break; + } + + // If the method only has annotation on the return value and it's not virtual avoid warning. + // Return value annotations are "consumed" by the caller of a method, and as such there is nothing + // wrong calling these dynamically. The only problem can happen if something overrides a virtual + // method with annotated return value at runtime - in this case the trimmer can't validate + // that the method will return only types which fulfill the annotation's requirements. + // For example: + // class BaseWithAnnotation + // { + // [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] + // public abstract Type GetTypeWithFields(); + // } + // + // class UsingTheBase + // { + // public void PrintFields(Base base) + // { + // // No warning here - GetTypeWithFields is correctly annotated to allow GetFields on the return value. + // Console.WriteLine(string.Join(" ", base.GetTypeWithFields().GetFields().Select(f => f.Name))); + // } + // } + // + // If at runtime (through ref emit) something generates code like this: + // class DerivedAtRuntimeFromBase + // { + // // No point in adding annotation on the return value - nothing will look at it anyway + // // Linker will not see this code, so there are no checks + // public override Type GetTypeWithFields() { return typeof(TestType); } + // } + // + // If TestType from above is trimmed, it may note have all its fields, and there would be no warnings generated. + // But there has to be code like this somewhere in the app, in order to generate the override: + // class RuntimeTypeGenerator + // { + // public MethodInfo GetBaseMethod() + // { + // // This must warn - that the GetTypeWithFields has annotation on the return value + // return typeof(BaseWithAnnotation).GetMethod("GetTypeWithFields"); + // } + // } + if (!method.IsVirtual && _context.Annotations.FlowAnnotations.MethodHasNoAnnotatedParameters (method)) + return; + + _context.LogWarning ( + $"Method '{method.GetDisplayName ()}' with parameters or return value with `DynamicallyAccessedMembersAttribute` is accessed via reflection. Trimmer can't guarantee availability of the requirements of the field.", + 2111, + _scopeStack.CurrentScope.Origin, + MessageSubCategory.TrimAnalysis); + } } internal bool ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode () diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/AnnotatedMembersAccessedViaReflection.cs b/test/Mono.Linker.Tests.Cases/DataFlow/AnnotatedMembersAccessedViaReflection.cs new file mode 100644 index 000000000000..f94b9a45867e --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/DataFlow/AnnotatedMembersAccessedViaReflection.cs @@ -0,0 +1,553 @@ +// 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; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Linq.Expressions; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Helpers; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + [SkipKeptItemsValidation] + [ExpectedNoWarnings] + class AnnotatedMembersAccessedViaReflection + { + public static void Main () + { + AnnotatedField.Test (); + AnnotatedMethodParameters.Test (); + AnnotatedMethodReturnValue.Test (); + AnnotatedProperty.Test (); + AnnotatedGenerics.Test (); + AnnotationOnGenerics.Test (); + AnnotationOnInteropMethod.Test (); + AccessThroughLdToken.Test (); + } + + class AnnotatedField + { + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + public static Type _annotatedField; + + [ExpectedWarning ("IL2110", nameof (_annotatedField))] + static void Reflection () + { + typeof (AnnotatedField).GetField ("_annotatedField").SetValue (null, typeof (TestType)); + } + + [RequiresUnreferencedCode ("test")] + static void ReflectionSuppressedByRUC () + { + typeof (AnnotatedField).GetField ("_annotatedField").SetValue (null, typeof (TestType)); + } + + [ExpectedWarning ("IL2110", nameof (_annotatedField))] + static void ReflectionReadOnly () + { + typeof (AnnotatedField).GetField ("_annotatedField").GetValue (null); + } + + [ExpectedWarning ("IL2110", nameof (_annotatedField))] + [DynamicDependency (DynamicallyAccessedMemberTypes.PublicFields, typeof (AnnotatedField))] + static void DynamicDependency () + { + } + + [RequiresUnreferencedCode ("test")] + [DynamicDependency (DynamicallyAccessedMemberTypes.PublicFields, typeof (AnnotatedField))] + static void DynamicDependencySuppressedByRUC () + { + } + + [ExpectedWarning ("IL2110", nameof (_annotatedField))] + [DynamicDependency (nameof (_annotatedField), typeof (AnnotatedField))] + static void DynamicDependencyByName () + { + } + + [ExpectedWarning ("IL2110", nameof (_annotatedField))] + static void DynamicallyAccessedMembers () + { + typeof (AnnotatedField).RequiresPublicFields (); + } + + [RequiresUnreferencedCode ("test")] + static void DynamicallyAccessedMembersSuppressedByRUC () + { + typeof (AnnotatedField).RequiresPublicFields (); + } + + [UnconditionalSuppressMessage ("test", "IL2026")] + public static void Test () + { + Reflection (); + ReflectionSuppressedByRUC (); + ReflectionReadOnly (); + DynamicDependency (); + DynamicDependencySuppressedByRUC (); + DynamicDependencyByName (); + DynamicallyAccessedMembers (); + DynamicallyAccessedMembersSuppressedByRUC (); + } + } + + class AnnotatedMethodParameters + { + public static void MethodWithSingleAnnotatedParameter ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type type) + { } + + class AttributeWithConstructorWithAnnotation : Attribute + { + public AttributeWithConstructorWithAnnotation ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type type) + { } + } + + [ExpectedWarning ("IL2111", nameof (MethodWithSingleAnnotatedParameter))] + static void Reflection () + { + typeof (AnnotatedMethodParameters).GetMethod (nameof (MethodWithSingleAnnotatedParameter)).Invoke (null, null); + } + + [RequiresUnreferencedCode ("test")] + static void ReflectionSuppressedByRUC () + { + typeof (AnnotatedMethodParameters).GetMethod (nameof (MethodWithSingleAnnotatedParameter)).Invoke (null, null); + } + + // Should not warn, there's nothing wrong about this + [AttributeWithConstructorWithAnnotation (typeof (TestType))] + static void AnnotatedAttributeConstructor () + { + } + + [ExpectedWarning ("IL2111", nameof (MethodWithSingleAnnotatedParameter))] + [DynamicDependency (DynamicallyAccessedMemberTypes.PublicMethods, typeof (AnnotatedMethodParameters))] + static void DynamicDependency () + { + } + + [RequiresUnreferencedCode ("test")] + [DynamicDependency (DynamicallyAccessedMemberTypes.PublicMethods, typeof (AnnotatedMethodParameters))] + static void DynamicDependencySuppressedByRUC () + { + } + + [ExpectedWarning ("IL2111", nameof (MethodWithSingleAnnotatedParameter))] + [DynamicDependency (nameof (MethodWithSingleAnnotatedParameter), typeof (AnnotatedMethodParameters))] + static void DynamicDependencyByName () + { + } + + [ExpectedWarning ("IL2111", nameof (MethodWithSingleAnnotatedParameter))] + static void DynamicallyAccessedMembers () + { + typeof (AnnotatedMethodParameters).RequiresPublicMethods (); + } + + [RequiresUnreferencedCode ("test")] + static void DynamicallyAccessedMembersSuppressedByRUC () + { + typeof (AnnotatedMethodParameters).RequiresPublicMethods (); + } + + [ExpectedWarning ("IL2111", nameof (MethodWithSingleAnnotatedParameter))] + static void Ldftn () + { + var _ = new Action (AnnotatedMethodParameters.MethodWithSingleAnnotatedParameter); + } + + interface IWithAnnotatedMethod + { + public void AnnotatedMethod ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors)] Type type); + } + + [ExpectedWarning ("IL2111", nameof (IWithAnnotatedMethod.AnnotatedMethod))] + static void Ldvirtftn () + { + IWithAnnotatedMethod instance = null; + var _ = new Action (instance.AnnotatedMethod); + } + + [UnconditionalSuppressMessage ("test", "IL2026")] + public static void Test () + { + Reflection (); + ReflectionSuppressedByRUC (); + DynamicDependency (); + DynamicDependencySuppressedByRUC (); + DynamicDependencyByName (); + DynamicallyAccessedMembers (); + DynamicallyAccessedMembersSuppressedByRUC (); + Ldftn (); + Ldvirtftn (); + } + } + + class AnnotatedMethodReturnValue + { + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + public static Type StaticMethodWithAnnotatedReturnValue () => null; + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + public Type InstanceMethodWithAnnotatedReturnValue () => null; + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + public virtual Type VirtualMethodWithAnnotatedReturnValue () => null; + + // Only virtual methods should warn - the problem is only possible if something overrides a virtual method. + // Getting an annotated value in itself is not dangerous in any way. + + static void ReflectionOnStatic () + { + typeof (AnnotatedMethodReturnValue).GetMethod (nameof (StaticMethodWithAnnotatedReturnValue)).Invoke (null, null); + } + + static void ReflectionOnInstance () + { + typeof (AnnotatedMethodReturnValue).GetMethod (nameof (InstanceMethodWithAnnotatedReturnValue)).Invoke (null, null); + } + + [ExpectedWarning ("IL2111", nameof (VirtualMethodWithAnnotatedReturnValue))] + static void ReflectionOnVirtual () + { + typeof (AnnotatedMethodReturnValue).GetMethod (nameof (VirtualMethodWithAnnotatedReturnValue)).Invoke (null, null); + } + + [RequiresUnreferencedCode ("test")] + static void ReflectionOnVirtualSuppressedByRUC () + { + typeof (AnnotatedMethodReturnValue).GetMethod (nameof (VirtualMethodWithAnnotatedReturnValue)).Invoke (null, null); + } + + [ExpectedWarning ("IL2111", nameof (VirtualMethodWithAnnotatedReturnValue))] + [DynamicDependency (DynamicallyAccessedMemberTypes.PublicMethods, typeof (AnnotatedMethodReturnValue))] + static void DynamicDependency () + { + } + + [RequiresUnreferencedCode ("test")] + [DynamicDependency (DynamicallyAccessedMemberTypes.PublicMethods, typeof (AnnotatedMethodReturnValue))] + static void DynamicDependencySuppressedByRUC () + { + } + + [DynamicDependency (nameof (StaticMethodWithAnnotatedReturnValue), typeof (AnnotatedMethodReturnValue))] + static void DynamicDependencyByNameOnStatic () + { + } + + [DynamicDependency (nameof (InstanceMethodWithAnnotatedReturnValue), typeof (AnnotatedMethodReturnValue))] + static void DynamicDependencyByNameOnInstance () + { + } + + [ExpectedWarning ("IL2111", nameof (VirtualMethodWithAnnotatedReturnValue))] + [DynamicDependency (nameof (VirtualMethodWithAnnotatedReturnValue), typeof (AnnotatedMethodReturnValue))] + static void DynamicDependencyByNameOnVirtual () + { + } + + [ExpectedWarning ("IL2111", nameof (VirtualMethodWithAnnotatedReturnValue))] + static void DynamicallyAccessedMembers () + { + typeof (AnnotatedMethodReturnValue).RequiresPublicMethods (); + } + + [RequiresUnreferencedCode ("test")] + static void DynamicallyAccessedMembersSuppressedByRUC () + { + typeof (AnnotatedMethodReturnValue).RequiresPublicMethods (); + } + + static void LdftnOnStatic () + { + var _ = new Func (AnnotatedMethodReturnValue.StaticMethodWithAnnotatedReturnValue); + } + + static void LdftnOnInstance () + { + var _ = new Func ((new AnnotatedMethodReturnValue ()).InstanceMethodWithAnnotatedReturnValue); + } + + [ExpectedWarning ("IL2111", nameof (VirtualMethodWithAnnotatedReturnValue))] + static void LdftnOnVirtual () + { + var _ = new Func ((new AnnotatedMethodReturnValue ()).VirtualMethodWithAnnotatedReturnValue); + } + + [UnconditionalSuppressMessage ("test", "IL2026")] + public static void Test () + { + ReflectionOnStatic (); + ReflectionOnInstance (); + ReflectionOnVirtual (); + ReflectionOnVirtualSuppressedByRUC (); + DynamicDependency (); + DynamicDependencySuppressedByRUC (); + DynamicDependencyByNameOnStatic (); + DynamicDependencyByNameOnInstance (); + DynamicDependencyByNameOnVirtual (); + DynamicallyAccessedMembers (); + DynamicallyAccessedMembersSuppressedByRUC (); + LdftnOnStatic (); + LdftnOnInstance (); + LdftnOnVirtual (); + } + } + + class AnnotatedProperty + { + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicNestedTypes)] + public static Type PropertyWithAnnotation { get; set; } + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicEvents)] + public static Type PropertyWithAnnotationGetterOnly { get => null; } + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicEvents)] + public virtual Type VirtualPropertyWithAnnotationGetterOnly { get => null; } + + class AttributeWithPropertyWithAnnotation : Attribute + { + public AttributeWithPropertyWithAnnotation () { } + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] + public Type PropertyWithAnnotation { get; set; } + } + + [ExpectedWarning ("IL2111", nameof (PropertyWithAnnotation))] + static void ReflectionOnPropertyItself () + { + typeof (AnnotatedProperty).GetProperty (nameof (PropertyWithAnnotation)); + } + + [RequiresUnreferencedCode ("test")] + static void ReflectionOnPropertyItselfSuppressedByRUC () + { + typeof (AnnotatedProperty).GetProperty (nameof (PropertyWithAnnotation)); + } + + static void ReflectionOnPropertyWithGetterOnly () + { + typeof (AnnotatedProperty).GetProperty (nameof (PropertyWithAnnotationGetterOnly)); + } + + [ExpectedWarning ("IL2111", nameof (VirtualPropertyWithAnnotationGetterOnly))] + static void ReflectionOnPropertyWithGetterOnlyOnVirtual () + { + typeof (AnnotatedProperty).GetProperty (nameof (VirtualPropertyWithAnnotationGetterOnly)); + } + + static void ReflectionOnGetter () + { + typeof (AnnotatedProperty).GetMethod ("get_" + nameof (PropertyWithAnnotation)); + } + + [ExpectedWarning ("IL2111", nameof (PropertyWithAnnotation) + ".set")] + static void ReflectionOnSetter () + { + typeof (AnnotatedProperty).GetMethod ("set_" + nameof (PropertyWithAnnotation)); + } + + [ExpectedWarning ("IL2111", nameof (VirtualPropertyWithAnnotationGetterOnly) + ".get")] + static void ReflectionOnVirtualGetter () + { + typeof (AnnotatedProperty).GetMethod ("get_" + nameof (VirtualPropertyWithAnnotationGetterOnly)); + } + + // Should not warn - there's nothing wrong with this + [AttributeWithPropertyWithAnnotation (PropertyWithAnnotation = typeof (TestType))] + static void AnnotatedAttributeProperty () + { + } + + [ExpectedWarning ("IL2111", nameof (PropertyWithAnnotation) + ".set")] + [ExpectedWarning ("IL2111", nameof (VirtualPropertyWithAnnotationGetterOnly) + ".get")] + [DynamicDependency (DynamicallyAccessedMemberTypes.PublicProperties, typeof (AnnotatedProperty))] + static void DynamicDependency () + { + } + + [RequiresUnreferencedCode ("test")] + [DynamicDependency (DynamicallyAccessedMemberTypes.PublicProperties, typeof (AnnotatedProperty))] + static void DynamicDependencySuppressedByRUC () + { + } + + [ExpectedWarning ("IL2111", nameof (PropertyWithAnnotation) + ".set")] + [ExpectedWarning ("IL2111", nameof (VirtualPropertyWithAnnotationGetterOnly) + ".get")] + static void DynamicallyAccessedMembers () + { + typeof (AnnotatedProperty).RequiresPublicProperties (); + } + + [RequiresUnreferencedCode ("test")] + static void DynamicallyAccessedMembersSuppressedByRUC () + { + typeof (AnnotatedProperty).RequiresPublicProperties (); + } + + [UnconditionalSuppressMessage ("test", "IL2026")] + public static void Test () + { + ReflectionOnPropertyItself (); + ReflectionOnPropertyItselfSuppressedByRUC (); + ReflectionOnPropertyWithGetterOnly (); + ReflectionOnPropertyWithGetterOnlyOnVirtual (); + ReflectionOnGetter (); + ReflectionOnSetter (); + ReflectionOnVirtualGetter (); + AnnotatedAttributeProperty (); + DynamicDependency (); + DynamicDependencySuppressedByRUC (); + DynamicallyAccessedMembers (); + DynamicallyAccessedMembersSuppressedByRUC (); + } + } + + // Annotation on generic parameter + class AnnotatedGenerics + { + public static void GenericWithAnnotation< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.Interfaces)] T> () + { } + + static void ReflectionOnly () + { + // Should not warn - there's nothing wrong with asking for MethodInfo alone + typeof (AnnotatedGenerics).GetMethod (nameof (GenericWithAnnotation)); + } + + // Similarly to direct reflection - no warning expected + [DynamicDependency (DynamicallyAccessedMemberTypes.PublicMethods, typeof (AnnotatedGenerics))] + static void DynamicDependency () + { + } + + // Similarly to direct reflection - no warning expected + static void DynamicallyAccessedMembers () + { + typeof (AnnotatedGenerics).RequiresPublicMethods (); + } + + // This should produce IL2071 https://github.com/mono/linker/issues/2144 + [ExpectedWarning ("IL2070", "MakeGenericMethod")] + static void InstantiateGeneric (Type type = null) + { + // This should warn due to MakeGenericMethod - in this case the generic parameter is unannotated type + typeof (AnnotatedGenerics).GetMethod (nameof (GenericWithAnnotation)).MakeGenericMethod (type); + } + + public static void Test () + { + ReflectionOnly (); + DynamicDependency (); + DynamicallyAccessedMembers (); + InstantiateGeneric (); + } + } + + // Annotation on non-generic parameter but on generic methods + class AnnotationOnGenerics + { + class GenericWithAnnotatedMethod + { + public static void AnnotatedMethod ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type type) + { } + } + + public static void GenericMethodWithAnnotation ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type type) + { } + + [ExpectedWarning ("IL2111", nameof (GenericWithAnnotatedMethod.AnnotatedMethod))] + public static void GenericTypeWithStaticMethodViaLdftn () + { + var _ = new Action (GenericWithAnnotatedMethod.AnnotatedMethod); + } + + [ExpectedWarning ("IL2111", nameof (GenericMethodWithAnnotation))] + public static void GenericMethodWithAnnotationReflection () + { + typeof (AnnotationOnGenerics).GetMethod (nameof (GenericMethodWithAnnotation)); + } + + public static void GenericMethodWithAnnotationDirectCall () + { + // Should not warn, nothing wrong about this + GenericMethodWithAnnotation (typeof (TestType)); + } + + [ExpectedWarning ("IL2111", nameof (GenericMethodWithAnnotation))] + public static void GenericMethodWithAnnotationViaLdftn () + { + var _ = new Action (GenericMethodWithAnnotation); + } + + [ExpectedWarning ("IL2111", nameof (GenericMethodWithAnnotation))] + public static void GenericMethodDynamicallyAccessedMembers () + { + typeof (AnnotationOnGenerics).RequiresPublicMethods (); + } + + public static void Test () + { + GenericTypeWithStaticMethodViaLdftn (); + GenericMethodWithAnnotationReflection (); + GenericMethodWithAnnotationDirectCall (); + GenericMethodWithAnnotationViaLdftn (); + GenericMethodDynamicallyAccessedMembers (); + } + } + + class AnnotationOnInteropMethod + { + struct ValueWithAnnotatedField + { + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + public Type _typeField; + } + + [ExpectedWarning ("IL2110", nameof (ValueWithAnnotatedField._typeField))] + [DllImport ("nonexistent")] + static extern ValueWithAnnotatedField GetValueWithAnnotatedField (); + + [ExpectedWarning ("IL2110", nameof (ValueWithAnnotatedField._typeField))] + [DllImport ("nonexistent")] + static extern void AcceptValueWithAnnotatedField (ValueWithAnnotatedField value); + + public static void Test () + { + GetValueWithAnnotatedField (); + AcceptValueWithAnnotatedField (default (ValueWithAnnotatedField)); + } + } + + class AccessThroughLdToken + { + public virtual Type PropertyWithLdToken { + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + get { + return null; + } + } + + [ExpectedWarning ("IL2111", nameof (PropertyWithLdToken))] + public static void Test () + { + Expression> getter = () => (new AccessThroughLdToken ()).PropertyWithLdToken; + } + } + + class TestType { } + } +} diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/VirtualMethodHierarchyDataflowAnnotationValidation.cs b/test/Mono.Linker.Tests.Cases/DataFlow/VirtualMethodHierarchyDataflowAnnotationValidation.cs index c21c32b1d29b..25107c005ee2 100644 --- a/test/Mono.Linker.Tests.Cases/DataFlow/VirtualMethodHierarchyDataflowAnnotationValidation.cs +++ b/test/Mono.Linker.Tests.Cases/DataFlow/VirtualMethodHierarchyDataflowAnnotationValidation.cs @@ -13,6 +13,10 @@ namespace Mono.Linker.Tests.Cases.DataFlow { [SkipKeptItemsValidation] [SandboxDependency ("Dependencies/TestSystemTypeBase.cs")] + + // Suppress warnings about accessing methods with annotations via reflection - the test below does that a LOT + // (The test accessed these methods through DynamicallyAccessedMembers annotations which is effectively the same reflection access) + [UnconditionalSuppressMessage ("test", "IL2111")] class VirtualMethodHierarchyDataflowAnnotationValidation { public static void Main () diff --git a/test/Mono.Linker.Tests.Cases/Reflection/ExpressionCallString.cs b/test/Mono.Linker.Tests.Cases/Reflection/ExpressionCallString.cs index 142e82dd5720..b4b71d5ebfbf 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/ExpressionCallString.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/ExpressionCallString.cs @@ -117,21 +117,6 @@ static string GetUnknownString () [Kept] class TestUnknownType { - [Kept] - public static void PublicMethod () - { - } - - [Kept] - protected static void ProtectedMethod () - { - } - - [Kept] - private static void PrivateMethod () - { - } - [Kept] [UnrecognizedReflectionAccessPattern (typeof (Expression), nameof (Expression.Call), new Type[] { typeof (Type), typeof (string), typeof (Type[]), typeof (Expression[]) }, messageCode: "IL2072")] @@ -148,13 +133,13 @@ public static void Test () [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] static Type GetUnknownType () { - return typeof (TestUnknownType); + return typeof (TestType); } [Kept] static Type TriggerUnrecognizedPattern () { - return typeof (TestUnknownType); + return typeof (TestType); } } @@ -355,5 +340,8 @@ public static void Test () [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] static Type GetUnknownTypeWithRequrements () { return null; } } + + [Kept] + class TestType { } } }