From 34db15f35ba4d319b1aca2899004500eb8832f4f Mon Sep 17 00:00:00 2001
From: vitek-karas <10670590+vitek-karas@users.noreply.github.com>
Date: Fri, 20 Jan 2023 02:18:33 -0800
Subject: [PATCH 1/5] Implements generic parameter data flow mostly trhough
data flow scanner
Previously generic data flow was done from generic dictionary nodes. Problem with that approach is that there's no origin information at that point. The warnings can't point to the place where the problematic instantiation is in the code - we only know that it exists.
Aside from it being unfriendly for the users, it means any RUC or suppressions don't work on these warnings the same way they do in linker/analyzer.
This change modifies the logic to tag the method as "needs data flow" whenever we spot an instantiation of an annotated generic in it somewhere.
Then the actualy validation/marking is done from data flow using the trim analysis patterns.
The only exception to this is generic data flow for base types and interface implementations, that one is done on the EEType nodes.
Note that AOT implements a much more precise version of the generic data flow validation as compared to linker/analyzer. See the big comment at the bening of GenericParameterWarningLocation.cs for how that works.
---
.../Common/Compiler/DisplayNameHelpers.cs | 5 +-
.../Compiler/Dataflow/FlowAnnotations.cs | 34 +-
.../Dataflow/GenericArgumentDataFlow.cs | 83 +-
.../Compiler/Dataflow/MethodBodyScanner.cs | 57 +-
.../Dataflow/ReflectionMethodBodyScanner.cs | 81 +-
...alysisGenericInstantiationAccessPattern.cs | 51 +
.../Dataflow/TrimAnalysisPatternStore.cs | 17 +-
.../TrimAnalysisReflectionAccessPattern.cs | 21 +-
.../ConstructedEETypeNode.cs | 4 +-
.../Compiler/DependencyAnalysis/EETypeNode.cs | 4 +-
.../GenericDefinitionEETypeNode.cs | 4 +-
.../Compiler/MetadataManager.cs | 2 +-
.../Compiler/UsageBasedMetadataManager.cs | 105 +-
.../ILCompiler.Compiler.csproj | 1 +
.../GenericParameterWarningLocation.cs | 1245 +++++++++++++++++
15 files changed, 1584 insertions(+), 130 deletions(-)
create mode 100644 src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisGenericInstantiationAccessPattern.cs
create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterWarningLocation.cs
diff --git a/src/coreclr/tools/Common/Compiler/DisplayNameHelpers.cs b/src/coreclr/tools/Common/Compiler/DisplayNameHelpers.cs
index df62b988f20e19..31ae384844d762 100644
--- a/src/coreclr/tools/Common/Compiler/DisplayNameHelpers.cs
+++ b/src/coreclr/tools/Common/Compiler/DisplayNameHelpers.cs
@@ -89,7 +89,10 @@ public static string GetDisplayName(this MethodDesc method)
if (method.Signature.Length > 0)
{
for (int i = 0; i < method.Signature.Length - 1; i++)
- sb.Append(method.Signature[i].GetDisplayNameWithoutNamespace()).Append(',');
+ {
+ TypeDesc instantiatedType = method.Signature[i].InstantiateSignature(method.OwningType.Instantiation, method.Instantiation);
+ sb.Append(instantiatedType.GetDisplayNameWithoutNamespace()).Append(',');
+ }
sb.Append(method.Signature[method.Signature.Length - 1].GetDisplayNameWithoutNamespace());
}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/FlowAnnotations.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/FlowAnnotations.cs
index 0cf5a42421be89..c4e164791dc625 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/FlowAnnotations.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/FlowAnnotations.cs
@@ -46,8 +46,8 @@ public bool RequiresDataflowAnalysis(MethodDesc method)
try
{
method = method.GetTypicalMethodDefinition();
- return GetAnnotations(method.OwningType).TryGetAnnotation(method, out var methodAnnotations)
- && (methodAnnotations.ReturnParameterAnnotation != DynamicallyAccessedMemberTypes.None || methodAnnotations.ParameterAnnotations != null);
+ TypeAnnotations typeAnnotations = GetAnnotations(method.OwningType);
+ return typeAnnotations.HasGenericParameterAnnotation() || typeAnnotations.TryGetAnnotation(method, out _);
}
catch (TypeSystemException)
{
@@ -73,7 +73,8 @@ public bool RequiresDataflowAnalysis(FieldDesc field)
try
{
field = field.GetTypicalFieldDefinition();
- return GetAnnotations(field.OwningType).TryGetAnnotation(field, out _);
+ TypeAnnotations typeAnnotations = GetAnnotations(field.OwningType);
+ return typeAnnotations.HasGenericParameterAnnotation() || typeAnnotations.TryGetAnnotation(field, out _);
}
catch (TypeSystemException)
{
@@ -105,6 +106,31 @@ public bool HasAnyAnnotations(TypeDesc type)
}
}
+ public bool HasGenericParameterAnnotation(TypeDesc type)
+ {
+ try
+ {
+ return GetAnnotations(type.GetTypeDefinition()).HasGenericParameterAnnotation();
+ }
+ catch (TypeSystemException)
+ {
+ return false;
+ }
+ }
+
+ public bool HasGenericParameterAnnotation(MethodDesc method)
+ {
+ try
+ {
+ method = method.GetTypicalMethodDefinition();
+ return GetAnnotations(method.OwningType).TryGetAnnotation(method, out var annotation) && annotation.GenericParameterAnnotations != null;
+ }
+ catch (TypeSystemException)
+ {
+ return false;
+ }
+ }
+
internal DynamicallyAccessedMemberTypes GetParameterAnnotation(ParameterProxy param)
{
MethodDesc method = param.Method.Method.GetTypicalMethodDefinition();
@@ -884,6 +910,8 @@ public bool TryGetAnnotation(GenericParameterDesc genericParameter, out Dynamica
return false;
}
+
+ public bool HasGenericParameterAnnotation() => _genericParameterAnnotations != null;
}
private readonly struct MethodAnnotations
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/GenericArgumentDataFlow.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/GenericArgumentDataFlow.cs
index 555091d04a7693..eeecfa6c0f623d 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/GenericArgumentDataFlow.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/GenericArgumentDataFlow.cs
@@ -18,45 +18,72 @@
namespace ILCompiler.Dataflow
{
- public readonly struct GenericArgumentDataFlow
+ public static class GenericArgumentDataFlow
{
- private readonly Logger _logger;
- private readonly NodeFactory _factory;
- private readonly FlowAnnotations _annotations;
- private readonly MessageOrigin _origin;
+ public static void ProcessGenericArgumentDataFlow(ref DependencyList dependencies, Logger logger, NodeFactory factory, FlowAnnotations annotations, in MessageOrigin origin, TypeDesc type)
+ {
+ var diagnosticContext = new DiagnosticContext(
+ origin,
+ !logger.ShouldSuppressAnalysisWarningsForRequires(origin.MemberDefinition, DiagnosticUtilities.RequiresUnreferencedCodeAttribute),
+ logger);
+ var reflectionMarker = new ReflectionMarker(logger, factory, annotations, typeHierarchyDataFlow: false, enabled: true);
+
+ ProcessGenericArgumentDataFlow(diagnosticContext, reflectionMarker, type);
+
+ if (reflectionMarker.Dependencies.Count > 0)
+ {
+ if (dependencies == null)
+ dependencies = reflectionMarker.Dependencies;
+ else
+ dependencies.AddRange(reflectionMarker.Dependencies);
+ }
+ }
- public GenericArgumentDataFlow(Logger logger, NodeFactory factory, FlowAnnotations annotations, in MessageOrigin origin)
+ public static void ProcessGenericArgumentDataFlow(in DiagnosticContext diagnosticContext, ReflectionMarker reflectionMarker, TypeDesc type)
{
- _logger = logger;
- _factory = factory;
- _annotations = annotations;
- _origin = origin;
+ if (type.HasInstantiation)
+ {
+ TypeDesc typeDefinition = type.GetTypeDefinition();
+ if (typeDefinition != type)
+ {
+ ProcessGenericInstantiation(diagnosticContext, reflectionMarker, type.Instantiation, typeDefinition.Instantiation);
+ }
+ }
}
- public DependencyList ProcessGenericArgumentDataFlow(GenericParameterDesc genericParameter, TypeDesc genericArgument)
+ public static void ProcessGenericArgumentDataFlow(in DiagnosticContext diagnosticContext, ReflectionMarker reflectionMarker, MethodDesc method)
{
- var genericParameterValue = _annotations.GetGenericParameterValue(genericParameter);
- Debug.Assert(genericParameterValue.DynamicallyAccessedMemberTypes != DynamicallyAccessedMemberTypes.None);
+ if (method.HasInstantiation)
+ {
+ MethodDesc typicalMethod = method.GetTypicalMethodDefinition();
+ if (typicalMethod != method)
+ {
+ ProcessGenericInstantiation(diagnosticContext, reflectionMarker, method.Instantiation, typicalMethod.Instantiation);
+ }
+ }
- MultiValue genericArgumentValue = _annotations.GetTypeValueFromGenericArgument(genericArgument);
+ ProcessGenericArgumentDataFlow(diagnosticContext, reflectionMarker, method.OwningType);
+ }
- var diagnosticContext = new DiagnosticContext(
- _origin,
- _logger.ShouldSuppressAnalysisWarningsForRequires(_origin.MemberDefinition, DiagnosticUtilities.RequiresUnreferencedCodeAttribute),
- _logger);
- return RequireDynamicallyAccessedMembers(diagnosticContext, genericArgumentValue, genericParameterValue, genericParameter.GetDisplayName());
+ public static void ProcessGenericArgumentDataFlow(in DiagnosticContext diagnosticContext, ReflectionMarker reflectionMarker, FieldDesc field)
+ {
+ ProcessGenericArgumentDataFlow(diagnosticContext, reflectionMarker, field.OwningType);
}
- private DependencyList RequireDynamicallyAccessedMembers(
- in DiagnosticContext diagnosticContext,
- in MultiValue value,
- ValueWithDynamicallyAccessedMembers targetValue,
- string reason)
+ private static void ProcessGenericInstantiation(in DiagnosticContext diagnosticContext, ReflectionMarker reflectionMarker, Instantiation instantiation, Instantiation typicalInstantiation)
{
- var reflectionMarker = new ReflectionMarker(_logger, _factory, _annotations, typeHierarchyDataFlowOrigin: null, enabled: true);
- var requireDynamicallyAccessedMembersAction = new RequireDynamicallyAccessedMembersAction(reflectionMarker, diagnosticContext, reason);
- requireDynamicallyAccessedMembersAction.Invoke(value, targetValue);
- return reflectionMarker.Dependencies;
+ for (int i = 0; i < instantiation.Length; i++)
+ {
+ var genericParameter = (GenericParameterDesc)typicalInstantiation[i];
+ if (reflectionMarker.Annotations.GetGenericParameterAnnotation(genericParameter) != default)
+ {
+ var genericParameterValue = reflectionMarker.Annotations.GetGenericParameterValue(genericParameter);
+ Debug.Assert(genericParameterValue.DynamicallyAccessedMemberTypes != DynamicallyAccessedMemberTypes.None);
+ MultiValue genericArgumentValue = reflectionMarker.Annotations.GetTypeValueFromGenericArgument(instantiation[i]);
+ var requireDynamicallyAccessedMembersAction = new RequireDynamicallyAccessedMembersAction(reflectionMarker, diagnosticContext, new GenericParameterOrigin(genericParameter));
+ requireDynamicallyAccessedMembersAction.Invoke(genericArgumentValue, genericParameterValue);
+ }
+ }
}
}
}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs
index 70640274fc3b44..f4beb9857e5927 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs
@@ -800,7 +800,7 @@ protected virtual void Scan(MethodIL methodBody, ref InterproceduralState interp
StackSlot retValue = PopUnknown(currentStack, 1, methodBody, offset);
// If the return value is a reference, treat it as the value itself for now
// We can handle ref return values better later
- ReturnValue = MultiValueLattice.Meet(ReturnValue, DereferenceValue(retValue.Value, locals, ref interproceduralState));
+ ReturnValue = MultiValueLattice.Meet(ReturnValue, DereferenceValue(methodBody, offset, retValue.Value, locals, ref interproceduralState));
ValidateNoReferenceToReference(locals, methodBody, offset);
}
ClearStack(ref currentStack);
@@ -947,23 +947,24 @@ private void ScanLdtoken(MethodIL methodBody, int offset, object operand, Stack<
var nullableDam = new RuntimeTypeHandleForNullableValueWithDynamicallyAccessedMembers(new TypeProxy(type),
new RuntimeTypeHandleForGenericParameterValue(genericParam));
currentStack.Push(new StackSlot(nullableDam));
- return;
+ break;
case MetadataType underlyingType:
var nullableType = new RuntimeTypeHandleForNullableSystemTypeValue(new TypeProxy(type), new SystemTypeValue(underlyingType));
currentStack.Push(new StackSlot(nullableType));
- return;
+ break;
default:
PushUnknown(currentStack);
- return;
+ break;
}
}
else
{
var typeHandle = new RuntimeTypeHandleValue(new TypeProxy(type));
currentStack.Push(new StackSlot(typeHandle));
- return;
}
}
+
+ HandleTypeReflectionAccess(methodBody, offset, type);
}
else if (operand is MethodDesc method)
{
@@ -1026,7 +1027,7 @@ protected void StoreInReference(MultiValue target, MultiValue source, MethodIL m
StoreMethodLocalValue(locals, source, localReference.LocalIndex, curBasicBlock);
break;
case FieldReferenceValue fieldReference
- when GetFieldValue(fieldReference.FieldDefinition).AsSingleValue() is FieldValue fieldValue:
+ when GetFieldValue(method, offset, fieldReference.FieldDefinition).AsSingleValue() is FieldValue fieldValue:
HandleStoreField(method, offset, fieldValue, source);
break;
case ParameterReferenceValue parameterReference
@@ -1038,7 +1039,7 @@ when GetMethodParameterValue(parameterReference.Parameter) is MethodParameterVal
HandleStoreMethodReturnValue(method, offset, methodReturnValue, source);
break;
case FieldValue fieldValue:
- HandleStoreField(method, offset, fieldValue, DereferenceValue(source, locals, ref ipState));
+ HandleStoreField(method, offset, fieldValue, DereferenceValue(method, offset, source, locals, ref ipState));
break;
case IValueWithStaticType valueWithStaticType:
if (valueWithStaticType.StaticType is not null && FlowAnnotations.IsTypeInterestingForDataflow(valueWithStaticType.StaticType))
@@ -1057,7 +1058,25 @@ when GetMethodParameterValue(parameterReference.Parameter) is MethodParameterVal
}
- protected abstract MultiValue GetFieldValue(FieldDesc field);
+ ///
+ /// GetFieldValue is called every time the scanner needs to represent a value of the field
+ /// either as a source or target. It is not called when just a reference to field is created,
+ /// But if such reference is dereferenced then it will get called.
+ /// It is NOT called for hoisted locals.
+ ///
+ ///
+ /// There should be no need to perform checks for hoisted locals. All of our reflection checks are based
+ /// on an assumption that problematic things happen because of running code. Doing things purely in the type system
+ /// (declaring new types which are never instantiated, declaring fields which are never assigned to, ...)
+ /// don't cause problems (or better way, they won't show observable behavioral differences).
+ /// Typically that would mean that accessing fields is also an uninteresting operation, unfortunately
+ /// static fields access can cause execution of static .cctor and that is running code -> possible problems.
+ /// So we have to track accesses in that case.
+ /// Hoisted locals are fields on closure classes/structs which should not have static .ctors, so we don't
+ /// need to track those. It makes the design a bit cleaner because hoisted locals are purely handled in here
+ /// and don't leak over to the reflection handling code in any way.
+ ///
+ protected abstract MultiValue GetFieldValue(MethodIL methodBody, int offset, FieldDesc field);
private void ScanLdfld(
MethodIL methodBody,
@@ -1083,7 +1102,7 @@ private void ScanLdfld(
}
else
{
- value = GetFieldValue(field);
+ value = GetFieldValue(methodBody, offset, field);
}
currentStack.Push(new StackSlot(value));
}
@@ -1119,7 +1138,7 @@ private void ScanStfld(
return;
}
- foreach (var value in GetFieldValue(field))
+ foreach (var value in GetFieldValue(methodBody, offset, field))
{
// GetFieldValue may return different node types, in which case they can't be stored to.
// At least not yet.
@@ -1127,7 +1146,7 @@ private void ScanStfld(
continue;
// Incomplete handling of ref fields -- if we're storing a reference to a value, pretend it's just the value
- MultiValue valueToStore = DereferenceValue(valueToStoreSlot.Value, locals, ref interproceduralState);
+ MultiValue valueToStore = DereferenceValue(methodBody, offset, valueToStoreSlot.Value, locals, ref interproceduralState);
HandleStoreField(methodBody, offset, fieldValue, valueToStore);
}
@@ -1163,7 +1182,12 @@ private ValueNodeList PopCallArguments(
return methodParams;
}
- internal MultiValue DereferenceValue(MultiValue maybeReferenceValue, ValueBasicBlockPair?[] locals, ref InterproceduralState interproceduralState)
+ internal MultiValue DereferenceValue(
+ MethodIL methodBody,
+ int offset,
+ MultiValue maybeReferenceValue,
+ ValueBasicBlockPair?[] locals,
+ ref InterproceduralState interproceduralState)
{
MultiValue dereferencedValue = MultiValueLattice.Top;
foreach (var value in maybeReferenceValue)
@@ -1175,7 +1199,7 @@ internal MultiValue DereferenceValue(MultiValue maybeReferenceValue, ValueBasicB
dereferencedValue,
CompilerGeneratedState.IsHoistedLocal(fieldReferenceValue.FieldDefinition)
? interproceduralState.GetHoistedLocal(new HoistedLocalKey(fieldReferenceValue.FieldDefinition))
- : GetFieldValue(fieldReferenceValue.FieldDefinition));
+ : GetFieldValue(methodBody, offset, fieldReferenceValue.FieldDefinition));
break;
case ParameterReferenceValue parameterReferenceValue:
dereferencedValue = MultiValue.Meet(
@@ -1224,6 +1248,11 @@ protected void AssignRefAndOutParameters(
}
}
+ ///
+ /// Called when type is accessed directly (basically only ldtoken)
+ ///
+ protected abstract void HandleTypeReflectionAccess(MethodIL methodBody, int offset, TypeDesc accessedType);
+
///
/// Called to handle reflection access to a method without any other specifics (ldtoken or ldftn for example)
///
@@ -1260,7 +1289,7 @@ private void HandleCall(
var dereferencedMethodParams = new List();
foreach (var argument in methodArguments)
- dereferencedMethodParams.Add(DereferenceValue(argument, locals, ref interproceduralState));
+ dereferencedMethodParams.Add(DereferenceValue(callingMethodBody, offset, argument, locals, ref interproceduralState));
MultiValue methodReturnValue;
bool handledFunction = HandleCall(
callingMethodBody,
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs
index 7a73ba40d0812f..83fa616dc495e8 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs
@@ -139,7 +139,19 @@ protected override ValueWithDynamicallyAccessedMembers GetMethodParameterValue(P
private MethodParameterValue GetMethodParameterValue(ParameterProxy parameter, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes)
=> _annotations.GetMethodParameterValue(parameter, dynamicallyAccessedMemberTypes);
- protected override MultiValue GetFieldValue(FieldDesc field) => _annotations.GetFieldValue(field);
+ ///
+ /// GetFieldValue is called every time the scanner needs to represent a value of the field
+ /// either as a source or target. It is not called when just a reference to field is created,
+ /// But if such reference is dereferenced then it will get called.
+ ///
+ protected override MultiValue GetFieldValue(MethodIL methodBody, int offset, FieldDesc field)
+ {
+ _origin = _origin.WithInstructionOffset(methodBody, offset);
+
+ ProcessGenericArgumentDataFlow(field);
+
+ return _annotations.GetFieldValue(field);
+ }
private void HandleStoreValueWithDynamicallyAccessedMembers(MethodIL methodBody, int offset, ValueWithDynamicallyAccessedMembers targetValue, MultiValue sourceValue, string reason)
{
@@ -172,6 +184,35 @@ protected override void HandleFieldReflectionAccess(MethodIL methodBody, int off
TrimAnalysisPatterns.Add(new TrimAnalysisReflectionAccessPattern(accessedField, _origin));
}
+ protected override void HandleTypeReflectionAccess(MethodIL methodBody, int offset, TypeDesc accessedType)
+ {
+ // Note that ldtoken alone is technically a reflection access to the type
+ // it doesn't lead to full reflection marking of the type
+ // since we implement full dataflow for type values and accesses to them.
+ _origin = _origin.WithInstructionOffset(methodBody, offset);
+
+ // Only check for generic instantiations.
+ ProcessGenericArgumentDataFlow(accessedType);
+ }
+
+ protected override void HandleMethodReflectionAccess(MethodIL methodBody, int offset, MethodDesc accessedMethod)
+ {
+ _origin = _origin.WithInstructionOffset(methodBody, offset);
+
+ TrimAnalysisPatterns.Add(new TrimAnalysisReflectionAccessPattern(accessedMethod, _origin));
+
+ ProcessGenericArgumentDataFlow(accessedMethod);
+ }
+
+ protected override void HandleFieldReflectionAccess(MethodIL methodBody, int offset, FieldDesc accessedField)
+ {
+ _origin = _origin.WithInstructionOffset(methodBody, offset);
+
+ TrimAnalysisPatterns.Add(new TrimAnalysisReflectionAccessPattern(accessedField, _origin));
+
+ ProcessGenericArgumentDataFlow(accessedField);
+ }
+
public override bool HandleCall(MethodIL callingMethodBody, MethodDesc calledMethod, ILOpcode operation, int offset, ValueNodeList methodParams, out MultiValue methodReturnValue)
{
Debug.Assert(callingMethodBody.OwningMethod == _origin.MemberDefinition);
@@ -201,6 +242,8 @@ public override bool HandleCall(MethodIL callingMethodBody, MethodDesc calledMet
_origin
));
+ ProcessGenericArgumentDataFlow(calledMethod);
+
var diagnosticContext = new DiagnosticContext(_origin, diagnosticsEnabled: false, _logger);
return HandleCall(
callingMethodBody,
@@ -634,6 +677,42 @@ private void HandleAssignmentPattern(
TrimAnalysisPatterns.Add(new TrimAnalysisAssignmentPattern(value, targetValue, origin, reason));
}
+ private void ProcessGenericArgumentDataFlow(MethodDesc method)
+ {
+ // We only need to validate static methods and then all generic methods
+ // Instance non-generic methods don't need validation because the creation of the instance
+ // is the place where the validation will happen.
+ if (!method.Signature.IsStatic && !method.HasInstantiation && !method.IsConstructor)
+ return;
+
+ if ((method.HasInstantiation && _annotations.HasGenericParameterAnnotation(method)) ||
+ (method.OwningType.HasInstantiation && _annotations.HasGenericParameterAnnotation(method.OwningType)))
+ {
+ TrimAnalysisPatterns.Add(new TrimAnalysisGenericInstantiationAccessPattern(method, _origin));
+ }
+ }
+
+ private void ProcessGenericArgumentDataFlow(FieldDesc field)
+ {
+ // We only need to validate static field accesses, instance field accesses don't need generic parameter validation
+ // because the create of the instance would do that instead.
+ if (!field.IsStatic)
+ return;
+
+ if (field.OwningType.HasInstantiation && _annotations.HasGenericParameterAnnotation(field.OwningType))
+ {
+ TrimAnalysisPatterns.Add(new TrimAnalysisGenericInstantiationAccessPattern(field, _origin));
+ }
+ }
+
+ private void ProcessGenericArgumentDataFlow(TypeDesc type)
+ {
+ if (type.HasInstantiation && _annotations.HasGenericParameterAnnotation(type))
+ {
+ TrimAnalysisPatterns.Add(new TrimAnalysisGenericInstantiationAccessPattern(type, _origin));
+ }
+ }
+
private static bool IsPInvokeDangerous(MethodDesc calledMethod, out bool comDangerousMethod, out bool aotUnsafeDelegate)
{
if (!calledMethod.IsPInvoke)
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisGenericInstantiationAccessPattern.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisGenericInstantiationAccessPattern.cs
new file mode 100644
index 00000000000000..a9a7bbb9b044b7
--- /dev/null
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisGenericInstantiationAccessPattern.cs
@@ -0,0 +1,51 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using ILCompiler.Logging;
+using ILLink.Shared.TrimAnalysis;
+using Internal.TypeSystem;
+
+#nullable enable
+
+namespace ILCompiler.Dataflow
+{
+ public readonly record struct TrimAnalysisGenericInstantiationAccessPattern
+ {
+ public TypeSystemEntity Entity { init; get; }
+ public MessageOrigin Origin { init; get; }
+
+ internal TrimAnalysisGenericInstantiationAccessPattern(TypeSystemEntity entity, MessageOrigin origin)
+ {
+ Entity = entity;
+ Origin = origin;
+ }
+
+ // No Merge - there's nothing to merge since this pattern is uniquely identified by both the origin and the entity
+ // and there's only one way to "access" a generic instantiation.
+
+ public void MarkAndProduceDiagnostics(ReflectionMarker reflectionMarker, Logger logger)
+ {
+ var diagnosticContext = new DiagnosticContext(
+ Origin,
+ logger.ShouldSuppressAnalysisWarningsForRequires(Origin.MemberDefinition, DiagnosticUtilities.RequiresUnreferencedCodeAttribute),
+ logger.ShouldSuppressAnalysisWarningsForRequires(Origin.MemberDefinition, DiagnosticUtilities.RequiresDynamicCodeAttribute),
+ logger.ShouldSuppressAnalysisWarningsForRequires(Origin.MemberDefinition, DiagnosticUtilities.RequiresAssemblyFilesAttribute),
+ logger);
+
+ switch (Entity)
+ {
+ case TypeDesc type:
+ GenericArgumentDataFlow.ProcessGenericArgumentDataFlow(diagnosticContext, reflectionMarker, type);
+ break;
+
+ case MethodDesc method:
+ GenericArgumentDataFlow.ProcessGenericArgumentDataFlow(diagnosticContext, reflectionMarker, method);
+ break;
+
+ case FieldDesc field:
+ GenericArgumentDataFlow.ProcessGenericArgumentDataFlow(diagnosticContext, reflectionMarker, field);
+ break;
+ }
+ }
+ }
+}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisPatternStore.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisPatternStore.cs
index 85c9aa72eee383..a241f386489d1e 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisPatternStore.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisPatternStore.cs
@@ -16,6 +16,7 @@ public readonly struct TrimAnalysisPatternStore
private readonly Dictionary<(MessageOrigin, bool), TrimAnalysisAssignmentPattern> AssignmentPatterns;
private readonly Dictionary MethodCallPatterns;
private readonly Dictionary<(MessageOrigin, TypeSystemEntity), TrimAnalysisReflectionAccessPattern> ReflectionAccessPatterns;
+ private readonly Dictionary<(MessageOrigin, TypeSystemEntity), TrimAnalysisGenericInstantiationAccessPattern> GenericInstantiations;
private readonly ValueSetLattice Lattice;
private readonly Logger _logger;
@@ -24,6 +25,7 @@ public TrimAnalysisPatternStore(ValueSetLattice lattice, Logger log
AssignmentPatterns = new Dictionary<(MessageOrigin, bool), TrimAnalysisAssignmentPattern>();
MethodCallPatterns = new Dictionary();
ReflectionAccessPatterns = new Dictionary<(MessageOrigin, TypeSystemEntity), TrimAnalysisReflectionAccessPattern>();
+ GenericInstantiations = new Dictionary<(MessageOrigin, TypeSystemEntity), TrimAnalysisGenericInstantiationAccessPattern>();
Lattice = lattice;
_logger = logger;
}
@@ -60,8 +62,16 @@ public void Add(TrimAnalysisReflectionAccessPattern pattern)
{
ReflectionAccessPatterns.TryAdd((pattern.Origin, pattern.Entity), pattern);
- // No Merge - there's nothing to merge since this pattern is unequily identified by both the origin and the entity
- // and there's only one way to "reflection access" an entity.
+ // No Merge - there's nothing to merge since this pattern is uniquely identified by both the origin and the entity
+ // and there's only one way to "access" a generic instantiation.
+ }
+
+ public void Add(TrimAnalysisGenericInstantiationAccessPattern pattern)
+ {
+ GenericInstantiations.TryAdd((pattern.Origin, pattern.Entity), pattern);
+
+ // No Merge - there's nothing to merge since this pattern is uniquely identified by both the origin and the entity
+ // and there's only one way to "access" a generic instantiation.
}
public void MarkAndProduceDiagnostics(ReflectionMarker reflectionMarker)
@@ -74,6 +84,9 @@ public void MarkAndProduceDiagnostics(ReflectionMarker reflectionMarker)
foreach (var pattern in ReflectionAccessPatterns.Values)
pattern.MarkAndProduceDiagnostics(reflectionMarker, _logger);
+
+ foreach (var pattern in GenericInstantiations.Values)
+ pattern.MarkAndProduceDiagnostics(reflectionMarker, _logger);
}
}
}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisReflectionAccessPattern.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisReflectionAccessPattern.cs
index 8a07621f565396..31217b0580742d 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisReflectionAccessPattern.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisReflectionAccessPattern.cs
@@ -1,9 +1,13 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Diagnostics;
using ILCompiler.Logging;
+using ILLink.Shared.TrimAnalysis;
using Internal.TypeSystem;
+#nullable enable
+
namespace ILCompiler.Dataflow
{
public readonly record struct TrimAnalysisReflectionAccessPattern
@@ -17,12 +21,25 @@ internal TrimAnalysisReflectionAccessPattern(TypeSystemEntity entity, MessageOri
Origin = origin;
}
- // No Merge - there's nothing to merge since this pattern is unequily identified by both the origin and the entity
+ // No Merge - there's nothing to merge since this pattern is uniquely identified by both the origin and the entity
// and there's only one way to "reflection access" an entity.
public void MarkAndProduceDiagnostics(ReflectionMarker reflectionMarker, Logger logger)
{
- reflectionMarker.CheckAndWarnOnReflectionAccess(Origin, Entity);
+ switch (Entity)
+ {
+ case MethodDesc method:
+ reflectionMarker.CheckAndWarnOnReflectionAccess(Origin, method, new MethodOrigin(Origin.MemberDefinition as MethodDesc));
+ break;
+
+ case FieldDesc field:
+ reflectionMarker.CheckAndWarnOnReflectionAccess(Origin, field, new MethodOrigin(Origin.MemberDefinition as MethodDesc));
+ break;
+
+ default:
+ Debug.Fail($"Unsupported entity for reflection access pattern: {Entity}");
+ break;
+ }
}
}
}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs
index 6d734d92ee6e0d..1e9e3144d2be0d 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/ConstructedEETypeNode.cs
@@ -75,8 +75,8 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact
}
}
- // Ask the metadata manager if we have any dependencies due to reflectability.
- factory.MetadataManager.GetDependenciesDueToReflectability(ref dependencyList, factory, _type);
+ // Ask the metadata manager if we have any dependencies due to the presence of the EEType.
+ factory.MetadataManager.GetDependenciesDueToEETypePresence(ref dependencyList, factory, _type);
factory.InteropStubManager.AddInterestingInteropConstructedTypeDependencies(ref dependencyList, factory, _type);
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs
index e6950d68d7dedf..0f1b9951a2c1ee 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs
@@ -596,8 +596,8 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact
if (!ConstructedEETypeNode.CreationAllowed(_type))
{
// If necessary MethodTable is the highest load level for this type, ask the metadata manager
- // if we have any dependencies due to reflectability.
- factory.MetadataManager.GetDependenciesDueToReflectability(ref dependencies, factory, _type);
+ // if we have any dependencies due to presence of the EEType.
+ factory.MetadataManager.GetDependenciesDueToEETypePresence(ref dependencies, factory, _type);
// If necessary MethodTable is the highest load level, consider this a module use
if(_type is MetadataType mdType)
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs
index f6397e07c2d033..904475d484c88d 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericDefinitionEETypeNode.cs
@@ -24,8 +24,8 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact
{
DependencyList dependencyList = null;
- // Ask the metadata manager if we have any dependencies due to reflectability.
- factory.MetadataManager.GetDependenciesDueToReflectability(ref dependencyList, factory, _type);
+ // Ask the metadata manager if we have any dependencies due to the presence of the EEType.
+ factory.MetadataManager.GetDependenciesDueToEETypePresence(ref dependencyList, factory, _type);
return dependencyList;
}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs
index d5d2d7bdfd3861..680d2b34dfeb82 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs
@@ -449,7 +449,7 @@ protected virtual void GetMetadataDependenciesDueToReflectability(ref Dependency
///
/// This method is an extension point that can provide additional metadata-based dependencies to generated EETypes.
///
- public void GetDependenciesDueToReflectability(ref DependencyList dependencies, NodeFactory factory, TypeDesc type)
+ public virtual void GetDependenciesDueToEETypePresence(ref DependencyList dependencies, NodeFactory factory, TypeDesc type)
{
MetadataCategory category = GetMetadataCategory(type);
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs
index 04d765c00e4c78..90efd3aa6a5070 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs
@@ -403,6 +403,38 @@ private static bool IsTrimmableAssembly(ModuleDesc assembly)
return false;
}
+ public override void GetDependenciesDueToEETypePresence(ref DependencyList dependencies, NodeFactory factory, TypeDesc type)
+ {
+ base.GetDependenciesDueToEETypePresence(ref dependencies, factory, type);
+
+ try
+ {
+ if (type.HasBaseType)
+ {
+ GetDataFlowDependenciesForInstantiation(ref dependencies, factory, type.BaseType, type);
+ }
+
+ foreach (var runtimeInterface in type.RuntimeInterfaces)
+ {
+ GetDataFlowDependenciesForInstantiation(ref dependencies, factory, runtimeInterface, type);
+ }
+ }
+ catch (TypeSystemException)
+ {
+ // Wasn't able to do dataflow because of missing references or something like that.
+ // This likely won't compile either, so we don't care about missing dependencies.
+ }
+ }
+
+ private void GetDataFlowDependenciesForInstantiation(ref DependencyList dependencies, NodeFactory factory, TypeDesc type, TypeDesc contextType)
+ {
+ if (type.HasInstantiation && FlowAnnotations.HasGenericParameterAnnotation(type))
+ {
+ TypeDesc instantiatedType = type.InstantiateSignature(contextType.Instantiation, Instantiation.Empty);
+ GenericArgumentDataFlow.ProcessGenericArgumentDataFlow(ref dependencies, Logger, factory, FlowAnnotations, new Logging.MessageOrigin(contextType), instantiatedType);
+ }
+ }
+
public override bool HasConditionalDependenciesDueToEETypePresence(TypeDesc type)
{
// Note: these are duplicated with the checks in GetConditionalDependenciesDueToEETypePresence
@@ -575,27 +607,10 @@ protected override void GetDependenciesDueToMethodCodePresenceInternal(ref Depen
if (scanReflection)
{
- if (methodIL != null && FlowAnnotations.RequiresDataflowAnalysis(method))
+ if (methodIL != null && Dataflow.ReflectionMethodBodyScanner.RequiresReflectionMethodBodyScannerForMethodBody(FlowAnnotations, method))
{
AddDataflowDependency(ref dependencies, factory, methodIL, "Method has annotated parameters");
}
-
- if ((method.HasInstantiation && !method.IsCanonicalMethod(CanonicalFormKind.Any)))
- {
- MethodDesc typicalMethod = method.GetTypicalMethodDefinition();
- Debug.Assert(typicalMethod != method);
-
- GetFlowDependenciesForInstantiation(ref dependencies, factory, method.Instantiation, typicalMethod.Instantiation, method);
- }
-
- TypeDesc owningType = method.OwningType;
- if (owningType.HasInstantiation && !owningType.IsCanonicalSubtype(CanonicalFormKind.Any))
- {
- TypeDesc owningTypeDefinition = owningType.GetTypeDefinition();
- Debug.Assert(owningType != owningTypeDefinition);
-
- GetFlowDependenciesForInstantiation(ref dependencies, factory, owningType.Instantiation, owningTypeDefinition.Instantiation, owningType);
- }
}
if (method.GetTypicalMethodDefinition() is Internal.TypeSystem.Ecma.EcmaMethod ecmaMethod)
@@ -776,52 +791,8 @@ public override DependencyList GetDependenciesForCustomAttribute(NodeFactory fac
return null;
}
- private void GetFlowDependenciesForInstantiation(ref DependencyList dependencies, NodeFactory factory, Instantiation instantiation, Instantiation typicalInstantiation, TypeSystemEntity source)
- {
- for (int i = 0; i < instantiation.Length; i++)
- {
- var genericParameter = (GenericParameterDesc)typicalInstantiation[i];
- if (FlowAnnotations.GetGenericParameterAnnotation(genericParameter) != default)
- {
- try
- {
- var deps = (new ILCompiler.Dataflow.GenericArgumentDataFlow(Logger, factory, FlowAnnotations, new Logging.MessageOrigin(source))).ProcessGenericArgumentDataFlow(genericParameter, instantiation[i]);
- if (deps.Count > 0)
- {
- if (dependencies == null)
- dependencies = deps;
- else
- dependencies.AddRange(deps);
- }
- }
- catch (TypeSystemException)
- {
- // Wasn't able to do dataflow because of missing references or something like that.
- // This likely won't compile either, so we don't care about missing dependencies.
- }
- }
- }
- }
-
public override void GetDependenciesForGenericDictionary(ref DependencyList dependencies, NodeFactory factory, MethodDesc method)
{
- TypeDesc owningType = method.OwningType;
-
- if (FlowAnnotations.HasAnyAnnotations(owningType))
- {
- MethodDesc typicalMethod = method.GetTypicalMethodDefinition();
- Debug.Assert(typicalMethod != method);
-
- GetFlowDependenciesForInstantiation(ref dependencies, factory, method.Instantiation, typicalMethod.Instantiation, method);
-
- if (owningType.HasInstantiation)
- {
- // Since this also introduces a new type instantiation into the system, collect the dependencies for that too.
- // We might not see the instantiated type elsewhere.
- GetFlowDependenciesForInstantiation(ref dependencies, factory, owningType.Instantiation, owningType.GetTypeDefinition().Instantiation, method);
- }
- }
-
// Presence of code might trigger the reflectability dependencies.
if ((_generationOptions & UsageBasedMetadataGenerationOptions.CreateReflectableArtifacts) != 0)
{
@@ -829,16 +800,6 @@ public override void GetDependenciesForGenericDictionary(ref DependencyList depe
}
}
- public override void GetDependenciesForGenericDictionary(ref DependencyList dependencies, NodeFactory factory, TypeDesc type)
- {
- if (FlowAnnotations.HasAnyAnnotations(type))
- {
- TypeDesc typeDefinition = type.GetTypeDefinition();
- Debug.Assert(type != typeDefinition);
- GetFlowDependenciesForInstantiation(ref dependencies, factory, type.Instantiation, typeDefinition.Instantiation, type);
- }
- }
-
public bool GeneratesAttributeMetadata(TypeDesc attributeType)
{
var ecmaType = attributeType.GetTypeDefinition() as EcmaType;
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj
index 33c4e33bde641b..289d7d0b5325a4 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/ILCompiler.Compiler.csproj
@@ -378,6 +378,7 @@
+
diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterWarningLocation.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterWarningLocation.cs
new file mode 100644
index 00000000000000..8aff7ec7ad2f7d
--- /dev/null
+++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterWarningLocation.cs
@@ -0,0 +1,1245 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq.Expressions;
+using System.Security.Policy;
+using Mono.Linker.Tests.Cases.Expectations.Assertions;
+
+namespace Mono.Linker.Tests.Cases.DataFlow
+{
+ // NativeAOT differences in behavior:
+ //
+ // Validation of generic parameters only matters if the instantiation can be used to run code with the substituted type.
+ // So for generic methods the validation has to happen basically always (since any access to the method can lead to the code
+ // of the method executing eventually).
+ // For generic types though the situation is different. Code on the type can only run if the type is instantiated (new)
+ // or if static members are accessed on it (method calls, or fields accesses both can lead to static .cctor execution).
+ // Others usages of the type cannot themselves lead to code execution in the type, and thus don't need to be validated.
+ // Currently linker and analyzer both validate every time there's a type occurrence in the code.
+ // NativeAOT on the other hand only validates the cases which can lead to code execution (this is partially because the compiler
+ // doesn't care about the type in other situations really).
+ // So for example local variables of a given type, or method parameters of that type alone will not cause code execution
+ // inside that type and thus won't be validated by NativeAOT compiler.
+ //
+ // Below this explanation/fact is referred to as "NativeAOT_StorageSpaceType"
+ // Storage space - declaring a storage space as having a specific type doesn't in itself do anything with that type as per
+ // the above description.
+
+ [SkipKeptItemsValidation]
+ [ExpectedNoWarnings]
+ public class GenericParameterWarningLocation
+ {
+ public static void Main ()
+ {
+ TypeInheritance.Test ();
+ TypeImplementingInterface.Test ();
+ MethodParametersAndReturn.Test ();
+ FieldDefinition.Test ();
+ PropertyDefinition.Test ();
+ MethodBody.Test ();
+ }
+
+ class TypeInheritance
+ {
+ class BaseWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods>
+ {
+ public static void GetMethods()
+ {
+ typeof (TPublicMethods).GetMethods ();
+ }
+ }
+
+ class BaseWithTwo<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods,
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields>
+ { }
+
+ // No warning - annotations applied
+ class DerivedWithSpecificType : BaseWithPublicMethods { }
+
+ // No warning - annotations match
+ class DerivedWithMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] TAllMethods>
+ : BaseWithPublicMethods
+ { }
+
+ [ExpectedWarning ("IL2091")]
+ class DerivedWithNoAnnotations
+ : BaseWithPublicMethods
+ { }
+
+ [ExpectedWarning ("IL2091")]
+ class DerivedWithMismatchAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields>
+ : BaseWithPublicMethods
+ { }
+
+ [ExpectedWarning ("IL2091", nameof (DynamicallyAccessedMemberTypes.PublicMethods))]
+ class DerivedWithOneMismatch<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields>
+ : BaseWithTwo
+ { }
+
+ class DerivedWithTwoMatching<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields,
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods>
+ : BaseWithTwo
+ { }
+
+ [ExpectedWarning ("IL2091")]
+ class DerivedWithOnlyStaticMethodReference : BaseWithPublicMethods
+ {
+ // The method body in this case looks like:
+ // BaseWithPublicMethods.GetMethods ()
+ // The type instantiation needs to be validated and in this case it produces a warning.
+ // This is no different from the same code being part of a completely unrelate method/class.
+ // So the fact that this is in derived class has no impact on the validation in this case.
+ [ExpectedWarning ("IL2091")]
+ public static void GetDerivedMethods () => GetMethods ();
+ }
+
+ public static void Test ()
+ {
+ Type t;
+ t = typeof (DerivedWithSpecificType);
+ t = typeof (DerivedWithMatchingAnnotation<>);
+ t = typeof (DerivedWithNoAnnotations<>);
+ t = typeof (DerivedWithMismatchAnnotation<>);
+ t = typeof (DerivedWithOneMismatch<>);
+ t = typeof (DerivedWithTwoMatching<,>);
+
+ DerivedWithOnlyStaticMethodReference.GetDerivedMethods ();
+ }
+ }
+
+ class TypeImplementingInterface
+ {
+ interface IWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> { }
+
+ interface IWithPublicFields<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> { }
+
+ // No warning - annotations applied
+ class ImplementsWithSpecificType : IWithPublicMethods, IWithPublicFields { }
+
+ // No warning - matching annotations
+ class ImplementsWithMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] TAll>
+ : IWithPublicMethods, IWithPublicFields
+ { }
+
+ [ExpectedWarning ("IL2091", nameof (DynamicallyAccessedMemberTypes.PublicFields))]
+ class ImplementsWithOneMismatch<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods>
+ : IWithPublicMethods, IWithPublicFields
+ { }
+
+ [ExpectedWarning ("IL2091", nameof (DynamicallyAccessedMemberTypes.PublicMethods))]
+ [ExpectedWarning ("IL2091", nameof (DynamicallyAccessedMemberTypes.PublicFields))]
+ class ImplementsWithTwoMismatches<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods,
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields>
+ : IWithPublicMethods, IWithPublicFields
+ { }
+
+ public static void Test ()
+ {
+ // Instantiate the types
+ new ImplementsWithSpecificType ();
+ new ImplementsWithMatchingAnnotation ();
+ new ImplementsWithOneMismatch ();
+ new ImplementsWithTwoMismatches ();
+
+ // Also reference the interfaces, otherwise they could be trimmed
+ Type t;
+ t = typeof (IWithPublicMethods<>);
+ t = typeof (IWithPublicFields<>);
+ }
+ }
+
+ //.NativeAOT: Method parameter types are not interesting until something creates an instance of them
+ // so there's no need to validate generic arguments. See comment at the top of the file for more details.
+ class MethodParametersAndReturn
+ {
+ class TypeWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods>
+ { }
+
+ interface IWithTwo<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods,
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields>
+ { }
+
+ static void MethodWithSpecificType (TypeWithPublicMethods one, IWithTwo two) { }
+
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ static void MethodWithOneMismatch (TypeWithPublicMethods one) { }
+
+ [ExpectedWarning ("IL2091", nameof (IWithTwo), ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ [ExpectedWarning ("IL2091", nameof (TypeWithPublicMethods), ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ static void MethodWithTwoMismatches<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields,
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods>
+ (IWithTwo two, TypeWithPublicMethods one)
+ { }
+
+ static TypeWithPublicMethods MethodWithSpecificReturnType () => null;
+
+ static TypeWithPublicMethods MethodWithMatchingReturn<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () => null;
+
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ static TypeWithPublicMethods MethodWithOneMismatchReturn () => null;
+
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ static IWithTwo MethodWithTwoMismatchesInReturn<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields,
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods>
+ () => null;
+
+ public static void Test ()
+ {
+ MethodWithSpecificType (null, null);
+ MethodWithOneMismatch (null);
+ MethodWithTwoMismatches (null, null);
+
+ MethodWithSpecificReturnType ();
+ MethodWithMatchingReturn ();
+ MethodWithOneMismatchReturn ();
+ MethodWithTwoMismatchesInReturn ();
+ }
+ }
+
+ //.NativeAOT: Field types are not interesting until something creates an instance of them
+ // so there's no need to validate generic arguments. See comment at the top of the file for more details.
+ class FieldDefinition
+ {
+ class TypeWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods>
+ { }
+
+ interface IWithTwo<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods,
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields>
+ { }
+
+ class SpecificType
+ {
+ static TypeWithPublicMethods _field;
+
+ public static void Test ()
+ {
+ _field = null;
+ }
+ }
+
+ class OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods>
+ {
+ static TypeWithPublicMethods _field;
+
+ public static void Test ()
+ {
+ _field = null;
+ }
+ }
+
+ class MultipleReferencesToTheSameType<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, TUnknown>
+ {
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ static TypeWithPublicMethods _field1;
+ static TypeWithPublicMethods _field2;
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ static TypeWithPublicMethods _field3;
+
+ public static void Test ()
+ {
+ _field1 = null;
+ _field2 = null;
+ _field3 = null;
+ }
+ }
+
+ class TwoMismatchesInOne<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods,
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields>
+ {
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ static IWithTwo _field;
+
+ public static void Test ()
+ {
+ _field = null;
+ }
+ }
+
+ public static void Test ()
+ {
+ SpecificType.Test ();
+ OneMatchingAnnotation.Test ();
+ MultipleReferencesToTheSameType.Test ();
+ TwoMismatchesInOne.Test ();
+ }
+ }
+
+ //.NativeAOT: Property types are not interesting until something creates an instance of them
+ // so there's no need to validate generic arguments. See comment at the top of the file for more details.
+ // In case of trimmer/aot it's even less important because properties don't exist in IL really
+ // and thus no code can manipulate them directly - only through reflection.
+ class PropertyDefinition
+ {
+ class TypeWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods>
+ { }
+
+ interface IWithTwo<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods,
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields>
+ { }
+
+ class SpecificType
+ {
+ static TypeWithPublicMethods Property { get; set; }
+
+ public static void Test ()
+ {
+ Property = null;
+ }
+ }
+
+ class OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods>
+ {
+ static TypeWithPublicMethods Property { get; set; }
+
+ public static void Test ()
+ {
+ Property = null;
+ }
+ }
+
+ class MultipleReferencesToTheSameType<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods, TUnknown>
+ {
+ // The warning is generated on the backing field
+ [ExpectedWarning ("IL2091", CompilerGeneratedCode = true, ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ static TypeWithPublicMethods Property1 {
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ get;
+
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ set;
+ }
+
+ static TypeWithPublicMethods Property2 {
+ get;
+ set;
+ }
+
+ // The warning is generated on the backing field
+ [ExpectedWarning ("IL2091", CompilerGeneratedCode = true, ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ static TypeWithPublicMethods Property3 {
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ get;
+
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ set;
+ }
+
+ public static void Test ()
+ {
+ Property1 = Property1;
+ Property2 = Property2;
+ Property3 = Property3;
+ }
+ }
+
+ class TwoMismatchesInOne<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods,
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields>
+ {
+ // The warnings are generated on the backing field
+ [ExpectedWarning ("IL2091", CompilerGeneratedCode = true, ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ [ExpectedWarning ("IL2091", CompilerGeneratedCode = true, ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ static IWithTwo Property {
+ // Getter is trimmed and doesn't produce any warning
+ get;
+
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ set;
+ }
+
+ public static void Test ()
+ {
+ Property = null;
+ }
+ }
+
+ public static void Test ()
+ {
+ SpecificType.Test ();
+ OneMatchingAnnotation.Test ();
+ MultipleReferencesToTheSameType.Test ();
+ TwoMismatchesInOne.Test ();
+ }
+ }
+
+ class MethodBody
+ {
+ class TypeWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> : Exception
+ {
+ public static void Method () { }
+ public void InstanceMethod () { }
+
+ public static string Field;
+ public string InstanceField;
+
+ public static string Property { get; set; }
+ }
+
+ interface IWithTwo<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods,
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields>
+ {
+ public static void Method () { }
+ public void InstanceMethod ();
+
+ public static string Field;
+
+ public static string Property { get; set; }
+ }
+
+ class TypeWithTwo<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods,
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> : Exception
+ { }
+
+ static void MethodWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () { }
+
+ void InstanceMethodWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> () { }
+
+ static void MethodWithTwo<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods,
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields> ()
+ { }
+
+ static MethodBody GetInstance () => null;
+
+ static TypeWithPublicMethods GetInstanceForTypeWithPublicMethods () => null;
+
+ class TypeOf
+ {
+ static void AccessOpenTypeDefinition ()
+ {
+ // Accessing the open type definition should not do anything on its own - just validating that it doesn't break anything
+ Type t = typeof (TypeWithPublicMethods<>);
+ t = typeof (IWithTwo<,>);
+ }
+
+ static void SpecificType ()
+ {
+ Type t = typeof (TypeWithPublicMethods);
+ t = typeof (IWithTwo);
+ }
+
+ static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> ()
+ {
+ Type t = typeof (TypeWithPublicMethods);
+ }
+
+ [ExpectedWarning ("IL2091")]
+ [ExpectedWarning ("IL2091")]
+ static void MultipleReferencesToTheSameType<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods,
+ TUnknown> ()
+ {
+ Type t = typeof (TypeWithPublicMethods); // Warn
+ t = typeof (TypeWithPublicMethods); // No warn
+ t = typeof (TypeWithPublicMethods); // Warn
+ }
+
+ [ExpectedWarning ("IL2091")]
+ [ExpectedWarning ("IL2091")]
+ static void TwoMismatchesInOneStatement<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields,
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods>
+ ()
+ {
+ Type t = typeof (IWithTwo);
+ }
+
+ public static void Test ()
+ {
+ AccessOpenTypeDefinition ();
+ SpecificType ();
+ OneMatchingAnnotation ();
+ MultipleReferencesToTheSameType ();
+ TwoMismatchesInOneStatement ();
+ }
+ }
+
+ class MethodCallOnGenericMethod
+ {
+ static void SpecificType ()
+ {
+ MethodWithPublicMethods ();
+ MethodWithTwo ();
+ }
+
+ static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> ()
+ {
+ MethodWithPublicMethods ();
+ }
+
+ [ExpectedWarning ("IL2091")]
+ [ExpectedWarning ("IL2091")]
+ static void MultipleReferencesToTheSameMethod<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods,
+ TUnknown> ()
+ {
+ MethodWithPublicMethods (); // Warn
+ MethodWithPublicMethods (); // No warn
+ MethodWithPublicMethods (); // Warn
+ }
+
+ [ExpectedWarning ("IL2091")]
+ [ExpectedWarning ("IL2091")]
+ static void TwoMismatchesInOneStatement<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields,
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods>
+ ()
+ {
+ MethodWithTwo ();
+ }
+
+ [ExpectedWarning ("IL2091")]
+ static void InstanceMethodMismatch ()
+ {
+ GetInstance ().InstanceMethodWithPublicMethods ();
+ }
+
+ public static void Test ()
+ {
+ SpecificType ();
+ OneMatchingAnnotation ();
+ MultipleReferencesToTheSameMethod ();
+ TwoMismatchesInOneStatement ();
+ InstanceMethodMismatch ();
+ }
+ }
+
+ class MethodCallOnGenericType
+ {
+ static void SpecificType ()
+ {
+ TypeWithPublicMethods.Method ();
+ IWithTwo.Method ();
+ }
+
+ static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> ()
+ {
+ TypeWithPublicMethods.Method ();
+ }
+
+ [ExpectedWarning ("IL2091")]
+ [ExpectedWarning ("IL2091")]
+ static void MultipleReferencesToTheSameMethod<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods,
+ TUnknown> ()
+ {
+ TypeWithPublicMethods.Method (); // Warn
+ TypeWithPublicMethods.Method (); // No warn
+ TypeWithPublicMethods.Method (); // Warn
+ }
+
+ [ExpectedWarning ("IL2091")]
+ [ExpectedWarning ("IL2091")]
+ static void TwoMismatchesInOneStatement<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields,
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods>
+ ()
+ {
+ IWithTwo.Method ();
+ }
+
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ static void InstanceMethodMismatch ()
+ {
+ TypeWithPublicMethods instance = GetInstanceForTypeWithPublicMethods ();
+ instance.InstanceMethod ();
+ }
+
+ public static void Test ()
+ {
+ SpecificType ();
+ OneMatchingAnnotation ();
+ MultipleReferencesToTheSameMethod ();
+ TwoMismatchesInOneStatement ();
+ InstanceMethodMismatch ();
+ }
+ }
+
+ class FieldAccessOnGenericType
+ {
+ static void SpecificType ()
+ {
+ _ = TypeWithPublicMethods.Field;
+ IWithTwo.Field = "";
+ }
+
+ static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> ()
+ {
+ _ = TypeWithPublicMethods.Field;
+ }
+
+ [ExpectedWarning ("IL2091")]
+ [ExpectedWarning ("IL2091")]
+ static void MultipleReferencesToTheSameField<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods,
+ TUnknown> ()
+ {
+ _ = TypeWithPublicMethods.Field; // Warn
+ TypeWithPublicMethods.Field = ""; // No warn
+ TypeWithPublicMethods.Field = ""; // Warn
+ }
+
+ [ExpectedWarning ("IL2091")]
+ [ExpectedWarning ("IL2091")]
+ static void TwoMismatchesInOneStatement<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields,
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods>
+ ()
+ {
+ _ = IWithTwo.Field;
+ }
+
+ // The local variable
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ static void InstanceFieldMismatch ()
+ {
+ TypeWithPublicMethods instance = GetInstanceForTypeWithPublicMethods ();
+ _ = instance.InstanceField;
+ }
+
+ public static void Test ()
+ {
+ SpecificType ();
+ OneMatchingAnnotation ();
+ MultipleReferencesToTheSameField ();
+ TwoMismatchesInOneStatement ();
+ InstanceFieldMismatch ();
+ }
+ }
+
+ //.NativeAOT: Local variable types are not interesting until something creates an instance of them
+ // so there's no need to validate generic arguments. See comment at the top of the file for more details.
+ class LocalVariable
+ {
+ static void SpecificType ()
+ {
+ TypeWithPublicMethods t = null;
+ IWithTwo i = null;
+ }
+
+ static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> ()
+ {
+ TypeWithPublicMethods t = null;
+ }
+
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ static void MultipleReferencesToTheSameType<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods,
+ TUnknown> ()
+ {
+ TypeWithPublicMethods t1 = null; // Warn
+ TypeWithPublicMethods t2 = null; // No warn
+ TypeWithPublicMethods t3 = null; // Warn
+ }
+
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ static void TwoMismatchesInOneStatement<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields,
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods>
+ ()
+ {
+ IWithTwo i = null;
+ }
+
+ public static void Test ()
+ {
+ SpecificType ();
+ OneMatchingAnnotation ();
+ MultipleReferencesToTheSameType ();
+ TwoMismatchesInOneStatement ();
+ }
+ }
+
+ class DelegateUsageOnGenericMethod
+ {
+ static void SpecificType ()
+ {
+ var a = new Action (MethodWithPublicMethods);
+ }
+
+ static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> ()
+ {
+ var a = new Action (MethodWithPublicMethods);
+ }
+
+ [ExpectedWarning ("IL2091")]
+ [ExpectedWarning ("IL2091")]
+ static void MultipleReferencesToTheSameMethod<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods,
+ TUnknown> ()
+ {
+ var a1 = new Action (MethodWithPublicMethods); // Warn
+ var a2 = new Action (MethodWithPublicMethods); // No warn
+ var a3 = new Action (MethodWithPublicMethods); // Warn
+ }
+
+ [ExpectedWarning ("IL2091")]
+ [ExpectedWarning ("IL2091")]
+ static void TwoMismatchesInOneStatement<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields,
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods>
+ ()
+ {
+ var a = new Action (MethodWithTwo);
+ }
+
+ public static void Test ()
+ {
+ SpecificType ();
+ OneMatchingAnnotation ();
+ MultipleReferencesToTheSameMethod ();
+ TwoMismatchesInOneStatement ();
+ }
+ }
+
+ class DelegateUsageOnGenericType
+ {
+ static void SpecificType ()
+ {
+ var a = new Action (TypeWithPublicMethods.Method);
+ }
+
+ static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> ()
+ {
+ var a = new Action (TypeWithPublicMethods.Method);
+ }
+
+ [ExpectedWarning ("IL2091")]
+ [ExpectedWarning ("IL2091")]
+ static void MultipleReferencesToTheSameMethod<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods,
+ TUnknown> ()
+ {
+ var a1 = new Action (TypeWithPublicMethods.Method); // Warn
+ var a2 = new Action (TypeWithPublicMethods.Method); // No warn
+ var a3 = new Action (TypeWithPublicMethods.Method); // Warn
+ }
+
+ [ExpectedWarning ("IL2091")]
+ [ExpectedWarning ("IL2091")]
+ static void TwoMismatchesInOneStatement<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields,
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods>
+ ()
+ {
+ var a = new Action (IWithTwo.Method);
+ }
+
+ public static void Test ()
+ {
+ SpecificType ();
+ OneMatchingAnnotation ();
+ MultipleReferencesToTheSameMethod ();
+ TwoMismatchesInOneStatement ();
+ }
+ }
+
+ class LdTokenOnGenericMethod
+ {
+ static void SpecificType ()
+ {
+ Expression a = () => MethodWithPublicMethods ();
+ }
+
+ static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> ()
+ {
+ Expression a = () => MethodWithPublicMethods ();
+ }
+
+ [ExpectedWarning ("IL2091")]
+ [ExpectedWarning ("IL2091")]
+ static void MultipleReferencesToTheSameMethod<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods,
+ TUnknown> ()
+ {
+ Expression a = () => MethodWithPublicMethods (); // Warn
+ a = () => MethodWithPublicMethods (); // No warn
+ a = () => MethodWithPublicMethods (); // Warn
+ }
+
+ [ExpectedWarning ("IL2091")]
+ [ExpectedWarning ("IL2091")]
+ static void TwoMismatchesInOneStatement<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields,
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods>
+ ()
+ {
+ Expression a = () => MethodWithTwo ();
+ }
+
+ public static void Test ()
+ {
+ SpecificType ();
+ OneMatchingAnnotation ();
+ MultipleReferencesToTheSameMethod ();
+ TwoMismatchesInOneStatement ();
+ }
+ }
+
+ class LdTokenOfMethodOnGenericType
+ {
+ static void SpecificType ()
+ {
+ Expression a = () => TypeWithPublicMethods.Method ();
+ }
+
+ static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> ()
+ {
+ Expression a = () => TypeWithPublicMethods.Method ();
+ }
+
+ // There are two warnings per "callsite" in this case because the generated IL does
+ // ldtoken method
+ // ldtoken owningtype
+ // In order to call the right Expression APIs.
+ [ExpectedWarning ("IL2091")]
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)]
+ [ExpectedWarning ("IL2091")]
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)]
+ static void MultipleReferencesToTheSameMethod<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods,
+ TUnknown> ()
+ {
+ Expression a = () => TypeWithPublicMethods.Method (); // Warn
+ a = () => TypeWithPublicMethods.Method (); // No warn
+ a = () => TypeWithPublicMethods.Method (); // Warn
+ }
+
+ // There are two warnings per "callsite" in this case because the generated IL does
+ // ldtoken method
+ // ldtoken owningtype
+ // In order to call the right Expression APIs.
+ [ExpectedWarning ("IL2091")]
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)]
+ [ExpectedWarning ("IL2091")]
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)]
+ static void TwoMismatchesInOneStatement<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields,
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods>
+ ()
+ {
+ Expression a = () => IWithTwo.Method ();
+ }
+
+ public static void Test ()
+ {
+ SpecificType ();
+ OneMatchingAnnotation ();
+ MultipleReferencesToTheSameMethod ();
+ TwoMismatchesInOneStatement ();
+ }
+ }
+
+ class LdTokenOfFieldOnGenericType
+ {
+ static void SpecificType ()
+ {
+ Expression> a = () => TypeWithPublicMethods.Field;
+ }
+
+ static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> ()
+ {
+ Expression> a = () => TypeWithPublicMethods.Field;
+ }
+
+ // There are two warnings per "callsite" in this case because the generated IL does
+ // ldtoken field
+ // ldtoken owningtype
+ // In order to call the right Expression APIs.
+ [ExpectedWarning ("IL2091")]
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)]
+ [ExpectedWarning ("IL2091")]
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)]
+ static void MultipleReferencesToTheSameField<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods,
+ TUnknown> ()
+ {
+ Expression> a = () => TypeWithPublicMethods.Field; // Warn
+ a = () => TypeWithPublicMethods.Field; // No warn
+ a = () => TypeWithPublicMethods.Field; // Warn
+ }
+
+ // There are two warnings per "callsite" in this case because the generated IL does
+ // ldtoken field
+ // ldtoken owningtype
+ // In order to call the right Expression APIs.
+ [ExpectedWarning ("IL2091")]
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)]
+ [ExpectedWarning ("IL2091")]
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)]
+ static void TwoMismatchesInOneStatement<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields,
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods>
+ ()
+ {
+ Expression> a = () => IWithTwo.Field;
+ }
+
+ public static void Test ()
+ {
+ SpecificType ();
+ OneMatchingAnnotation ();
+ MultipleReferencesToTheSameField ();
+ TwoMismatchesInOneStatement ();
+ }
+ }
+
+ class LdTokenOfPropertyOnGenericType
+ {
+ static void SpecificType ()
+ {
+ Expression> a = () => TypeWithPublicMethods.Property;
+ }
+
+ static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> ()
+ {
+ Expression> a = () => TypeWithPublicMethods.Property;
+ }
+
+ // There are two warnings per "callsite" in this case because the generated IL does
+ // ldtoken method (getter)
+ // ldtoken owningtype
+ // In order to call the right Expression APIs.
+ [ExpectedWarning ("IL2091")]
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)]
+ [ExpectedWarning ("IL2091")]
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)]
+ static void MultipleReferencesToTheSameProperty<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods,
+ TUnknown> ()
+ {
+ Expression> a = () => TypeWithPublicMethods.Property; // Warn
+ a = () => TypeWithPublicMethods.Property; // No warn
+ a = () => TypeWithPublicMethods.Property; // Warn
+ }
+
+ // There are two warnings per "callsite" in this case because the generated IL does
+ // ldtoken method (getter)
+ // ldtoken owningtype
+ // In order to call the right Expression APIs.
+ [ExpectedWarning ("IL2091")]
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)]
+ [ExpectedWarning ("IL2091")]
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)]
+ static void TwoMismatchesInOneStatement<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields,
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods>
+ ()
+ {
+ Expression> a = () => IWithTwo.Property;
+ }
+
+ public static void Test ()
+ {
+ SpecificType ();
+ OneMatchingAnnotation ();
+ MultipleReferencesToTheSameProperty ();
+ TwoMismatchesInOneStatement ();
+ }
+ }
+
+ class CreateInstance
+ {
+ static void SpecificType ()
+ {
+ object a = new TypeWithPublicMethods ();
+ }
+
+ static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> ()
+ {
+ object a = new TypeWithPublicMethods ();
+ }
+
+ [ExpectedWarning ("IL2091")]
+ [ExpectedWarning ("IL2091")]
+ static void MultipleReferencesToTheSameMethod<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods,
+ TUnknown> ()
+ {
+ object a1 = new TypeWithPublicMethods (); // Warn
+ object a2 = new TypeWithPublicMethods (); // No warn
+ object a3 = new TypeWithPublicMethods (); // Warn
+ }
+
+ [ExpectedWarning ("IL2091")]
+ [ExpectedWarning ("IL2091")]
+ static void TwoMismatchesInOneStatement<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields,
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods>
+ ()
+ {
+ object a = new TypeWithTwo ();
+ }
+
+ public static void Test ()
+ {
+ SpecificType ();
+ OneMatchingAnnotation ();
+ MultipleReferencesToTheSameMethod ();
+ TwoMismatchesInOneStatement ();
+ }
+ }
+
+ //.NativeAOT: Checking an instance for its type is not interesting until something creates an instance of that type
+ // so there's no need to validate generic arguments. See comment at the top of the file for more details.
+ class IsInstance
+ {
+ static object _value = null;
+
+ static void SpecificType ()
+ {
+ bool a = _value is TypeWithPublicMethods;
+ }
+
+ static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> ()
+ {
+ bool a = _value is TypeWithPublicMethods;
+ }
+
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ static void MultipleReferencesToTheSameMethod<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods,
+ TUnknown> ()
+ {
+ bool a1 = _value is TypeWithPublicMethods; // Warn
+ bool a2 = _value is TypeWithPublicMethods; // No warn
+ bool a3 = _value is TypeWithPublicMethods; // Warn
+ }
+
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ static void TwoMismatchesInOneStatement<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields,
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods>
+ ()
+ {
+ bool a = _value is TypeWithTwo;
+ }
+
+ public static void Test ()
+ {
+ SpecificType ();
+ OneMatchingAnnotation ();
+ MultipleReferencesToTheSameMethod ();
+ TwoMismatchesInOneStatement ();
+ }
+ }
+
+ // This is basically the same operation as IsInstance
+ class AsType
+ {
+ static object _value = null;
+
+ static void SpecificType ()
+ {
+ object a = _value as TypeWithPublicMethods;
+ }
+
+ static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> ()
+ {
+ object a = _value as TypeWithPublicMethods;
+ }
+
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ static void MultipleReferencesToTheSameMethod<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods,
+ TUnknown> ()
+ {
+ object a1 = _value as TypeWithPublicMethods; // Warn
+ object a2 = _value as TypeWithPublicMethods; // No warn
+ object a3 = _value as TypeWithPublicMethods; // Warn
+ }
+
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ static void TwoMismatchesInOneStatement<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields,
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods>
+ ()
+ {
+ object a = _value as TypeWithTwo;
+ }
+
+ public static void Test ()
+ {
+ SpecificType ();
+ OneMatchingAnnotation ();
+ MultipleReferencesToTheSameMethod ();
+ TwoMismatchesInOneStatement ();
+ }
+ }
+
+ //.NativeAOT: Exception types are effectively very similar to local variable or method parameters.
+ // and are not interesting until something creates an instance of them
+ // so there's no need to validate generic arguments. See comment at the top of the file for more details.
+ class ExceptionCatch
+ {
+ static void SpecificType ()
+ {
+ try {
+ DoNothing ();
+ } catch (TypeWithPublicMethods) {
+ }
+ }
+
+ static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> ()
+ {
+ try {
+ DoNothing ();
+ } catch (TypeWithPublicMethods) {
+ }
+ }
+
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ static void MultipleReferencesToTheSameType<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods,
+ TUnknown> ()
+ {
+ try {
+ DoNothing ();
+ } catch (TypeWithPublicMethods) { // Warn
+ }
+
+ try {
+ DoNothing ();
+ } catch (TypeWithPublicMethods) { // No warn
+ }
+
+ try {
+ DoNothing ();
+ } catch (TypeWithPublicMethods) { // Warn
+ }
+ }
+
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ static void TwoMismatchesInOneStatement<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields,
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods>
+ ()
+ {
+ try {
+ DoNothing ();
+ } catch (TypeWithTwo) { // Warn x2
+ }
+ }
+
+ public static void Test ()
+ {
+ SpecificType ();
+ OneMatchingAnnotation ();
+ MultipleReferencesToTheSameType ();
+ TwoMismatchesInOneStatement ();
+ }
+ }
+
+ // This is basically the same as IsInstance and thus not dangerous
+ class ExceptionFilter
+ {
+ static void SpecificType ()
+ {
+ try {
+ DoNothing ();
+ } catch (Exception ex) when (ex is TypeWithPublicMethods) {
+ }
+ }
+
+ static void OneMatchingAnnotation<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods> ()
+ {
+ try {
+ DoNothing ();
+ } catch (Exception ex) when (ex is TypeWithPublicMethods) {
+ }
+ }
+
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ static void MultipleReferencesToTheSameType<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods,
+ TUnknown> ()
+ {
+ try {
+ DoNothing ();
+ } catch (Exception ex) when (ex is TypeWithPublicMethods) { // Warn
+ }
+
+ try {
+ DoNothing ();
+ } catch (Exception ex) when (ex is TypeWithPublicMethods) { // No warn
+ }
+
+ try {
+ DoNothing ();
+ } catch (Exception ex) when (ex is TypeWithPublicMethods) { // Warn
+ }
+ }
+
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // NativeAOT_StorageSpaceType
+ static void TwoMismatchesInOneStatement<
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] TPublicFields,
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] TPublicMethods>
+ ()
+ {
+ try {
+ DoNothing ();
+ } catch (Exception ex) when (ex is TypeWithTwo) { // Warn x2
+ }
+ }
+
+ public static void Test ()
+ {
+ SpecificType ();
+ OneMatchingAnnotation ();
+ MultipleReferencesToTheSameType ();
+ TwoMismatchesInOneStatement ();
+ }
+ }
+
+ public static void Test ()
+ {
+ TypeOf.Test ();
+ MethodCallOnGenericMethod.Test ();
+ MethodCallOnGenericType.Test ();
+ FieldAccessOnGenericType.Test ();
+ LocalVariable.Test ();
+ DelegateUsageOnGenericMethod.Test ();
+ DelegateUsageOnGenericType.Test ();
+ LdTokenOnGenericMethod.Test ();
+ LdTokenOfMethodOnGenericType.Test ();
+ LdTokenOfFieldOnGenericType.Test ();
+ LdTokenOfPropertyOnGenericType.Test ();
+ CreateInstance.Test ();
+ IsInstance.Test ();
+ AsType.Test ();
+ ExceptionCatch.Test ();
+ ExceptionFilter.Test ();
+ }
+ }
+
+ class TestType { }
+
+ static void DoNothing () { }
+ }
+}
From f699343e9f6d12ea701ba0645500575fd00eaaba Mon Sep 17 00:00:00 2001
From: vitek-karas <10670590+vitek-karas@users.noreply.github.com>
Date: Fri, 20 Jan 2023 03:06:34 -0800
Subject: [PATCH 2/5] Fix build after rebase
---
.../Compiler/Dataflow/GenericArgumentDataFlow.cs | 4 ++--
.../Compiler/Dataflow/ReflectionMethodBodyScanner.cs | 12 ------------
.../Dataflow/TrimAnalysisReflectionAccessPattern.cs | 5 ++---
3 files changed, 4 insertions(+), 17 deletions(-)
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/GenericArgumentDataFlow.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/GenericArgumentDataFlow.cs
index eeecfa6c0f623d..6d7b357a9a6e18 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/GenericArgumentDataFlow.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/GenericArgumentDataFlow.cs
@@ -26,7 +26,7 @@ public static void ProcessGenericArgumentDataFlow(ref DependencyList dependencie
origin,
!logger.ShouldSuppressAnalysisWarningsForRequires(origin.MemberDefinition, DiagnosticUtilities.RequiresUnreferencedCodeAttribute),
logger);
- var reflectionMarker = new ReflectionMarker(logger, factory, annotations, typeHierarchyDataFlow: false, enabled: true);
+ var reflectionMarker = new ReflectionMarker(logger, factory, annotations, typeHierarchyDataFlowOrigin: null, enabled: true);
ProcessGenericArgumentDataFlow(diagnosticContext, reflectionMarker, type);
@@ -80,7 +80,7 @@ private static void ProcessGenericInstantiation(in DiagnosticContext diagnosticC
var genericParameterValue = reflectionMarker.Annotations.GetGenericParameterValue(genericParameter);
Debug.Assert(genericParameterValue.DynamicallyAccessedMemberTypes != DynamicallyAccessedMemberTypes.None);
MultiValue genericArgumentValue = reflectionMarker.Annotations.GetTypeValueFromGenericArgument(instantiation[i]);
- var requireDynamicallyAccessedMembersAction = new RequireDynamicallyAccessedMembersAction(reflectionMarker, diagnosticContext, new GenericParameterOrigin(genericParameter));
+ var requireDynamicallyAccessedMembersAction = new RequireDynamicallyAccessedMembersAction(reflectionMarker, diagnosticContext, genericParameter.GetDisplayName());
requireDynamicallyAccessedMembersAction.Invoke(genericArgumentValue, genericParameterValue);
}
}
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs
index 83fa616dc495e8..8b54be44762117 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs
@@ -172,18 +172,6 @@ protected override void HandleStoreParameter(MethodIL methodBody, int offset, Me
protected override void HandleStoreMethodReturnValue(MethodIL methodBody, int offset, MethodReturnValue returnValue, MultiValue valueToStore)
=> HandleStoreValueWithDynamicallyAccessedMembers(methodBody, offset, returnValue, valueToStore, returnValue.Method.GetDisplayName());
- protected override void HandleMethodReflectionAccess(MethodIL methodBody, int offset, MethodDesc accessedMethod)
- {
- _origin = _origin.WithInstructionOffset(methodBody, offset);
- TrimAnalysisPatterns.Add(new TrimAnalysisReflectionAccessPattern(accessedMethod, _origin));
- }
-
- protected override void HandleFieldReflectionAccess(MethodIL methodBody, int offset, FieldDesc accessedField)
- {
- _origin = _origin.WithInstructionOffset(methodBody, offset);
- TrimAnalysisPatterns.Add(new TrimAnalysisReflectionAccessPattern(accessedField, _origin));
- }
-
protected override void HandleTypeReflectionAccess(MethodIL methodBody, int offset, TypeDesc accessedType)
{
// Note that ldtoken alone is technically a reflection access to the type
diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisReflectionAccessPattern.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisReflectionAccessPattern.cs
index 31217b0580742d..8f40b94c02d811 100644
--- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisReflectionAccessPattern.cs
+++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/TrimAnalysisReflectionAccessPattern.cs
@@ -3,7 +3,6 @@
using System.Diagnostics;
using ILCompiler.Logging;
-using ILLink.Shared.TrimAnalysis;
using Internal.TypeSystem;
#nullable enable
@@ -29,11 +28,11 @@ public void MarkAndProduceDiagnostics(ReflectionMarker reflectionMarker, Logger
switch (Entity)
{
case MethodDesc method:
- reflectionMarker.CheckAndWarnOnReflectionAccess(Origin, method, new MethodOrigin(Origin.MemberDefinition as MethodDesc));
+ reflectionMarker.CheckAndWarnOnReflectionAccess(Origin, method);
break;
case FieldDesc field:
- reflectionMarker.CheckAndWarnOnReflectionAccess(Origin, field, new MethodOrigin(Origin.MemberDefinition as MethodDesc));
+ reflectionMarker.CheckAndWarnOnReflectionAccess(Origin, field);
break;
default:
From a6cec9ecf9b1cf19e0ae6db34ddafa3ec9740d8b Mon Sep 17 00:00:00 2001
From: vitek-karas <10670590+vitek-karas@users.noreply.github.com>
Date: Fri, 20 Jan 2023 14:00:15 -0800
Subject: [PATCH 3/5] Add GenericParameterDataFlow tests - change test infra
Changes the expected warning validation to use tokens to compare message origins (same reason as with Kept validation - consistently converting things to string is hard)
---
.../DataFlow/GenericParameterDataFlow.cs | 892 ++++++++++++++++++
.../GenericParameterWarningLocation.cs | 7 +-
.../TestCasesRunner/AssemblyChecker.cs | 39 -
.../TestCasesRunner/AssemblyQualifiedToken.cs | 51 +
.../TestCasesRunner/ResultChecker.cs | 11 +-
5 files changed, 957 insertions(+), 43 deletions(-)
create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs
create mode 100644 src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/AssemblyQualifiedToken.cs
diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs
new file mode 100644
index 00000000000000..aa80500a606f4c
--- /dev/null
+++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs
@@ -0,0 +1,892 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Reflection;
+using Mono.Linker.Tests.Cases.Expectations.Assertions;
+using Mono.Linker.Tests.Cases.Expectations.Helpers;
+using BindingFlags = System.Reflection.BindingFlags;
+
+namespace Mono.Linker.Tests.Cases.DataFlow
+{
+ // NativeAOT differences in behavior:
+ //
+ // See the description on top of GenericParameterWarningLocation for the expected differences in behavior
+ // for NativeAOT.
+ // The tests affected by this are marked with "NativeAOT_StorageSpaceType"
+ //
+
+ [SkipKeptItemsValidation]
+ [ExpectedNoWarnings]
+ public class GenericParameterDataFlow
+ {
+ public static void Main ()
+ {
+ TestSingleGenericParameterOnType ();
+ TestMultipleGenericParametersOnType ();
+ TestBaseTypeGenericRequirements ();
+ TestDeepNestedTypesWithGenerics ();
+ TestInterfaceTypeGenericRequirements ();
+ TestTypeGenericRequirementsOnMembers ();
+ TestPartialInstantiationTypes ();
+
+ TestSingleGenericParameterOnMethod ();
+ TestMultipleGenericParametersOnMethod ();
+ TestMethodGenericParametersViaInheritance ();
+
+ TestNewConstraintSatisfiesParameterlessConstructor