diff --git a/docs/error-codes.md b/docs/error-codes.md index cafb2a506cd2..7e1af87fed5a 100644 --- a/docs/error-codes.md +++ b/docs/error-codes.md @@ -1753,6 +1753,68 @@ void TestMethod() } ``` +#### `IL2112` Trim analysis: 'DynamicallyAccessedMembersAttribute' on 'type' or one of its base types references 'member' which requires unreferenced code. [message]. [url] + +- 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 one of them is annotated with `RequiresUnreferencedCodeAttribute` which can break functionality when trimming. 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 member with `RequiresUnreferencedCodeAttribute`. + + ```C# + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] + public class AnnotatedType { + // Trim analysis warning IL2112: AnnotatedType.Method(): 'DynamicallyAccessedMembersAttribute' on 'AnnotatedType' or one of its + // base types references 'AnnotatedType.Method()' which requires unreferenced code. Using this member is trim unsafe. + [RequiresUnreferencedCode("Using this member is trim unsafe")] + public static void Method() { } + } + ``` + +#### `IL2113` Trim analysis: 'DynamicallyAccessedMembersAttribute' on 'type' or one of its base types references 'member' which requires unreferenced code. [message]. [url] + +- 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 `RequiresUnreferencedCodeAttribute` which can break functionality when trimming. 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. + + ```C# + public class BaseType { + [RequiresUnreferencedCode("Using this member is trim unsafe")] + public static void Method() { } + } + + // Trim analysis warning IL2113: AnnotatedType: 'DynamicallyAccessedMembersAttribute' on 'AnnotatedType' or one of its + // base types references 'BaseType.Method()' which requires unreferenced code. Using this member is trim unsafe. + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] + public class AnnotatedType : BaseType { + } + ``` + +#### `IL2114 ` 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 one of them 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 member with `DynamicallyAccessedMembersAttribute` requirements. + + ```C# + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] + public class AnnotatedType { + // Trim analysis warning IL2114: System.Type AnnotatedType::Field: 'DynamicallyAccessedMembersAttribute' on 'AnnotatedType' or one of its + // base types references 'System.Type AnnotatedType::Field' which has 'DynamicallyAccessedMembersAttribute' requirements . + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] + public static Type Field; + } + ``` + +#### `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. + + ```C# + public class BaseType { + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] + public static Type Field; + } + + // Trim analysis warning IL2115: AnnotatedType: 'DynamicallyAccessedMembersAttribute' on 'AnnotatedType' or one of its + // base types references 'System.Type BaseType::Field' which has 'DynamicallyAccessedMembersAttribute' requirements . + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] + public class AnnotatedType : BaseType { + } + ``` + ## 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/DynamicallyAccessedMembersTypeHierarchy.cs b/src/linker/Linker.Dataflow/DynamicallyAccessedMembersTypeHierarchy.cs index 4d310f192e3c..e3e871510520 100644 --- a/src/linker/Linker.Dataflow/DynamicallyAccessedMembersTypeHierarchy.cs +++ b/src/linker/Linker.Dataflow/DynamicallyAccessedMembersTypeHierarchy.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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. @@ -118,7 +118,8 @@ public DynamicallyAccessedMembersTypeHierarchy (LinkContext context, MarkStep ma // so we need to apply the annotation to this type as well using var _ = _scopeStack.PushScope (new MessageOrigin (type)); var reflectionMethodBodyScanner = new ReflectionMethodBodyScanner (_context, _markStep, _scopeStack); - var reflectionPatternContext = new ReflectionPatternContext (_context, true, _scopeStack.CurrentScope.Origin, type); + // Set up a context to report warnings on access to annotated members, with the annotated type as the origin. + var reflectionPatternContext = new ReflectionPatternContext (_context, reportingEnabled: true, _scopeStack.CurrentScope.Origin, type); reflectionMethodBodyScanner.ApplyDynamicallyAccessedMembersToType (ref reflectionPatternContext, type, annotation); reflectionPatternContext.Dispose (); } @@ -128,7 +129,6 @@ public DynamicallyAccessedMembersTypeHierarchy (LinkContext context, MarkStep ma public DynamicallyAccessedMemberTypes ApplyDynamicallyAccessedMembersToTypeHierarchy ( ReflectionMethodBodyScanner reflectionMethodBodyScanner, - ref ReflectionPatternContext reflectionPatternContext, TypeDefinition type) { Debug.Assert (_context.Annotations.IsMarked (type)); @@ -142,6 +142,9 @@ public DynamicallyAccessedMemberTypes ApplyDynamicallyAccessedMembersToTypeHiera return annotation; // Apply the effective annotation for the type + using var _ = _scopeStack.PushScope (new MessageOrigin (type)); + // Set up a context to report warnings on access to annotated members, with the annotated type as the origin. + var reflectionPatternContext = new ReflectionPatternContext (_context, reportingEnabled: true, _scopeStack.CurrentScope.Origin, type); reflectionMethodBodyScanner.ApplyDynamicallyAccessedMembersToType (ref reflectionPatternContext, type, annotation); // Mark it as applied in the cache @@ -174,7 +177,7 @@ public DynamicallyAccessedMemberTypes ApplyDynamicallyAccessedMembersToTypeHiera break; foreach (var candidateType in candidateTypes) { - ApplyDynamicallyAccessedMembersToTypeHierarchyInner (reflectionMethodBodyScanner, ref reflectionPatternContext, candidateType); + ApplyDynamicallyAccessedMembersToTypeHierarchyInner (reflectionMethodBodyScanner, candidateType); } } @@ -183,7 +186,6 @@ public DynamicallyAccessedMemberTypes ApplyDynamicallyAccessedMembersToTypeHiera bool ApplyDynamicallyAccessedMembersToTypeHierarchyInner ( ReflectionMethodBodyScanner reflectionMethodBodyScanner, - ref ReflectionPatternContext reflectionPatternContext, TypeDefinition type) { (var annotation, var applied) = GetCachedInfoForTypeInHierarchy (type); @@ -196,13 +198,13 @@ bool ApplyDynamicallyAccessedMembersToTypeHierarchyInner ( TypeDefinition baseType = type.BaseType?.Resolve (); if (baseType != null) - applied = ApplyDynamicallyAccessedMembersToTypeHierarchyInner (reflectionMethodBodyScanner, ref reflectionPatternContext, baseType); + applied = ApplyDynamicallyAccessedMembersToTypeHierarchyInner (reflectionMethodBodyScanner, baseType); if (!applied && type.HasInterfaces) { foreach (InterfaceImplementation iface in type.Interfaces) { var interfaceType = iface.InterfaceType.Resolve (); if (interfaceType != null) { - if (ApplyDynamicallyAccessedMembersToTypeHierarchyInner (reflectionMethodBodyScanner, ref reflectionPatternContext, interfaceType)) { + if (ApplyDynamicallyAccessedMembersToTypeHierarchyInner (reflectionMethodBodyScanner, interfaceType)) { applied = true; break; } @@ -211,6 +213,9 @@ bool ApplyDynamicallyAccessedMembersToTypeHierarchyInner ( } if (applied) { + using var _ = _scopeStack.PushScope (new MessageOrigin (type)); + // Set up a context to report warnings on access to annotated members, with the annotated type as the origin. + var reflectionPatternContext = new ReflectionPatternContext (_context, reportingEnabled: true, _scopeStack.CurrentScope.Origin, type); reflectionMethodBodyScanner.ApplyDynamicallyAccessedMembersToType (ref reflectionPatternContext, type, annotation); _typesInDynamicallyAccessedMembersHierarchy[type] = (annotation, true); } diff --git a/src/linker/Linker.Dataflow/FlowAnnotations.cs b/src/linker/Linker.Dataflow/FlowAnnotations.cs index dc8b5129100d..6fa50ed7b845 100644 --- a/src/linker/Linker.Dataflow/FlowAnnotations.cs +++ b/src/linker/Linker.Dataflow/FlowAnnotations.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// 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. @@ -65,6 +65,13 @@ public DynamicallyAccessedMemberTypes GetFieldAnnotation (FieldDefinition field) public DynamicallyAccessedMemberTypes GetTypeAnnotation (TypeDefinition type) => GetAnnotations (type).TypeAnnotation; + public bool ShouldWarnWhenAccessedForReflection (IMemberDefinition provider) => + provider switch { + MethodDefinition method => ShouldWarnWhenAccessedForReflection (method), + FieldDefinition field => ShouldWarnWhenAccessedForReflection (field), + _ => false + }; + public DynamicallyAccessedMemberTypes GetGenericParameterAnnotation (GenericParameter genericParameter) { TypeDefinition declaringType = _context.Resolve (genericParameter.DeclaringType); @@ -83,13 +90,55 @@ 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 ShouldWarnWhenAccessedForReflection (MethodDefinition method) + { + if (!GetAnnotations (method.DeclaringType).TryGetAnnotation (method, out var annotation)) + return false; + + if (annotation.ParameterAnnotations == null && annotation.ReturnParameterAnnotation == DynamicallyAccessedMemberTypes.None) + return false; - public bool MethodHasNoAnnotatedParameters (MethodDefinition method) => - GetAnnotations (method.DeclaringType).TryGetAnnotation (method, out var annotation) && - annotation.ParameterAnnotations == null; + // 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"); + // } + // } + return method.IsVirtual || annotation.ParameterAnnotations != null; + } public bool ShouldWarnWhenAccessedForReflection (FieldDefinition field) => GetAnnotations (field.DeclaringType).TryGetAnnotation (field, out _); diff --git a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs index 282ffb5bf100..0fffc00318b0 100644 --- a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs +++ b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs @@ -128,7 +128,7 @@ public void ApplyDynamicallyAccessedMembersToType (ref ReflectionPatternContext // Handle cases where a type has no members but annotations are to be applied to derived type members reflectionPatternContext.RecordHandledPattern (); - MarkTypeForDynamicallyAccessedMembers (ref reflectionPatternContext, type, annotation); + MarkTypeForDynamicallyAccessedMembers (ref reflectionPatternContext, type, annotation, DependencyKind.DynamicallyAccessedMemberOnType); } ValueNode GetValueNodeForCustomAttributeArgument (CustomAttributeArgument argument) @@ -1032,7 +1032,7 @@ public override bool HandleCall (MethodBody callingMethodBody, MethodReference c MarkType (ref reflectionContext, staticType); var annotation = _markStep.DynamicallyAccessedMembersTypeHierarchy - .ApplyDynamicallyAccessedMembersToTypeHierarchy (this, ref reflectionContext, staticType); + .ApplyDynamicallyAccessedMembersToTypeHierarchy (this, staticType); reflectionContext.RecordHandledPattern (); @@ -2230,7 +2230,7 @@ void RequireDynamicallyAccessedMembers (ref ReflectionPatternContext reflectionC reflectionContext.RecordHandledPattern (); } } else if (uniqueValue is SystemTypeValue systemTypeValue) { - MarkTypeForDynamicallyAccessedMembers (ref reflectionContext, systemTypeValue.TypeRepresented, requiredMemberTypes); + MarkTypeForDynamicallyAccessedMembers (ref reflectionContext, systemTypeValue.TypeRepresented, requiredMemberTypes, DependencyKind.DynamicallyAccessedMember); } else if (uniqueValue is KnownStringValue knownStringValue) { TypeReference typeRef = _context.TypeNameResolver.ResolveTypeName (knownStringValue.Contents, reflectionContext.Source, out AssemblyDefinition typeAssembly); TypeDefinition foundType = ResolveToTypeDefinition (typeRef); @@ -2239,7 +2239,7 @@ void RequireDynamicallyAccessedMembers (ref ReflectionPatternContext reflectionC reflectionContext.RecordHandledPattern (); } else { MarkType (ref reflectionContext, typeRef); - MarkTypeForDynamicallyAccessedMembers (ref reflectionContext, foundType, requiredMemberTypes); + MarkTypeForDynamicallyAccessedMembers (ref reflectionContext, foundType, requiredMemberTypes, DependencyKind.DynamicallyAccessedMember); _context.MarkingHelpers.MarkMatchingExportedType (foundType, typeAssembly, new DependencyInfo (DependencyKind.DynamicallyAccessedMember, foundType)); } } else if (uniqueValue == NullValue.Instance) { @@ -2286,31 +2286,31 @@ void RequireDynamicallyAccessedMembers (ref ReflectionPatternContext reflectionC static bool HasBindingFlag (BindingFlags? bindingFlags, BindingFlags? search) => bindingFlags != null && (bindingFlags & search) == search; - void MarkTypeForDynamicallyAccessedMembers (ref ReflectionPatternContext reflectionContext, TypeDefinition typeDefinition, DynamicallyAccessedMemberTypes requiredMemberTypes) + void MarkTypeForDynamicallyAccessedMembers (ref ReflectionPatternContext reflectionContext, TypeDefinition typeDefinition, DynamicallyAccessedMemberTypes requiredMemberTypes, DependencyKind dependencyKind) { foreach (var member in typeDefinition.GetDynamicallyAccessedMembers (_context, requiredMemberTypes)) { switch (member) { case MethodDefinition method: - MarkMethod (ref reflectionContext, method, DependencyKind.DynamicallyAccessedMember); + MarkMethod (ref reflectionContext, method, dependencyKind); break; case FieldDefinition field: - MarkField (ref reflectionContext, field, DependencyKind.DynamicallyAccessedMember); + MarkField (ref reflectionContext, field, dependencyKind); break; case TypeDefinition nestedType: - DependencyInfo nestedDependencyInfo = new DependencyInfo (DependencyKind.DynamicallyAccessedMember, reflectionContext.Source); + DependencyInfo nestedDependencyInfo = new DependencyInfo (dependencyKind, reflectionContext.Source); reflectionContext.RecordRecognizedPattern (nestedType, () => _markStep.MarkEntireType (nestedType, includeBaseAndInterfaceTypes: true, nestedDependencyInfo)); break; case PropertyDefinition property: - MarkProperty (ref reflectionContext, property, DependencyKind.DynamicallyAccessedMember); + MarkProperty (ref reflectionContext, property, dependencyKind); break; case EventDefinition @event: - MarkEvent (ref reflectionContext, @event, DependencyKind.DynamicallyAccessedMember); + MarkEvent (ref reflectionContext, @event, dependencyKind); break; case InterfaceImplementation interfaceImplementation: - MarkInterfaceImplementation (ref reflectionContext, interfaceImplementation, DependencyKind.DynamicallyAccessedMember); + MarkInterfaceImplementation (ref reflectionContext, interfaceImplementation, dependencyKind); break; case null: - DependencyInfo dependencyInfo = new DependencyInfo (DependencyKind.DynamicallyAccessedMember, reflectionContext.Source); + DependencyInfo dependencyInfo = new DependencyInfo (dependencyKind, reflectionContext.Source); reflectionContext.RecordRecognizedPattern (typeDefinition, () => _markStep.MarkEntireType (typeDefinition, includeBaseAndInterfaceTypes: true, dependencyInfo)); break; } diff --git a/src/linker/Linker.Steps/MarkStep.cs b/src/linker/Linker.Steps/MarkStep.cs index 858a5e771165..cff7fe11cd80 100644 --- a/src/linker/Linker.Steps/MarkStep.cs +++ b/src/linker/Linker.Steps/MarkStep.cs @@ -72,6 +72,7 @@ internal DynamicallyAccessedMembersTypeHierarchy DynamicallyAccessedMembersTypeH DependencyKind.AccessedViaReflection, DependencyKind.BaseType, DependencyKind.DynamicallyAccessedMember, + DependencyKind.DynamicallyAccessedMemberOnType, DependencyKind.DynamicDependency, DependencyKind.NestedType, DependencyKind.TypeInAssembly, @@ -85,6 +86,7 @@ internal DynamicallyAccessedMembersTypeHierarchy DynamicallyAccessedMembersTypeH DependencyKind.Custom, DependencyKind.CustomAttributeField, DependencyKind.DynamicallyAccessedMember, + DependencyKind.DynamicallyAccessedMemberOnType, DependencyKind.EventSourceProviderField, DependencyKind.FieldAccess, DependencyKind.FieldOnGenericInstance, @@ -110,6 +112,7 @@ internal DynamicallyAccessedMembersTypeHierarchy DynamicallyAccessedMembersTypeH DependencyKind.DeclaringType, DependencyKind.DeclaringTypeOfCalledMethod, DependencyKind.DynamicallyAccessedMember, + DependencyKind.DynamicallyAccessedMemberOnType, DependencyKind.DynamicDependency, DependencyKind.ElementType, DependencyKind.FieldType, @@ -146,6 +149,7 @@ internal DynamicallyAccessedMembersTypeHierarchy DynamicallyAccessedMembersTypeH DependencyKind.DefaultCtorForNewConstrainedGenericArgument, DependencyKind.DirectCall, DependencyKind.DynamicallyAccessedMember, + DependencyKind.DynamicallyAccessedMemberOnType, DependencyKind.DynamicDependency, DependencyKind.ElementMethod, DependencyKind.EventMethod, @@ -329,7 +333,7 @@ void MarkEntireTypeInternal (TypeDefinition type, bool includeBaseAndInterfaceTy _entireTypesMarked[type] = includeBaseAndInterfaceTypes; - bool isDynamicDependencyReason = reason.Kind == DependencyKind.DynamicallyAccessedMember || reason.Kind == DependencyKind.DynamicDependency; + bool isDynamicDependencyReason = reason.Kind == DependencyKind.DynamicallyAccessedMember || reason.Kind == DependencyKind.DynamicDependency || reason.Kind == DependencyKind.DynamicallyAccessedMemberOnType; if (type.HasNestedTypes) { foreach (TypeDefinition nested in type.NestedTypes) @@ -1540,6 +1544,57 @@ protected void MarkField (FieldReference reference, DependencyInfo reason) MarkField (field, reason); } + void ReportWarningsForTypeHierarchyReflectionAccess (IMemberDefinition member) + { + Debug.Assert (member is MethodDefinition or FieldDefinition); + + // Don't check whether the current scope is a RUC type or RUC method because these warnings + // are not suppressed in RUC scopes. Here the scope represents the DynamicallyAccessedMembers + // annotation on a type, not a callsite which uses the annotation. We always want to warn about + // possible reflection access indicated by these annotations. + + var type = _scopeStack.CurrentScope.Origin.MemberDefinition as TypeDefinition; + Debug.Assert (type != null); + + static bool IsDeclaredWithinType (IMemberDefinition member, TypeDefinition type) + { + while ((member = member.DeclaringType) != null) { + if (member == type) + return true; + } + return false; + } + + var reportOnMember = IsDeclaredWithinType (member, type); + var memberScope = reportOnMember ? _scopeStack.PushScope (new MessageOrigin (member)) : null; + + try { + var origin = _scopeStack.CurrentScope.Origin; + + if (member is MethodDefinition method && DoesMethodRequireUnreferencedCode (method, out RequiresUnreferencedCodeAttribute attribute)) { + 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)); + var code = reportOnMember ? 2112 : 2113; + _context.LogWarning (message, code, origin, MessageSubCategory.TrimAnalysis); + } + + if (_context.Annotations.FlowAnnotations.ShouldWarnWhenAccessedForReflection (member)) { + var message = string.Format ( + "'DynamicallyAccessedMembersAttribute' on '{0}' or one of its base types references '{1}' which has 'DynamicallyAccessedMembersAttribute' requirements.", + type.GetDisplayName (), + (member as MemberReference).GetDisplayName ()); + var code = reportOnMember ? 2114 : 2115; + _context.LogWarning (message, code, origin, MessageSubCategory.TrimAnalysis); + } + } finally { + memberScope?.Dispose (); + } + } + void MarkField (FieldDefinition field, in DependencyInfo reason) { #if DEBUG @@ -1567,9 +1622,11 @@ void MarkField (FieldDefinition field, in DependencyInfo reason) MessageSubCategory.TrimAnalysis); break; + case DependencyKind.DynamicallyAccessedMemberOnType: + ReportWarningsForTypeHierarchyReflectionAccess (field); + break; } - if (CheckProcessed (field)) return; @@ -1688,8 +1745,13 @@ internal void MarkPropertyVisibleToReflection (PropertyDefinition property, in D internal void MarkEventVisibleToReflection (EventDefinition @event, in DependencyInfo reason) { - // MarkEvent actually marks the add/remove/invoke methods as well, so no need to mark those explicitly MarkEvent (@event, reason); + // MarkEvent already marks the add/remove/invoke methods, but we need to mark them with the + // DependencyInfo used to access the event from reflection, to produce warnings for annotated + // event methods. + MarkMethodIfNotNull (@event.AddMethod, reason); + MarkMethodIfNotNull (@event.InvokeMethod, reason); + MarkMethodIfNotNull (@event.InvokeMethod, reason); MarkMethodsIf (@event.OtherMethods, m => true, reason); } @@ -2783,19 +2845,27 @@ void ProcessAnalysisAnnotationsForMethod (MethodDefinition method, DependencyKin case DependencyKind.KeptForSpecialAttribute: return; + case DependencyKind.DynamicallyAccessedMember: + case DependencyKind.DynamicallyAccessedMemberOnType: + // 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 (method.IsVirtual && Annotations.GetBaseMethods (method) != null) + return; + break; + default: // 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 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) + if (dependencyKind == DependencyKind.DynamicallyAccessedMemberOnType) { + // DynamicallyAccessedMembers on type gets special treatment so that the warning origin + // is the type or the annotated member. + ReportWarningsForTypeHierarchyReflectionAccess (method); return; + } CheckAndReportRequiresUnreferencedCode (method); @@ -2815,48 +2885,6 @@ void ProcessAnalysisAnnotationsForMethod (MethodDefinition method, DependencyKin 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, @@ -2890,24 +2918,29 @@ internal bool ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode () return false; } - internal void CheckAndReportRequiresUnreferencedCode (MethodDefinition method) + bool DoesMethodRequireUnreferencedCode (MethodDefinition method, out RequiresUnreferencedCodeAttribute attribute) { - var currentOrigin = _scopeStack.CurrentScope.Origin; + if (Annotations.TryGetLinkerAttribute (method, out attribute)) + return true; + + if ((method.IsStatic || method.IsConstructor) && + Annotations.TryGetEffectiveRequiresUnreferencedCodeAttributeOnType (method.DeclaringType, out attribute)) + return true; + return false; + } + + internal void CheckAndReportRequiresUnreferencedCode (MethodDefinition method) + { // If the caller of a method is already marked with `RequiresUnreferencedCodeAttribute` a new warning should not // be produced for the callee. if (ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode ()) return; - if (method.IsStatic || method.IsConstructor) { - if (Annotations.TryGetEffectiveRequiresUnreferencedCodeAttributeOnType (method.DeclaringType, out RequiresUnreferencedCodeAttribute requiresUnreferencedCodeOnTypeHierarchy)) { - ReportRequiresUnreferencedCode (method.GetDisplayName (), requiresUnreferencedCodeOnTypeHierarchy, currentOrigin); - return; - } - } + if (!DoesMethodRequireUnreferencedCode (method, out RequiresUnreferencedCodeAttribute requiresUnreferencedCode)) + return; - if (Annotations.TryGetLinkerAttribute (method, out RequiresUnreferencedCodeAttribute requiresUnreferencedCode)) - ReportRequiresUnreferencedCode (method.GetDisplayName (), requiresUnreferencedCode, currentOrigin); + ReportRequiresUnreferencedCode (method.GetDisplayName (), requiresUnreferencedCode, _scopeStack.CurrentScope.Origin); } private void ReportRequiresUnreferencedCode (string displayName, RequiresUnreferencedCodeAttribute requiresUnreferencedCode, MessageOrigin currentOrigin) diff --git a/src/linker/Linker/DependencyInfo.cs b/src/linker/Linker/DependencyInfo.cs index 5ebaa2d81975..72ab233df3dc 100644 --- a/src/linker/Linker/DependencyInfo.cs +++ b/src/linker/Linker/DependencyInfo.cs @@ -137,7 +137,9 @@ public enum DependencyKind SerializedRecursiveType = 85, // recursive type kept due to serialization handling SerializedMember = 86, // field or property kept on a type for serialization - PreservedOperator = 87 // operator method preserved on a type + PreservedOperator = 87, // operator method preserved on a type + + DynamicallyAccessedMemberOnType = 88, // type with DynamicallyAccessedMembers annotations (including those inherited from base types and interfaces) } public readonly struct DependencyInfo : IEquatable diff --git a/test/Mono.Linker.Tests.Cases/Reflection/TypeHierarchyLibraryModeSuppressions.cs b/test/Mono.Linker.Tests.Cases/Reflection/TypeHierarchyLibraryModeSuppressions.cs index 6d81d5b07bcd..b03b481a4e0e 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/TypeHierarchyLibraryModeSuppressions.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/TypeHierarchyLibraryModeSuppressions.cs @@ -26,24 +26,24 @@ public static void Main () [Kept] [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] - [ExpectedWarning ("IL2026", nameof (Unsuppressed))] class Unsuppressed { [Kept] [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [ExpectedWarning ("IL2112", "--RUC on Unsuppressed--")] [RequiresUnreferencedCode ("--RUC on Unsuppressed--")] public void RUCMethod () { } } [Kept] [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] - [KeptAttributeAttribute (typeof (UnconditionalSuppressMessageAttribute))] - [UnconditionalSuppressMessage ("TrimAnalysis", "IL2026")] [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] class Suppressed { [Kept] [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [KeptAttributeAttribute (typeof (UnconditionalSuppressMessageAttribute))] + [UnconditionalSuppressMessage ("TrimAnalysis", "IL2112")] [RequiresUnreferencedCode ("--RUC on Suppressed--")] public void RUCMethod () { } } diff --git a/test/Mono.Linker.Tests.Cases/Reflection/TypeHierarchyReflectionWarnings.cs b/test/Mono.Linker.Tests.Cases/Reflection/TypeHierarchyReflectionWarnings.cs new file mode 100644 index 000000000000..3cbdf4b0e4e3 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/Reflection/TypeHierarchyReflectionWarnings.cs @@ -0,0 +1,481 @@ +// 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.Text; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Helpers; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Reflection +{ + [ExpectedNoWarnings] + public class TypeHierarchyReflectionWarnings + { + public static void Main () + { + RequirePublicMethods (annotatedBase.GetType ()); + // Reference to the derived type should apply base annotations + var t = typeof (DerivedFromAnnotatedBase); + RequirePublicMethods (annotatedDerivedFromBase.GetType ()); + RequirePublicNestedTypes (annotatedPublicNestedTypes.GetType ()); + RequirePublicFields (derivedFromAnnotatedDerivedFromBase.GetType ()); + RequirePublicMethods (annotatedPublicMethods.GetType ()); + RequirePublicFields (annotatedPublicFields.GetType ()); + RequirePublicProperties (annotatedPublicProperties.GetType ()); + RequirePublicEvents (annotatedPublicEvents.GetType ()); + RequirePublicNestedTypes (annotatedPublicNestedTypes.GetType ()); + RequireInterfaces (annotatedInterfaces.GetType ()); + RequireAll (annotatedAll.GetType ()); + RequirePublicMethods (annotatedRUCPublicMethods.GetType ()); + + // Instantiate this type just so its property getters are considered reachable + var b = new DerivedFromAnnotatedDerivedFromBase (); + + // Check that this field doesn't produce a warning even if it is kept + // for some non-reflection access. + var f = AnnotatedPublicMethods.DAMField; + } + + [Kept] + static AnnotatedAll annotatedAll; + [Kept] + static AnnotatedPublicMethods annotatedPublicMethods; + [Kept] + static AnnotatedPublicFields annotatedPublicFields; + [Kept] + static AnnotatedPublicProperties annotatedPublicProperties; + [Kept] + static AnnotatedPublicEvents annotatedPublicEvents; + [Kept] + static AnnotatedInterfaces annotatedInterfaces; + [Kept] + static AnnotatedBase annotatedBase; + [Kept] + static AnnotatedDerivedFromBase annotatedDerivedFromBase; + [Kept] + static AnnotatedPublicNestedTypes annotatedPublicNestedTypes; + [Kept] + static DerivedFromAnnotatedDerivedFromBase derivedFromAnnotatedDerivedFromBase; + [Kept] + static AnnotatedRUCPublicMethods annotatedRUCPublicMethods; + + [Kept] + [KeptMember (".ctor()")] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] + class AnnotatedAll + { + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + [ExpectedWarning ("IL2114", nameof (AnnotatedAll), nameof (DAMField))] + public Type DAMField; + + [Kept] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [ExpectedWarning ("IL2112", "--RUC on AnnotatedAll.RUCMethod--")] + [RequiresUnreferencedCode ("--RUC on AnnotatedAll.RUCMethod--")] + public void RUCMethod () { } + + [Kept] + [ExpectedWarning ("IL2114", nameof (AnnotatedAll), nameof (DAMMethod))] + public void DAMMethod ( + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + Type t + ) + { } + } + + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + class AnnotatedPublicMethods + { + [Kept] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [ExpectedWarning ("IL2112", "--RUC on AnnotatedPublicMethods.RUCMethod--")] + [RequiresUnreferencedCode ("--RUC on AnnotatedPublicMethods.RUCMethod--")] + public void RUCMethod () { } + + // No warning for members not selected by the type's annotation + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + public static Type UnusedDAMField; + + // No warning for members not selected by the type's annotation, even if field is referenced statically + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + public static Type DAMField; + + [Kept] + [ExpectedWarning ("IL2114", nameof (AnnotatedPublicMethods), nameof (DAMMethod))] + public void DAMMethod ( + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + Type t + ) + { } + + [Kept] + // No warning for non-virtual method which only has DAM on return parameter + [return: KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [return: DynamicallyAccessedMembersAttribute (DynamicallyAccessedMemberTypes.PublicMethods)] + public Type DAMReturnMethod () => null; + + [Kept] + [ExpectedWarning ("IL2114", nameof (AnnotatedPublicMethods), nameof (DAMVirtualMethod))] + public virtual void DAMVirtualMethod ( + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + Type type + ) + { } + + [Kept] + [ExpectedWarning ("IL2114", nameof (AnnotatedPublicMethods), nameof (DAMReturnVirtualMethod))] + [return: KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [return: DynamicallyAccessedMembersAttribute (DynamicallyAccessedMemberTypes.PublicMethods)] + public virtual Type DAMReturnVirtualMethod () => null; + } + + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] + class AnnotatedPublicFields + { + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + [ExpectedWarning ("IL2114", nameof (AnnotatedPublicFields), nameof (DAMField))] + public Type DAMField; + + } + + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] + class AnnotatedPublicProperties + { + [Kept] + [KeptBackingField] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + public static string DAMProperty { + [Kept] + // No warning for non-virtual getter since return value is not annotated + get; + [Kept] + // Property access reports warnings on getter/setter + [ExpectedWarning ("IL2114", nameof (AnnotatedPublicProperties), nameof (DAMProperty) + ".set")] + set; + } + } + + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicEvents)] + class AnnotatedPublicEvents + { + [Kept] + [KeptMember (".ctor(System.Object,System.IntPtr)")] + [KeptMember ("Invoke(System.Object,System.Int32)")] + [KeptBaseType (typeof (MulticastDelegate))] + public delegate void MyEventHandler (object sender, int i); + + [Kept] + // We always keep event methods when an event is kept, so this generates warnings + // on the event itself (since an event access is considered to reference the annotated add method), + // and on the add method (if it is accessed through reflection). + [ExpectedWarning ("IL2026", "--RUC on add_RUCEvent--")] + public event MyEventHandler RUCEvent { + [Kept] + [ExpectedWarning ("IL2112", nameof (AnnotatedPublicEvents), "--RUC on add_RUCEvent--")] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [RequiresUnreferencedCode ("--RUC on add_RUCEvent--")] + add { } + [Kept] + remove { } + } + } + + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + interface RequiredInterface + { + // Removed, because keeping the interface on its own + // doesn't apply its type annotations + [RequiresUnreferencedCode ("--RUC on RequiredInterface.UnusedMethod--")] + void RUCMethod (); + } + + [Kept] + [KeptInterface (typeof (RequiredInterface))] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.Interfaces)] + class AnnotatedInterfaces : RequiredInterface + { + [Kept] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + // This should produce a warning: https://github.com/mono/linker/issues/2161 + [RequiresUnreferencedCode ("--RUC on AnnotatedInterfaces.UnusedMethod--")] + public void RUCMethod () { } + } + + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + class AnnotatedBase + { + [Kept] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [ExpectedWarning ("IL2112", "--RUC on AnnotatedBase--")] + [RequiresUnreferencedCode ("--RUC on AnnotatedBase--")] + public void RUCMethod () { } + } + + [KeptBaseType (typeof (AnnotatedBase))] + // Warning about base member could go away with https://github.com/mono/linker/issues/2175 + [ExpectedWarning ("IL2113", "--RUC on AnnotatedBase--")] + class DerivedFromAnnotatedBase : AnnotatedBase + { + [Kept] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [ExpectedWarning ("IL2112", "--RUC on DerivedFromAnnotatedBase--")] + [RequiresUnreferencedCode ("--RUC on DerivedFromAnnotatedBase--")] + public void RUCMethod () { } + } + + [KeptMember (".ctor()")] + class Base + { + [Kept] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [RequiresUnreferencedCode ("--RUCBaseMethod--")] + public void RUCBaseMethod () { } + + [Kept] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [RequiresUnreferencedCode ("--Base.RUCVirtualMethod--")] + public virtual void RUCVirtualMethod () { } + + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicMethods)] + public string DAMField1; + + [Kept] + [KeptBackingField] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicMethods)] + public virtual string DAMVirtualProperty { [Kept] get; } + } + + [KeptBaseType (typeof (Base))] + [KeptMember (".ctor()")] + [ExpectedWarning ("IL2113", "--RUCBaseMethod--")] + [ExpectedWarning ("IL2113", "--Base.RUCVirtualMethod--")] + [ExpectedWarning ("IL2115", nameof (Base), nameof (Base.DAMVirtualProperty) + ".get")] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + class AnnotatedDerivedFromBase : Base + { + [Kept] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [ExpectedWarning ("IL2112", "--RUC on AnnotatedDerivedFromBase--")] + [RequiresUnreferencedCode ("--RUC on AnnotatedDerivedFromBase--")] + public void RUCMethod () { } + + [Kept] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + // shouldn't warn because we warn on the base method instead + [RequiresUnreferencedCode ("--AnnotatedDerivedFromBase.RUCVirtualMethod--")] + public override void RUCVirtualMethod () { } + + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicMethods)] + public string DAMField2; + + [Kept] + [KeptBackingField] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + // shouldn't warn because we warn on the base getter instead + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicMethods)] + public override string DAMVirtualProperty { [Kept] get; } + + } + + [KeptBaseType (typeof (AnnotatedDerivedFromBase))] + [KeptMember (".ctor()")] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] + // Warnings about base members could go away with https://github.com/mono/linker/issues/2175 + [ExpectedWarning ("IL2115", nameof (Base), nameof (DAMField1))] + [ExpectedWarning ("IL2115", nameof (AnnotatedDerivedFromBase), nameof (DAMField2))] + [ExpectedWarning ("IL2113", "--RUCBaseMethod--")] + [ExpectedWarning ("IL2113", "--Base.RUCVirtualMethod--")] + [ExpectedWarning ("IL2113", "--RUC on AnnotatedDerivedFromBase--")] + [ExpectedWarning ("IL2115", nameof (Base), nameof (Base.DAMVirtualProperty) + ".get")] + class DerivedFromAnnotatedDerivedFromBase : AnnotatedDerivedFromBase + { + [Kept] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [ExpectedWarning ("IL2112", "--RUC on AnnotatedDerivedFromBase--")] + [RequiresUnreferencedCode ("--RUC on AnnotatedDerivedFromBase--")] + public void RUCMethod () { } + + [Kept] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + // shouldn't warn because we warn on the base method instead + [RequiresUnreferencedCode ("--DerivedFromAnnotatedDerivedFromBase.RUCVirtualMethod--")] + public override void RUCVirtualMethod () { } + + [Kept] + [ExpectedWarning ("IL2114", nameof (DerivedFromAnnotatedDerivedFromBase), nameof (DAMField3))] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicMethods)] + public string DAMField3; + + [Kept] + [KeptBackingField] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + // shouldn't warn because we warn on the base getter instead + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicMethods)] + public override string DAMVirtualProperty { [Kept] get; } + } + + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicNestedTypes)] + class AnnotatedPublicNestedTypes + { + [KeptMember (".ctor()")] + public class NestedType + { + [Kept] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [ExpectedWarning ("IL2112", "--RUC on NestedType.RUCMethod--")] + [RequiresUnreferencedCode ("--RUC on NestedType.RUCMethod--")] + void RUCMethod () { } + } + + [KeptMember (".ctor()")] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + public class NestedAnnotatedType + { + [Kept] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [ExpectedWarning ("IL2112", "--RUC on NestedAnnotatedType.RUCMethod--")] + [RequiresUnreferencedCode ("--RUC on NestedAnnotatedType.RUCMethod--")] + void RUCMethod () { } + } + + [KeptMember (".ctor()")] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [RequiresUnreferencedCode ("--RUC on NestedRUCType--")] + public class NestedRUCType + { + [Kept] + [ExpectedWarning ("IL2112", "--RUC on NestedRUCType--")] + public NestedRUCType () { } + + [Kept] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [ExpectedWarning ("IL2112", "--RUC on NestedRUCType.RUCMethod--")] + [RequiresUnreferencedCode ("--RUC on NestedRUCType.RUCMethod--")] + void RUCMethod () { } + + [Kept] + void Method () { } + + [Kept] + [ExpectedWarning ("IL2112", "--RUC on NestedRUCType--")] + static void StaticMethod () { } + } + + [KeptMember (".ctor()")] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [ExpectedWarning ("IL2112", nameof (NestedRUCTypeWithDefaultConstructor) + "()", "--RUC on NestedRUCTypeWithDefaultConstructor--", CompilerGeneratedCode = true)] + [RequiresUnreferencedCode ("--RUC on NestedRUCTypeWithDefaultConstructor--")] + public class NestedRUCTypeWithDefaultConstructor + { + } + } + + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + [RequiresUnreferencedCode ("--AnnotatedRUCPublicMethods--")] + public class AnnotatedRUCPublicMethods + { + public AnnotatedRUCPublicMethods () { } + + [Kept] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [ExpectedWarning ("IL2112", "--RUC on AnnotatedRUCPublicMethods.RUCMethod--")] + [RequiresUnreferencedCode ("--RUC on AnnotatedRUCPublicMethods.RUCMethod--")] + public void RUCMethod () { } + + [Kept] + public void Method () { } + + [Kept] + [ExpectedWarning ("IL2112", "--AnnotatedRUCPublicMethods--")] + public static void StaticMethod () { } + } + + [Kept] + static void RequirePublicMethods ( + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + Type type) + { } + + [Kept] + static void RequirePublicFields ( + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] + Type type) + { } + + [Kept] + static void RequirePublicProperties ( + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] + Type type) + { } + + [Kept] + static void RequirePublicEvents ( + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicEvents)] + Type type) + { } + + [Kept] + static void RequireAll ( + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] + Type type) + { } + + [Kept] + static void RequirePublicNestedTypes ( + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicNestedTypes)] + Type type) + { } + + [Kept] + static void RequireInterfaces ( + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.Interfaces)] + Type type) + { } + } +} diff --git a/test/Mono.Linker.Tests.Cases/Reflection/TypeHierarchySuppressions.cs b/test/Mono.Linker.Tests.Cases/Reflection/TypeHierarchySuppressions.cs index 67c9e4efcb4e..fe9a44c3b092 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/TypeHierarchySuppressions.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/TypeHierarchySuppressions.cs @@ -15,33 +15,25 @@ namespace Mono.Linker.Tests.Cases.Reflection [ExpectedNoWarnings] public class TypeHierarchySuppressions { - // https://github.com/mono/linker/issues/2136 - // Warnings should originate from the types (or rather their members, with the - // proposed behavior), and the type suppressions should silence the relevant - // warnings. - - // Should originate from types instead - [ExpectedWarning ("IL2026", "--RUC on Unsuppressed--")] - [ExpectedWarning ("IL2026", "--RUC on DerivedFromUnsuppressed1--")] - - // Should be suppressed by type-level suppression - [ExpectedWarning ("IL2026", "--RUC on Suppressed--")] - [ExpectedWarning ("IL2026", "--RUC on SuppressedOnDerived1--")] - [ExpectedWarning ("IL2026", "--RUC on DerivedFromSuppressed1--")] public static void Main () { RequireMethods (unsuppressed.GetType ()); RequireMethods (suppressed.GetType ()); + RequireAll (annotatedAllSuppressed.GetType ()); var t = typeof (DerivedFromSuppressed1); var t2 = typeof (DerivedFromUnsuppressed1); var t3 = typeof (SuppressedOnDerived1); + var t4 = typeof (SuppressedBaseWarningsOnDerived); UseDerivedTypes (); + + // Suppressing type hierarchy warnings is unsafe because it allows + // derived types to access annotated methods without any warnings: + derivedFromSuppressed.GetType ().GetMethod ("RUCDerivedMethod"); + } - // Referencing these types in a separate method ensures that they get - // marked after applying annotations on the base type. [Kept] static void UseDerivedTypes () { @@ -52,9 +44,12 @@ static void UseDerivedTypes () [Kept] static Unsuppressed unsuppressed; - [Kept] static Suppressed suppressed; + [Kept] + static DerivedFromSuppressed1 derivedFromSuppressed; + [Kept] + static AnnotatedAllSuppressed annotatedAllSuppressed; [Kept] static void RequireMethods ( @@ -63,41 +58,45 @@ static void RequireMethods ( Type type) { } + [Kept] + static void RequireAll ( + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] + Type type) + { } + [Kept] [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] - // https://github.com/mono/linker/issues/2136 - // [ExpectedWarning ("IL2026", "--RUC on Unsuppressed--")] class Unsuppressed { [Kept] [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [ExpectedWarning ("IL2112", "--RUC on Unsuppressed--")] [RequiresUnreferencedCode ("--RUC on Unsuppressed--")] public void RUCMethod () { } } [Kept] [KeptBaseType (typeof (Unsuppressed))] - // https://github.com/mono/linker/issues/2136 - // [ExpectedWarning ("IL2026", "--RUC on DerivedFromUnsuppressed1--")] + [ExpectedWarning ("IL2113", "--RUC on Unsuppressed--")] class DerivedFromUnsuppressed1 : Unsuppressed { [Kept] [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [ExpectedWarning ("IL2112", "--RUC on DerivedFromUnsuppressed1--")] [RequiresUnreferencedCode ("--RUC on DerivedFromUnsuppressed1--")] public void DerivedRUCMethod () { } } [Kept] [KeptBaseType (typeof (Unsuppressed))] - [ExpectedWarning ("IL2026", "--RUC on DerivedFromUnsuppressed2")] - // https://github.com/mono/linker/issues/2136 - // Should originate from the base type instead - [ExpectedWarning ("IL2026", "--RUC on Unsuppressed--")] + [ExpectedWarning ("IL2113", "--RUC on Unsuppressed--")] class DerivedFromUnsuppressed2 : Unsuppressed { [Kept] [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [ExpectedWarning ("IL2112", "--RUC on DerivedFromUnsuppressed2")] [RequiresUnreferencedCode ("--RUC on DerivedFromUnsuppressed2--")] public void DerivedRUCMethod () { } } @@ -105,7 +104,7 @@ public void DerivedRUCMethod () { } [Kept] [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] [KeptAttributeAttribute (typeof (UnconditionalSuppressMessageAttribute))] - [UnconditionalSuppressMessage ("TrimAnalysis", "IL2026")] + [UnconditionalSuppressMessage ("TrimAnalysis", "IL2112")] [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] class Suppressed { @@ -117,51 +116,99 @@ public void RUCMethod () { } [Kept] [KeptBaseType (typeof (Suppressed))] - // https://github.com/mono/linker/issues/2136 - // [ExpectedWarning ("IL2026", "--RUC on DerivedFromSuppressed1--")] + // Base method should warn even though it was suppressed on the base + [ExpectedWarning ("IL2113", "--RUC on Suppressed--")] class DerivedFromSuppressed1 : Suppressed { [Kept] [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [ExpectedWarning ("IL2112", "--RUC on DerivedFromSuppressed1--")] [RequiresUnreferencedCode ("--RUC on DerivedFromSuppressed1--")] public void RUCDerivedMethod () { } } [Kept] [KeptBaseType (typeof (Suppressed))] - [ExpectedWarning ("IL2026", "--RUC on DerivedFromSuppressed2--")] - // https://github.com/mono/linker/issues/2136 - [ExpectedWarning ("IL2026", "--RUC on Suppressed--")] + // Base method should warn even though it was suppressed on the base + [ExpectedWarning ("IL2113", "--RUC on Suppressed--")] class DerivedFromSuppressed2 : Suppressed { [Kept] [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [ExpectedWarning ("IL2112", "--RUC on DerivedFromSuppressed2--")] [RequiresUnreferencedCode ("--RUC on DerivedFromSuppressed2--")] public void RUCDerivedMethod () { } } [Kept] [KeptBaseType (typeof (Unsuppressed))] - [KeptAttributeAttribute (typeof (UnconditionalSuppressMessageAttribute))] - [UnconditionalSuppressMessage ("TrimAnalysis", "IL2026")] + [ExpectedWarning ("IL2113", "--RUC on Unsuppressed--")] class SuppressedOnDerived1 : Unsuppressed { [Kept] [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [KeptAttributeAttribute (typeof (UnconditionalSuppressMessageAttribute))] + [UnconditionalSuppressMessage ("TrimAnalysis", "IL2112")] [RequiresUnreferencedCode ("--RUC on SuppressedOnDerived1--")] public void DerivedRUCMethod () { } } [Kept] [KeptBaseType (typeof (Unsuppressed))] - [KeptAttributeAttribute (typeof (UnconditionalSuppressMessageAttribute))] - [UnconditionalSuppressMessage ("TrimAnalysis", "IL2026")] + [ExpectedWarning ("IL2113", "--RUC on Unsuppressed--")] class SuppressedOnDerived2 : Unsuppressed { [Kept] [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [KeptAttributeAttribute (typeof (UnconditionalSuppressMessageAttribute))] + [UnconditionalSuppressMessage ("TrimAnalysis", "IL2112")] [RequiresUnreferencedCode ("--RUC on SuppressedOnDerived2--")] public void DerivedRUCMethod () { } } + + [Kept] + [KeptBaseType (typeof (Unsuppressed))] + [KeptAttributeAttribute (typeof (UnconditionalSuppressMessageAttribute))] + // Suppress warnings about base members + [UnconditionalSuppressMessage ("TrimAnalysis", "IL2113")] + class SuppressedBaseWarningsOnDerived : Unsuppressed + { + [Kept] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [ExpectedWarning ("IL2112", "--RUC on SuppressedBaseWarningsOnDerived")] + [RequiresUnreferencedCode ("--RUC on SuppressedBaseWarningsOnDerived--")] + public void DerivedRUCMethod () { } + } + + [Kept] + [KeptMember (".ctor()")] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] + class AnnotatedAllSuppressed + { + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [KeptAttributeAttribute (typeof (UnconditionalSuppressMessageAttribute))] + [UnconditionalSuppressMessage ("TrimAnalysis", "IL2114")] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + public Type DAMTField; + + [Kept] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [KeptAttributeAttribute (typeof (UnconditionalSuppressMessageAttribute))] + [UnconditionalSuppressMessage ("TrimAnalysis", "IL2112")] + [RequiresUnreferencedCode ("--RUC on AnnotatedAllSuppresed.RUCMethod--")] + public static void RUCMethod () { } + + [Kept] + [KeptAttributeAttribute (typeof (UnconditionalSuppressMessageAttribute))] + [UnconditionalSuppressMessage ("TrimAnalysis", "IL2114")] + public void DAMTMethod ( + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + Type t + ) + { } + } } } diff --git a/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs b/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs index 7c71f3270a03..e03e732f8a6d 100644 --- a/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs +++ b/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Runtime.CompilerServices; using System.Text; using System.Text.RegularExpressions; using Mono.Cecil; @@ -761,6 +762,9 @@ void VerifyLoggedMessages (AssemblyDefinition original, LinkerTestLogger logger, if (actualName.StartsWith (attrProvider.DeclaringType.FullName) && actualName.Contains ("<" + attrProvider.Name + ">")) return true; + if (methodDefinition.Name == ".ctor" && + methodDefinition.DeclaringType.FullName == attrProvider.FullName) + return true; } return false;