Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions src/tools/illink/src/linker/Linker.Steps/MarkStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Comment on lines 1190 to +1193
TypeDefinition? type = Context.Resolve(constructor_type);

if (type == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
@@ -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<T>
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<T>
extends [System.Runtime]System.Object
{
.custom instance void class Mono.Linker.Tests.Cases.Attributes.Dependencies.DynamicallyAccessedMembersGenericAttribute`1<!T>::'.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
}
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
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()
{
new WithGenericAttribute_OfString();
new WithGenericAttribute_OfInt();
new WithConstrainedGenericAttribute();
typeof(WithNewConstrainedGenericAttribute).GetCustomAttributes(false);
ReflectOnGenericAttributeWithUnannotatedTypeParameter();
}

[Kept]
Expand Down Expand Up @@ -60,6 +71,12 @@ class ConstraintType
{
}

[Kept]
class TypeWithPublicMethods
{
public void Method() { }
}

[KeptBaseType(typeof(ConstraintType))]
class DerivedFromConstraintType : ConstraintType
{
Expand All @@ -72,5 +89,40 @@ class ConstrainedGenericAttribute<T> : Attribute
[Kept]
public ConstrainedGenericAttribute() { }
}

[Kept]
class Handler
{
[Kept]
public Handler() { }
}

[Kept]
[KeptAttributeAttribute(typeof(NewConstrainedGenericAttribute<Handler>))]
[NewConstrainedGenericAttribute<Handler>]
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<T> 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);
}
}
}
Loading