Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,40 @@ namespace ILCompiler.Dataflow
{
internal static class GenericArgumentDataFlow
{
public static void ProcessGenericArgumentDataFlow(ref DependencyList dependencies, NodeFactory factory, in MessageOrigin origin, TypeDesc type, TypeDesc contextType)
public static void ProcessGenericArgumentDataFlow(
ref DependencyList dependencies,
NodeFactory factory,
in MessageOrigin origin,
TypeDesc type,
TypeDesc contextType,
bool suppressTrimAnalysisWarnings = false,
bool suppressAotAnalysisWarnings = false)
{
ProcessGenericArgumentDataFlow(ref dependencies, factory, origin, type, contextType.Instantiation, Instantiation.Empty);
ProcessGenericArgumentDataFlow(
ref dependencies,
factory,
origin,
type,
contextType.Instantiation,
Instantiation.Empty,
suppressTrimAnalysisWarnings,
suppressAotAnalysisWarnings);
}

public static void ProcessGenericArgumentDataFlow(ref DependencyList dependencies, NodeFactory factory, in MessageOrigin origin, TypeDesc type, MethodDesc contextMethod)
{
ProcessGenericArgumentDataFlow(ref dependencies, factory, origin, type, contextMethod.OwningType.Instantiation, contextMethod.Instantiation);
}

public static void ProcessGenericArgumentDataFlow(ref DependencyList dependencies, NodeFactory factory, in MessageOrigin origin, TypeDesc type, Instantiation typeContext, Instantiation methodContext)
public static void ProcessGenericArgumentDataFlow(
ref DependencyList dependencies,
NodeFactory factory,
in MessageOrigin origin,
TypeDesc type,
Instantiation typeContext,
Instantiation methodContext,
bool suppressTrimAnalysisWarnings = false,
bool suppressAotAnalysisWarnings = false)
{
if (!type.HasInstantiation)
return;
Expand All @@ -48,9 +71,18 @@ public static void ProcessGenericArgumentDataFlow(ref DependencyList dependencie

var diagnosticContext = new DiagnosticContext(
origin,
!logger.ShouldSuppressAnalysisWarningsForRequires(origin.MemberDefinition, DiagnosticUtilities.RequiresUnreferencedCodeAttribute),
logger);
var reflectionMarker = new ReflectionMarker(logger, factory, flowAnnotations, typeHierarchyDataFlowOrigin: null, enabled: true);
suppressTrimmerDiagnostics: suppressTrimAnalysisWarnings || logger.ShouldSuppressAnalysisWarningsForRequires(origin.MemberDefinition, DiagnosticUtilities.RequiresUnreferencedCodeAttribute),
suppressAotDiagnostics: suppressAotAnalysisWarnings || logger.ShouldSuppressAnalysisWarningsForRequires(origin.MemberDefinition, DiagnosticUtilities.RequiresDynamicCodeAttribute),
suppressSingleFileDiagnostics: logger.ShouldSuppressAnalysisWarningsForRequires(origin.MemberDefinition, DiagnosticUtilities.RequiresAssemblyFilesAttribute),
logger: logger);
var reflectionMarker = new ReflectionMarker(
logger,
factory,
flowAnnotations,
typeHierarchyDataFlowOrigin: null,
enabled: true,
suppressTrimAnalysisWarnings: suppressTrimAnalysisWarnings,
suppressAotAnalysisWarnings: suppressAotAnalysisWarnings);

ProcessGenericArgumentDataFlow(diagnosticContext, reflectionMarker, instantiatedType);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ public class ReflectionMarker
private readonly Logger _logger;
private readonly MetadataType? _typeHierarchyDataFlowOrigin;
private readonly bool _enabled;
private readonly bool _suppressTrimAnalysisWarnings;
private readonly bool _suppressAotAnalysisWarnings;

public NodeFactory Factory { get; }
public FlowAnnotations Annotations { get; }
Expand All @@ -39,13 +41,22 @@ internal enum AccessKind
TokenAccess
}

public ReflectionMarker(Logger logger, NodeFactory factory, FlowAnnotations annotations, MetadataType? typeHierarchyDataFlowOrigin, bool enabled)
public ReflectionMarker(
Logger logger,
NodeFactory factory,
FlowAnnotations annotations,
MetadataType? typeHierarchyDataFlowOrigin,
bool enabled,
bool suppressTrimAnalysisWarnings = false,
bool suppressAotAnalysisWarnings = false)
{
_logger = logger;
Factory = factory;
Annotations = annotations;
_typeHierarchyDataFlowOrigin = typeHierarchyDataFlowOrigin;
_enabled = enabled;
_suppressTrimAnalysisWarnings = suppressTrimAnalysisWarnings;
_suppressAotAnalysisWarnings = suppressAotAnalysisWarnings;
}

internal void MarkTypeForDynamicallyAccessedMembers(in MessageOrigin origin, TypeDesc typeDefinition, DynamicallyAccessedMemberTypes requiredMemberTypes, TypeSystemEntity reason, bool declaredOnly = false)
Expand Down Expand Up @@ -298,20 +309,23 @@ private void ReportWarningsForReflectionAccess(in MessageOrigin origin, TypeSyst
// This is because reflection access is actually problematic on all members which are in a "requires" scope
// so for example even instance methods. See for example https://github.com/dotnet/linker/issues/3140 - it's possible
// to call a method on a "null" instance via reflection.
if (_logger.ShouldSuppressAnalysisWarningsForRequires(entity, DiagnosticUtilities.RequiresUnreferencedCodeAttribute, out CustomAttributeValue<TypeDesc>? requiresAttribute) &&
if (!_suppressTrimAnalysisWarnings &&
_logger.ShouldSuppressAnalysisWarningsForRequires(entity, DiagnosticUtilities.RequiresUnreferencedCodeAttribute, out CustomAttributeValue<TypeDesc>? requiresAttribute) &&
ShouldProduceRequiresWarningForReflectionAccess(entity, accessKind))
ReportRequires(origin, entity, DiagnosticUtilities.RequiresUnreferencedCodeAttribute, requiresAttribute.Value);

if (_logger.ShouldSuppressAnalysisWarningsForRequires(entity, DiagnosticUtilities.RequiresAssemblyFilesAttribute, out requiresAttribute) &&
ShouldProduceRequiresWarningForReflectionAccess(entity, accessKind))
ReportRequires(origin, entity, DiagnosticUtilities.RequiresAssemblyFilesAttribute, requiresAttribute.Value);

if (_logger.ShouldSuppressAnalysisWarningsForRequires(entity, DiagnosticUtilities.RequiresDynamicCodeAttribute, out requiresAttribute) &&
if (!_suppressAotAnalysisWarnings &&
_logger.ShouldSuppressAnalysisWarningsForRequires(entity, DiagnosticUtilities.RequiresDynamicCodeAttribute, out requiresAttribute) &&
ShouldProduceRequiresWarningForReflectionAccess(entity, accessKind))
ReportRequires(origin, entity, DiagnosticUtilities.RequiresDynamicCodeAttribute, requiresAttribute.Value);

// Below is about accessing DAM annotated members, so only RUC is applicable as a suppression scope
if (_logger.ShouldSuppressAnalysisWarningsForRequires(origin.MemberDefinition, DiagnosticUtilities.RequiresUnreferencedCodeAttribute))
if (_suppressTrimAnalysisWarnings ||
_logger.ShouldSuppressAnalysisWarningsForRequires(origin.MemberDefinition, DiagnosticUtilities.RequiresUnreferencedCodeAttribute))
return;

bool isReflectionAccessCoveredByDAM = Annotations.ShouldWarnWhenAccessedForReflection(entity);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,23 @@ public override IEnumerable<DependencyListEntry> GetStaticDependencies(NodeFacto

if (_typeDefinition is MetadataType metadataType)
{
// The generic instantiation in the interface list is only reachable through the members of
// the type which implements the interface, which are all in the Requires scope of a
// type-level Requires attribute, so the matching attribute silences these warnings. Note
// that the data flow still needs to run to mark the members required by the instantiation.
bool suppressTrimAnalysisWarnings = _typeDefinition.DoesTypeRequire(DiagnosticUtilities.RequiresUnreferencedCodeAttribute, out _);
bool suppressAotAnalysisWarnings = _typeDefinition.DoesTypeRequire(DiagnosticUtilities.RequiresDynamicCodeAttribute, out _);

foreach (var interfaceType in metadataType.ExplicitlyImplementedInterfaces)
{
GenericArgumentDataFlow.ProcessGenericArgumentDataFlow(ref dependencies, factory, new MessageOrigin(_typeDefinition), interfaceType, _typeDefinition);
GenericArgumentDataFlow.ProcessGenericArgumentDataFlow(
ref dependencies,
factory,
new MessageOrigin(_typeDefinition),
interfaceType,
_typeDefinition,
suppressTrimAnalysisWarnings,
suppressAotAnalysisWarnings);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,23 +134,28 @@ public override void Initialize(AnalysisContext context)
if (dataFlowAnalyzerContext.TrimAnalyzer is null)
return;

// Examine generic instantiations in base types and interface list
// Examine generic instantiations in the interface list
context.RegisterSymbolAction(context =>
{
var type = (INamedTypeSymbol)context.Symbol;
// RUC on type doesn't silence DAM warnings about generic base/interface types.
// This knowledge lives in IsInRequiresUnreferencedCodeAttributeScope,
// which we still call for consistency here, but it is expected to return false.
if (type.IsInRequiresUnreferencedCodeAttributeScope(out _))
return;

var location = GetPrimaryLocation(type.Locations);

var typeNameResolver = new TypeNameResolver(context.Compilation);
var genericArgumentDataFlow = new GenericArgumentDataFlow(dataFlowAnalyzerContext.TrimAnalyzer, FeatureContext.None, typeNameResolver, type, location, context.ReportDiagnostic);

foreach (var interfaceType in type.Interfaces)
genericArgumentDataFlow.ProcessGenericArgumentDataFlow(interfaceType);
// The generic instantiations in the interface list are only reachable through the
// members of the type, which are all in the Requires scope of a type-level
// RequiresUnreferencedCode, so the attribute silences these warnings.
// This is not the case for the DynamicallyAccessedMembers hierarchy below, or for
// attributes on the type, which may be accessed via reflection from code which is
// not in a Requires scope.
if (!type.HasAttribute(RequiresUnreferencedCodeAnalyzer.RequiresUnreferencedCodeAttribute))
{
var genericArgumentDataFlow = new GenericArgumentDataFlow(dataFlowAnalyzerContext.TrimAnalyzer, FeatureContext.None, typeNameResolver, type, location, context.ReportDiagnostic);

foreach (var interfaceType in type.Interfaces)
genericArgumentDataFlow.ProcessGenericArgumentDataFlow(interfaceType);
}

DynamicallyAccessedMembersTypeHierarchy.ApplyDynamicallyAccessedMembersToTypeHierarchy(typeNameResolver, location, type, context.ReportDiagnostic);
}, SymbolKind.NamedType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ namespace Mono.Linker.Dataflow
{
internal static class GenericArgumentDataFlow
{
public static void ProcessGenericArgumentDataFlow(in MessageOrigin origin, MarkStep markStep, LinkContext context, TypeReference type)
public static void ProcessGenericArgumentDataFlow(in MessageOrigin origin, MarkStep markStep, LinkContext context, TypeReference type, bool suppressTrimAnalysisWarnings = false)
{
var diagnosticContext = new DiagnosticContext(origin, !context.Annotations.ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode(origin.Provider, out _), context);
var reflectionMarker = new ReflectionMarker(context, markStep, enabled: true);
var diagnosticContext = new DiagnosticContext(origin, !suppressTrimAnalysisWarnings && !context.Annotations.ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode(origin.Provider, out _), context);
var reflectionMarker = new ReflectionMarker(context, markStep, enabled: true, suppressTrimAnalysisWarnings: suppressTrimAnalysisWarnings);
ProcessGenericArgumentDataFlow(in diagnosticContext, reflectionMarker, context, type);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ public readonly struct ReflectionMarker
{
readonly MarkStep _markStep;
readonly bool _enabled;
readonly bool _suppressTrimAnalysisWarnings;
public LinkContext Context { get; }

public ReflectionMarker(LinkContext context, MarkStep markStep, bool enabled)
public ReflectionMarker(LinkContext context, MarkStep markStep, bool enabled, bool suppressTrimAnalysisWarnings = false)
{
Context = context;
_markStep = markStep;
_enabled = enabled;
_suppressTrimAnalysisWarnings = suppressTrimAnalysisWarnings;
}

internal void MarkTypeForDynamicallyAccessedMembers(in MessageOrigin origin, TypeReference type, DynamicallyAccessedMemberTypes requiredMemberTypes, DependencyKind dependencyKind, bool declaredOnly = false)
Expand Down Expand Up @@ -125,31 +127,31 @@ internal void MarkMethod(in MessageOrigin origin, MethodReference methodRef, Dep
if (Context.TryResolve(methodRef) is not MethodDefinition method)
return;

_markStep.MarkMethodVisibleToReflection(method, new DependencyInfo(dependencyKind, origin.Provider), origin);
_markStep.MarkMethodVisibleToReflection(method, new DependencyInfo(dependencyKind, origin.Provider), origin, suppressTrimAnalysisWarnings: _suppressTrimAnalysisWarnings);
}

void MarkField(in MessageOrigin origin, FieldDefinition field, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection)
{
if (!_enabled)
return;

_markStep.MarkFieldVisibleToReflection(field, new DependencyInfo(dependencyKind, origin.Provider), origin);
_markStep.MarkFieldVisibleToReflection(field, new DependencyInfo(dependencyKind, origin.Provider), origin, suppressTrimAnalysisWarnings: _suppressTrimAnalysisWarnings);
}

internal void MarkProperty(in MessageOrigin origin, PropertyDefinition property, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection)
{
if (!_enabled)
return;

_markStep.MarkPropertyVisibleToReflection(property, new DependencyInfo(dependencyKind, origin.Provider), origin);
_markStep.MarkPropertyVisibleToReflection(property, new DependencyInfo(dependencyKind, origin.Provider), origin, suppressTrimAnalysisWarnings: _suppressTrimAnalysisWarnings);
}

void MarkEvent(in MessageOrigin origin, EventDefinition @event, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection)
{
if (!_enabled)
return;

_markStep.MarkEventVisibleToReflection(@event, new DependencyInfo(dependencyKind, origin.Provider), origin);
_markStep.MarkEventVisibleToReflection(@event, new DependencyInfo(dependencyKind, origin.Provider), origin, suppressTrimAnalysisWarnings: _suppressTrimAnalysisWarnings);
}

void MarkInterfaceImplementation(in MessageOrigin origin, InterfaceImplementation interfaceImplementation, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection)
Expand Down Expand Up @@ -216,7 +218,7 @@ internal void MarkStaticConstructor(in MessageOrigin origin, TypeReference typeR
if (typeRef.ResolveToTypeDefinition(Context) is not TypeDefinition type)
return;

_markStep.MarkStaticConstructorVisibleToReflection(type, new DependencyInfo(DependencyKind.AccessedViaReflection, origin.Provider), origin);
_markStep.MarkStaticConstructorVisibleToReflection(type, new DependencyInfo(DependencyKind.AccessedViaReflection, origin.Provider), origin, suppressTrimAnalysisWarnings: _suppressTrimAnalysisWarnings);
}
}
}
Loading
Loading