Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.GetDisplayName();

if (genericParameter.DeclaringType is TypeReference declaringType)
return declaringType.GetDisplayName();

return genericParameter.Name;
}
Comment thread
Copilot marked this conversation as resolved.

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 @@ -1206,6 +1206,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 1208 to +1211
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