Skip to content
Closed
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 @@ -37,11 +37,14 @@ public AttributeDataFlow(Logger logger, NodeFactory factory, FlowAnnotations ann
_logger = logger;
_origin = origin;

// Warnings for attributes on type declarations are not suppressed by RUC on the type.
// This allows attribute warnings to still be reported when attributes are applied to RUC types.
bool isAttributeOnType = _origin.MemberDefinition is TypeDesc;
_diagnosticContext = new DiagnosticContext(
_origin,
_logger.ShouldSuppressAnalysisWarningsForRequires(_origin.MemberDefinition, DiagnosticUtilities.RequiresUnreferencedCodeAttribute),
_logger.ShouldSuppressAnalysisWarningsForRequires(_origin.MemberDefinition, DiagnosticUtilities.RequiresDynamicCodeAttribute),
_logger.ShouldSuppressAnalysisWarningsForRequires(_origin.MemberDefinition, DiagnosticUtilities.RequiresAssemblyFilesAttribute),
!isAttributeOnType && _logger.ShouldSuppressAnalysisWarningsForRequires(_origin.MemberDefinition, DiagnosticUtilities.RequiresUnreferencedCodeAttribute),
!isAttributeOnType && _logger.ShouldSuppressAnalysisWarningsForRequires(_origin.MemberDefinition, DiagnosticUtilities.RequiresDynamicCodeAttribute),
!isAttributeOnType && _logger.ShouldSuppressAnalysisWarningsForRequires(_origin.MemberDefinition, DiagnosticUtilities.RequiresAssemblyFilesAttribute),
_logger);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,11 @@ private void ReportWarningsForTypeHierarchyReflectionAccess(MessageOrigin origin

Debug.Assert(_typeHierarchyDataFlowOrigin != null);

// Allow RUC on the type itself to suppress these warnings.
// This allows a type with RUC to suppress warnings about the type using other RUC types.
if (DiagnosticUtilities.TryGetRequiresAttribute(_typeHierarchyDataFlowOrigin, DiagnosticUtilities.RequiresUnreferencedCodeAttribute, out _))
return;

static bool IsDeclaredWithinType(TypeSystemEntity member, TypeDesc type)
{
TypeDesc owningType = member.GetOwningType();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,13 @@ internal bool ShouldSuppressAnalysisWarningsForRequires(TypeSystemEntity originM
if (originMember is FieldDesc field)
return field.DoesFieldRequire(requiresAttribute, out attribute);

// For type symbols, check if the type itself has the requires attribute
// This allows a type's RUC to suppress warnings about the type using other RUC types
// (e.g., in generic constraints, base types, etc.)
if (originMember is TypeDesc type &&
DiagnosticUtilities.TryGetRequiresAttribute(type, requiresAttribute, out attribute))
return true;

if (originMember.GetOwningType() == null) // Basically a way to test if the entity is a member (type, method, field, ...)
{
attribute = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,16 +174,23 @@ void CheckMatchingAttributesInInterfaces(
context.RegisterCompilationAction(extraCompilationAction);
}

internal void CheckAndCreateRequiresDiagnostic(
private void CheckAndCreateRequiresDiagnostic(
ISymbol member,
ISymbol containingSymbol,
ImmutableArray<ISymbol> incompatibleMembers,
in DiagnosticContext diagnosticContext)
in DiagnosticContext diagnosticContext,
bool isAttributeOnType)
{
// Do not emit any diagnostic if caller is annotated with the attribute too.
// Do not emit any diagnostic if caller is annotated with the attribute too
if (containingSymbol.IsInRequiresScope(RequiresAttributeName, out _))
return;

{
// Warnings for attributes on type declarations are not suppressed by RUC on the type.
// This allows attribute warnings to still be reported when attributes are applied to RUC types.
if (!isAttributeOnType)
{
return;
}
}
if (CreateSpecialIncompatibleMembersDiagnostic(incompatibleMembers, member, diagnosticContext))
return;

Expand Down Expand Up @@ -224,7 +231,8 @@ private void AnalyzeImplicitBaseCtor(SymbolAnalysisContext context)
baseCtor,
implicitCtor,
ImmutableArray<ISymbol>.Empty,
diagnosticContext);
diagnosticContext,
isAttributeOnType: false);
}

[Flags]
Expand Down Expand Up @@ -405,11 +413,45 @@ internal void CheckAndCreateRequiresDiagnostic(
ISymbol containingSymbol = operation.FindContainingSymbol(owningSymbol);

var incompatibleMembers = context.GetSpecialIncompatibleMembers(this);

bool isAttributeOnType = containingSymbol is INamedTypeSymbol && IsAttributeOnType(operation, member);
CheckAndCreateRequiresDiagnostic(
member,
containingSymbol,
incompatibleMembers,
diagnosticContext);
diagnosticContext,
isAttributeOnType);
}

private static bool IsAttributeOnType(IOperation operation, ISymbol member)
{
INamedTypeSymbol? potentialAttrType = null;
if (operation.Kind == OperationKind.ObjectCreation &&
member is IMethodSymbol potentialAttrCtor &&
potentialAttrCtor.IsConstructor())
{
potentialAttrType = potentialAttrCtor.ContainingType;
}
else if (member is IMethodSymbol method &&
method.MethodKind == MethodKind.PropertySet &&
method.AssociatedSymbol is IPropertySymbol associatedProperty)
{
potentialAttrType = associatedProperty.ContainingType;
}

return potentialAttrType != null && IsAttributeType(potentialAttrType);
}

private static bool IsAttributeType(INamedTypeSymbol? type)
{
var current = type;
while (current != null)
{
if (current.ToDisplayString() == "System.Attribute")
return true;
current = current.BaseType;
}
return false;
}

internal virtual bool IsIntrinsicallyHandled(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,14 @@ private static bool ExcludeStatics(AttributeData attributeData)
/// </summary>
public static bool IsInRequiresScope(this ISymbol member, string attributeName, [NotNullWhen(true)] out AttributeData? requiresAttribute)
{
// Requires attribute on a type does not silence warnings that originate
// from the type directly. We also only check the containing type for members
// below, not of nested types.
if (member is ITypeSymbol)
// For type symbols, check if the type itself has the requires attribute
// This allows a type's RUC to suppress warnings about the type using other RUC types
// (e.g., in generic constraints, base types, etc.)
if (member is ITypeSymbol typeSymbol)
{
if (typeSymbol.TryGetAttribute(attributeName, out requiresAttribute))
return true;

requiresAttribute = null;
return false;
}
Expand All @@ -78,7 +81,7 @@ public static bool IsInRequiresScope(this ISymbol member, string attributeName,
if (member.ContainingType is ITypeSymbol containingType && containingType.TryGetAttribute(attributeName, out requiresAttribute))
{
if (!ExcludeStatics(requiresAttribute))
return true;
return true;

if (!member.IsStatic)
return true;
Expand Down
13 changes: 12 additions & 1 deletion src/tools/illink/src/linker/Linker.Steps/MarkStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1713,7 +1713,13 @@ protected void MarkField(FieldReference reference, DependencyInfo reason, in Mes
void ReportWarningsForReflectionAccess(in MessageOrigin origin, MethodDefinition method, DependencyKind dependencyKind)
{
if (Annotations.ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode(origin.Provider, out _))
return;
{
// Warnings for attributes on type declarations are not suppressed by RUC on the type.
// This allows attribute warnings to still be reported when attributes are applied to RUC types.
if (dependencyKind is not (DependencyKind.AttributeConstructor or DependencyKind.AttributeProperty) ||
origin.Provider is not TypeDefinition)
return;
}

bool isReflectionAccessCoveredByRUC;
bool isCompilerGenerated = CompilerGeneratedState.IsNestedFunctionOrStateMachineMember(method);
Expand Down Expand Up @@ -1791,6 +1797,11 @@ void ReportWarningsForTypeHierarchyReflectionAccess(IMemberDefinition member, Me
var type = origin.Provider as TypeDefinition;
Debug.Assert(type != null);

// Allow RUC on the type itself to suppress these warnings.
// This allows a type with RUC to suppress warnings about the type using other RUC types.
if (Annotations.TryGetLinkerAttribute<RequiresUnreferencedCodeAttribute>(type, out _))
return;

static bool IsDeclaredWithinType(IMemberDefinition member, TypeDefinition type)
{
while ((member = member.DeclaringType) != null)
Expand Down
4 changes: 4 additions & 0 deletions src/tools/illink/src/linker/Linker/Annotations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,10 @@ internal bool ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode(ICustomA
if (originMember is FieldDefinition field)
return DoesFieldRequireUnreferencedCode(field, out attribute);

if (originMember is TypeDefinition type &&
TryGetLinkerAttribute<RequiresUnreferencedCodeAttribute>(type, out attribute))
return true;

if (originMember is not IMemberDefinition member)
return false;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,8 +237,6 @@ static void TestDerivedTypeWithOpenGenericOnBaseWithRUCOnDerived()
{
new DerivedTypeWithOpenGenericOnBaseWithRUCOnDerived<TestType>();
}
[ExpectedWarning("IL2091", nameof(BaseTypeWithOpenGenericDAMT<T>))]
[ExpectedWarning("IL2091", nameof(IGenericInterfaceTypeWithRequirements<T>))]
[RequiresUnreferencedCode("RUC")]
class DerivedTypeWithOpenGenericOnBaseWithRUCOnDerived<T> : BaseTypeWithOpenGenericDAMT<T>, IGenericInterfaceTypeWithRequirements<T>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -627,16 +627,13 @@ public AnnotatedRUCPublicMethods() { }

[Kept]
[KeptAttributeAttribute(typeof(RequiresUnreferencedCodeAttribute))]
[ExpectedWarning("IL2112", "--RUC on AnnotatedRUCPublicMethods.RUCMethod--")]
[RequiresUnreferencedCode("--RUC on AnnotatedRUCPublicMethods.RUCMethod--")]
public void RUCMethod() { }

[Kept]
[ExpectedWarning("IL2112", "--AnnotatedRUCPublicMethods--")]
public void Method() { }

[Kept]
[ExpectedWarning("IL2112", "--AnnotatedRUCPublicMethods--")]
public static void StaticMethod() { }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ public static void Main()
AttributeParametersAndProperties.Test();
MembersOnClassWithRequires<int>.Test();
ConstFieldsOnClassWithRequires.Test();
RequiresOnClassWithAttributes.Test();
RequiresOnAttributeTypeWithDAM.Test();

// Instantiate classes so linker warnings match analyzer warnings
new TestUnconditionalSuppressMessage();
Expand Down Expand Up @@ -783,13 +785,10 @@ class BaseForDAMAnnotatedClass
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)]
[RequiresUnreferencedCode("This class is dangerous")]
[RequiresDynamicCode("This class is dangerous")]
[ExpectedWarning("IL2113", "BaseForDAMAnnotatedClass.baseField")]
class DAMAnnotatedClass : BaseForDAMAnnotatedClass
{
[ExpectedWarning("IL2112", "DAMAnnotatedClass.publicField")]
public static int publicField;

[ExpectedWarning("IL2112", "DAMAnnotatedClass.privatefield")]
static int privatefield;
}

Expand Down Expand Up @@ -1213,23 +1212,17 @@ class BaseForDAMAnnotatedClass
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)]
[RequiresUnreferencedCode("This class is dangerous")]
[RequiresDynamicCode("This class is dangerous")]
[ExpectedWarning("IL2113", "BaseForDAMAnnotatedClass.baseProperty.get")]
[ExpectedWarning("IL2113", "BaseForDAMAnnotatedClass.baseProperty.set")]
class DAMAnnotatedClass : BaseForDAMAnnotatedClass
{
public static int publicProperty
{
[ExpectedWarning("IL2112", "DAMAnnotatedClass.publicProperty.get")]
get;
[ExpectedWarning("IL2112", "DAMAnnotatedClass.publicProperty.set")]
set;
}

static int privateProperty
{
[ExpectedWarning("IL2112", "DAMAnnotatedClass.privateProperty.get")]
get;
[ExpectedWarning("IL2112", "DAMAnnotatedClass.privateProperty.set")]
set;
}
}
Expand Down Expand Up @@ -1391,15 +1384,11 @@ public class ClassWithAttribute
}
}

// This warning should ideally be suppressed by the RUC on the type:
[UnexpectedWarning("IL2091", Tool.All, "https://github.com/dotnet/runtime/issues/108523")]
[RequiresUnreferencedCode("--GenericClassWithWarningWithRequires--")]
public class GenericClassWithWarningWithRequires<U> : RequiresAll<U>
{
}

// This warning should ideally be suppressed by the RUC on the type:
[UnexpectedWarning("IL2091", Tool.All, "https://github.com/dotnet/runtime/issues/108523")]
[RequiresUnreferencedCode("--ClassWithWarningWithRequires--")]
public class ClassWithWarningWithRequires : RequiresAll<T>
{
Expand All @@ -1409,19 +1398,17 @@ public class ClassWithWarningWithRequires : RequiresAll<T>
class ClassWithWarningOnGenericArgumentConstructor : RequiresNew<ClassWithRequires>
{
// Analyzer misses warning for implicit call to the base constructor, because the new constraint is not checked in dataflow.
[ExpectedWarning("IL2026", Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/108507")]
[ExpectedWarning("IL2026", Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/runtime/issues/118869")]
public ClassWithWarningOnGenericArgumentConstructor()
{
}
}

[UnexpectedWarning("IL2026", Tool.All, "https://github.com/dotnet/runtime/issues/108507")]
[RequiresUnreferencedCode("--ClassWithWarningOnGenericArgumentConstructorWithRequires--")]
class ClassWithWarningOnGenericArgumentConstructorWithRequires : RequiresNew<ClassWithRequires>
{
}

[UnexpectedWarning("IL2091", Tool.All, "https://github.com/dotnet/runtime/issues/108523")]
[RequiresUnreferencedCode("--GenericAnnotatedWithWarningWithRequires--")]
public class GenericAnnotatedWithWarningWithRequires<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] TFields> : RequiresAll<TFields>
{
Expand Down Expand Up @@ -1500,5 +1487,78 @@ public static void Test()
TestClassUsingFieldInAttribute();
}
}

class RequiresOnClassWithAttributes
{
public static void Test()
{
typeof(RUCTypeWithAttribute).GetCustomAttributes();
}

// Requires on the type should not suppress attribute warnings
[ExpectedWarning("IL2026", nameof(AttributeWithRUCAttribute))]
[ExpectedWarning("IL2026", nameof(AttributeWithRUCPropertyAttribute))]
[RequiresUnreferencedCode(nameof(RUCTypeWithAttribute))]
[AttributeWithRUCProperty(Property = 4)]
[AttributeWithRUC]
class RUCTypeWithAttribute
{
}

class AttributeWithRUCPropertyAttribute : Attribute
{
public int Property { get; [RequiresUnreferencedCode(nameof(AttributeWithRUCPropertyAttribute.Property))] set; }
}

[RequiresUnreferencedCode(nameof(AttributeWithRUCAttribute))]
class AttributeWithRUCAttribute : Attribute
{
}
}

public class RequiresOnAttributeTypeWithDAM
{
public static void Test(
AttributeWithDAMAttribute instance = null,
DerivedAttributeWithDAMAttribute derivedInstance = null,
DerivedAttributePropertyRequiresAttribute derivedInstanceWithProperty = null)
{
instance.GetType().GetConstructors();
derivedInstance.GetType().GetConstructors();
derivedInstanceWithProperty.GetType().GetMethods();
}

[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
[RequiresUnreferencedCode("Suppresses DAM type hierarchy warnings on the ctor")]
public class AttributeWithDAMAttribute : Attribute
{
[RequiresUnreferencedCode(nameof(AttributeWithDAMAttribute))]
public AttributeWithDAMAttribute()
{
}
}

[RequiresUnreferencedCode(nameof(AttributeWithRequiresAttribute))]
public class AttributeWithRequiresAttribute : Attribute
{
}

[RequiresUnreferencedCode("Suppresses DAM type hierarchy warnings on the type")]
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]
public class DerivedAttributeWithDAMAttribute : AttributeWithRequiresAttribute
{
}

public class AttributePropertyRequiresAttribute : Attribute
{
public int Property { get; [RequiresUnreferencedCode(nameof(AttributePropertyRequiresAttribute.Property))] set; }
}

[RequiresUnreferencedCode("Suppresses DAM type hierarchy warnings on the type")]
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)]
public class DerivedAttributePropertyRequiresAttribute : Attribute
{
}
}
}
}
Loading