diff --git a/src/tools/illink/src/linker/Linker.Dataflow/DiagnosticUtilities.cs b/src/tools/illink/src/linker/Linker.Dataflow/DiagnosticUtilities.cs index 6b1bebb31dfdb2..c6e15e51e6cf2c 100644 --- a/src/tools/illink/src/linker/Linker.Dataflow/DiagnosticUtilities.cs +++ b/src/tools/illink/src/linker/Linker.Dataflow/DiagnosticUtilities.cs @@ -10,10 +10,16 @@ static class DiagnosticUtilities internal static string GetParameterNameForErrorMessage(ParameterDefinition parameterDefinition) => string.IsNullOrEmpty(parameterDefinition.Name) ? $"#{parameterDefinition.Index}" : parameterDefinition.Name; - internal static string GetGenericParameterDeclaringMemberDisplayName(GenericParameter genericParameter) => - genericParameter.DeclaringMethod != null ? - genericParameter.DeclaringMethod.GetDisplayName() : - genericParameter.DeclaringType.GetDisplayName(); + internal static string GetGenericParameterDeclaringMemberDisplayName(GenericParameter genericParameter) + { + if (genericParameter.DeclaringMethod is MethodReference declaringMethod) + return declaringMethod.Name; + + if (genericParameter.DeclaringType is TypeReference declaringType) + return declaringType.Name; + + return genericParameter.Name; + } internal static string GetMethodSignatureDisplayName(IMethodSignature methodSignature) => (methodSignature is MethodReference method) ? method.GetDisplayName() : (methodSignature.ToString() ?? string.Empty); diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs index fdd086d0dd03b0..4b08374f7f48ab 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs +++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs @@ -1188,6 +1188,9 @@ protected internal virtual void MarkCustomAttribute(CustomAttribute ca, in Depen MarkCustomAttributeArguments(ca, origin); TypeReference constructor_type = ca.Constructor.DeclaringType; + if (GenericArgumentDataFlow.RequiresGenericArgumentDataFlow(Context.Annotations.FlowAnnotations, constructor_type)) + GenericArgumentDataFlow.ProcessGenericArgumentDataFlow(in origin, this, Context, constructor_type); + TypeDefinition? type = Context.Resolve(constructor_type); if (type == null) diff --git a/src/tools/illink/src/linker/Linker/MemberReferenceExtensions.cs b/src/tools/illink/src/linker/Linker/MemberReferenceExtensions.cs index 71be73a49db94c..7480ec33a19e84 100644 --- a/src/tools/illink/src/linker/Linker/MemberReferenceExtensions.cs +++ b/src/tools/illink/src/linker/Linker/MemberReferenceExtensions.cs @@ -30,7 +30,13 @@ public static string GetDisplayName(this MemberReference member) public static string GetNamespaceDisplayName(this MemberReference member) { + if (member == null) + return string.Empty; + var type = member is TypeReference typeReference ? typeReference : member.DeclaringType; + if (type == null) + return string.Empty; + while (type.DeclaringType != null) type = type.DeclaringType; diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Attributes/Dependencies/GenericAttributesDataFlow.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Attributes/Dependencies/GenericAttributesDataFlow.il new file mode 100644 index 00000000000000..4dcf19a31f8d12 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Attributes/Dependencies/GenericAttributesDataFlow.il @@ -0,0 +1,52 @@ +.assembly extern System.Runtime +{ + .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) +} +.assembly 'GenericAttributesDataFlow' +{ + .hash algorithm 0x00008004 + .ver 1:0:0:0 +} +.module GenericAttributesDataFlow.dll + +.namespace Mono.Linker.Tests.Cases.Attributes.Dependencies +{ + // A generic attribute whose type parameter requires DynamicallyAccessedMemberTypes.PublicMethods. + .class public auto ansi beforefieldinit DynamicallyAccessedMembersGenericAttribute`1 + extends [System.Runtime]System.Attribute + { + .param type T + .custom instance void [System.Runtime]System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute::.ctor(valuetype [System.Runtime]System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes) = ( + 01 00 08 00 00 00 00 00 + ) + + .method public hidebysig specialname rtspecialname + instance default void '.ctor' () cil managed + { + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void class [System.Runtime]System.Attribute::'.ctor'() + IL_0006: ret + } + } + + // The attribute is applied with the class's own generic parameter as the generic argument. This + // construct cannot be expressed in C# (CS8968: an attribute type argument cannot use type + // parameters), so it is provided in IL. The trimmer must analyze the generic-argument data flow of + // the attribute without crashing and warn (IL2091) because the unannotated generic argument does + // not satisfy the attribute's PublicMethods requirement. + .class public auto ansi beforefieldinit ClassWithUnannotatedTypeParameter`1 + extends [System.Runtime]System.Object + { + .custom instance void class Mono.Linker.Tests.Cases.Attributes.Dependencies.DynamicallyAccessedMembersGenericAttribute`1::'.ctor'() = (01 00 00 00) + + .method public hidebysig specialname rtspecialname + instance default void '.ctor' () cil managed + { + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: call instance void class [System.Runtime]System.Object::'.ctor'() + IL_0006: ret + } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Attributes/GenericAttributes.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Attributes/GenericAttributes.cs index e8340a5b7d2cc2..8574fc90e94d33 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Attributes/GenericAttributes.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Attributes/GenericAttributes.cs @@ -1,8 +1,17 @@ using System; +using System.Reflection; using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; namespace Mono.Linker.Tests.Cases.Attributes { + // A generic attribute whose type parameter is annotated with DynamicallyAccessedMembers can only be + // applied with a generic-parameter argument in IL (C# forbids type parameters as generic attribute + // arguments - CS8968), so the data-flow scenario is provided by a compiled-before IL assembly. + // The warning originates in that dependency assembly, so it is asserted with LogContains + // (ExpectedWarning only matches origins in the test assembly itself). + [SetupCompileBefore("GenericAttributesDataFlow.dll", new[] { "Dependencies/GenericAttributesDataFlow.il" })] + [LogContains("IL2091.*ClassWithUnannotatedTypeParameter", regexMatch: true)] class GenericAttributes { static void Main() @@ -10,6 +19,8 @@ static void Main() new WithGenericAttribute_OfString(); new WithGenericAttribute_OfInt(); new WithConstrainedGenericAttribute(); + typeof(WithNewConstrainedGenericAttribute).GetCustomAttributes(false); + ReflectOnGenericAttributeWithUnannotatedTypeParameter(); } [Kept] @@ -60,6 +71,12 @@ class ConstraintType { } + [Kept] + class TypeWithPublicMethods + { + public void Method() { } + } + [KeptBaseType(typeof(ConstraintType))] class DerivedFromConstraintType : ConstraintType { @@ -72,5 +89,40 @@ class ConstrainedGenericAttribute : Attribute [Kept] public ConstrainedGenericAttribute() { } } + + [Kept] + class Handler + { + [Kept] + public Handler() { } + } + + [Kept] + [KeptAttributeAttribute(typeof(NewConstrainedGenericAttribute))] + [NewConstrainedGenericAttribute] + class WithNewConstrainedGenericAttribute + { + } + + [Kept] + [KeptBaseType(typeof(Attribute))] + class NewConstrainedGenericAttribute<[KeptGenericParamAttributes(GenericParameterAttributes.DefaultConstructorConstraint)] T> : Attribute + where T : new() + { + [Kept] + public NewConstrainedGenericAttribute() { } + } + + // Reflecting over the generic instantiation keeps the dependency type and forces the trimmer + // to analyze the generic attribute applied to it. + [Kept] + static void ReflectOnGenericAttributeWithUnannotatedTypeParameter() + { + // ClassWithUnannotatedTypeParameter applies a DynamicallyAccessedMembers-annotated generic + // attribute using its own (unverifiable) generic parameter as the argument, so the trimmer + // must analyze the generic-argument data flow without crashing and warn (IL2091). + Type.GetType("Mono.Linker.Tests.Cases.Attributes.Dependencies.ClassWithUnannotatedTypeParameter`1, GenericAttributesDataFlow")! + .MakeGenericType(typeof(TypeWithPublicMethods)).GetCustomAttributes(false); + } } }