Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/linker.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSven Boemer <sbomer@gmail.com>2022-06-07 21:23:01 +0300
committerGitHub <noreply@github.com>2022-06-07 21:23:01 +0300
commit42eddb34cbde2364da89dc40dac722957f225b15 (patch)
treeb4d7b99aeb02c19a57fd1fb5a0062a7d5a834e4d
parent1481a51970586b26208a7bc6173dc77d658f3508 (diff)
Scan compiler-generated methods as a group (#2792)
This scans compiler-generated methods together as a group when marking the corresponding user code. There are two scans that we do: 1. An initial scan to determine whether we need to run the dataflow scanner. It is also what marks static dependencies of the method IL. 2. The full dataflow scan (only if the initial scan says we need to). This produces warnings and marks reflection dependencies. Both scans are now done for the group of compiler-generated methods when marking the user code. For now, we only do the dataflow scan once per method, but in a later change, we will need to allow re-scanning compiler-generated callees (as part of the full scan for the corresponding user code) to properly track captured state. When compiler-generated code is accessed via reflection, we now don't do a dataflow scan because we don't have the context that might be captured from user code. This also means that reflection-dependencies of the compiler-generated code aren't kept, and dataflow warnings aren't produced, unless the code is reached through the corresponding user method. To guard against this, there are new warnings on reflection access to compiler-generated code. The reflection access warnings are only for compiler-generated code which would normally require the reflection method body scanner. This is a heuristic meant to conservatively approximate "this compiler-generated code would produce dataflow warnings if invoked by reflection with an unknown context".
-rw-r--r--src/ILLink.Shared/DiagnosticId.cs5
-rw-r--r--src/ILLink.Shared/SharedStrings.resx18
-rw-r--r--src/linker/Linker.Dataflow/MethodBodyScanner.cs80
-rw-r--r--src/linker/Linker.Dataflow/MethodProxy.cs9
-rw-r--r--src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs26
-rw-r--r--src/linker/Linker.Dataflow/TrimAnalysisAssignmentPattern.cs7
-rw-r--r--src/linker/Linker.Dataflow/TrimAnalysisMethodCallPattern.cs3
-rw-r--r--src/linker/Linker.Dataflow/TrimAnalysisPatternStore.cs6
-rw-r--r--src/linker/Linker.Steps/MarkStep.cs164
-rw-r--r--src/linker/Linker/CompilerGeneratedState.cs114
-rw-r--r--test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs6
-rw-r--r--test/ILLink.RoslynAnalyzer.Tests/RequiresCapabilityTests.cs8
-rw-r--r--test/Mono.Linker.Tests.Cases/DataFlow/CompilerGeneratedCodeAccessedViaReflection.cs523
-rw-r--r--test/Mono.Linker.Tests.Cases/RequiresCapability/ReflectionAccessFromCompilerGeneratedCode.cs213
-rw-r--r--test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresInCompilerGeneratedCode.cs136
-rw-r--r--test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnClass.cs18
-rw-r--r--test/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInCompilerGeneratedCode.cs23
17 files changed, 1237 insertions, 122 deletions
diff --git a/src/ILLink.Shared/DiagnosticId.cs b/src/ILLink.Shared/DiagnosticId.cs
index ac417f4ee..8b7f2166b 100644
--- a/src/ILLink.Shared/DiagnosticId.cs
+++ b/src/ILLink.Shared/DiagnosticId.cs
@@ -179,6 +179,9 @@ namespace ILLink.Shared
DynamicallyAccessedMembersOnTypeReferencesMemberOnBaseWithDynamicallyAccessedMembers = 2115,
RequiresUnreferencedCodeOnStaticConstructor = 2116,
MethodsAreAssociatedWithUserMethod = 2117,
+ CompilerGeneratedMemberAccessedViaReflection = 2118,
+ DynamicallyAccessedMembersOnTypeReferencesCompilerGeneratedMember = 2119,
+ DynamicallyAccessedMembersOnTypeReferencesCompilerGeneratedMemberOnBase = 2120,
// Single-file diagnostic ids.
AvoidAssemblyLocationInSingleFile = 3000,
@@ -215,7 +218,7 @@ namespace ILLink.Shared
2103 => MessageSubCategory.TrimAnalysis,
2106 => MessageSubCategory.TrimAnalysis,
2107 => MessageSubCategory.TrimAnalysis,
- >= 2109 and <= 2116 => MessageSubCategory.TrimAnalysis,
+ >= 2109 and <= 2120 => MessageSubCategory.TrimAnalysis,
>= 3050 and <= 3052 => MessageSubCategory.AotAnalysis,
>= 3054 and <= 3055 => MessageSubCategory.AotAnalysis,
_ => MessageSubCategory.None,
diff --git a/src/ILLink.Shared/SharedStrings.resx b/src/ILLink.Shared/SharedStrings.resx
index d53bb93f7..29d51ff1e 100644
--- a/src/ILLink.Shared/SharedStrings.resx
+++ b/src/ILLink.Shared/SharedStrings.resx
@@ -1071,6 +1071,24 @@
<data name="MethodsAreAssociatedWithUserMethodMessage" xml:space="preserve">
<value>Methods '{0}' and '{1}' are both associated with lambda or local function '{2}'. This is currently unsupported and may lead to incorrectly reported warnings.</value>
</data>
+ <data name="CompilerGeneratedMemberAccessedViaReflectionTitle" xml:space="preserve">
+ <value>Compiler-generated member is accessed via reflection. Trimmer can't guarantee availability of the requirements of the member.</value>
+ </data>
+ <data name="CompilerGeneratedMemberAccessedViaReflectionMessage" xml:space="preserve">
+ <value>Compiler-generated member '{0}' is accessed via reflection. Trimmer can't guarantee availability of the requirements of the member.</value>
+ </data>
+ <data name="DynamicallyAccessedMembersOnTypeReferencesCompilerGeneratedMemberTitle" xml:space="preserve">
+ <value>'DynamicallyAccessedMemberAttribute' on a type or one of its base types references a compiler-generated member.</value>
+ </data>
+ <data name="DynamicallyAccessedMembersOnTypeReferencesCompilerGeneratedMemberMessage" xml:space="preserve">
+ <value>'DynamicallyAccessedMemberAttribute' on '{0}' or one of its base types references compiler-generated member '{1}'.</value>
+ </data>
+ <data name="DynamicallyAccessedMembersOnTypeReferencesCompilerGeneratedMemberOnBaseTitle" xml:space="preserve">
+ <value>'DynamicallyAccessedMemberAttribute' on a type or one of its base types references a compiler-generated member.</value>
+ </data>
+ <data name="DynamicallyAccessedMembersOnTypeReferencesCompilerGeneratedMemberOnBaseMessage" xml:space="preserve">
+ <value>'DynamicallyAccessedMemberAttribute' on '{0}' or one of its base types references compiler-generated member '{1}'.</value>
+ </data>
<data name="AvoidAssemblyLocationInSingleFileTitle" xml:space="preserve">
<value>Avoid accessing Assembly file path when publishing as a single file</value>
</data>
diff --git a/src/linker/Linker.Dataflow/MethodBodyScanner.cs b/src/linker/Linker.Dataflow/MethodBodyScanner.cs
index 300cea2f7..0562acee1 100644
--- a/src/linker/Linker.Dataflow/MethodBodyScanner.cs
+++ b/src/linker/Linker.Dataflow/MethodBodyScanner.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using ILLink.Shared;
using ILLink.Shared.DataFlow;
@@ -44,6 +45,7 @@ namespace Mono.Linker.Dataflow
{
protected readonly LinkContext _context;
protected static ValueSetLattice<SingleValue> MultiValueLattice => default;
+ protected static ValueSetLattice<MethodProxy> MethodLattice => default;
protected MethodBodyScanner (LinkContext context)
{
@@ -220,7 +222,76 @@ namespace Mono.Linker.Dataflow
}
}
- public void Scan (MethodBody methodBody)
+ // Scans the method as well as any nested functions (local functions or lambdas) and state machines
+ // reachable from it.
+ public virtual void InterproceduralScan (MethodBody methodBody)
+ {
+ var methodsInGroup = new ValueSet<MethodProxy> (methodBody.Method);
+
+ // Optimization to prevent multiple scans of a method.
+ // Eventually we will need to allow re-scanning in some cases, for example
+ // when we discover new inputs to a method. But we aren't doing dataflow across
+ // lambdas and local functions yet, so no need for now.
+ HashSet<MethodDefinition> scannedMethods = new HashSet<MethodDefinition> ();
+
+ while (true) {
+ if (!TryGetNextMethodToScan (out MethodDefinition? methodToScan))
+ break;
+
+ scannedMethods.Add (methodToScan);
+ Scan (methodToScan.Body, ref methodsInGroup);
+
+ // For state machine methods, also scan the state machine members.
+ // Simplification: assume that all generated methods of the state machine type are
+ // invoked at the point where the state machine method is called.
+ if (CompilerGeneratedState.TryGetStateMachineType (methodToScan, out TypeDefinition? stateMachineType)) {
+ foreach (var method in stateMachineType.Methods) {
+ Debug.Assert (!CompilerGeneratedNames.IsLambdaOrLocalFunction (method.Name));
+ if (method.Body is MethodBody stateMachineBody)
+ Scan (stateMachineBody, ref methodsInGroup);
+ }
+ }
+ }
+
+#if DEBUG
+ // Validate that the compiler-generated callees tracked by the compiler-generated state
+ // are the same set of methods that we discovered and scanned above.
+ if (_context.CompilerGeneratedState.TryGetCompilerGeneratedCalleesForUserMethod (methodBody.Method, out List<IMemberDefinition>? compilerGeneratedCallees)) {
+ var calleeMethods = compilerGeneratedCallees.OfType<MethodDefinition> ();
+ Debug.Assert (methodsInGroup.Count () == 1 + calleeMethods.Count ());
+ foreach (var method in calleeMethods)
+ Debug.Assert (methodsInGroup.Contains (method));
+ } else {
+ Debug.Assert (methodsInGroup.Count () == 1);
+ }
+#endif
+
+ bool TryGetNextMethodToScan ([NotNullWhen (true)] out MethodDefinition? method)
+ {
+ foreach (var candidate in methodsInGroup) {
+ var candidateMethod = candidate.Method;
+ if (!scannedMethods.Contains (candidateMethod) && candidateMethod.HasBody) {
+ method = candidateMethod;
+ return true;
+ }
+ }
+ method = null;
+ return false;
+ }
+ }
+
+ void TrackNestedFunctionReference (MethodReference referencedMethod, ref ValueSet<MethodProxy> methodsInGroup)
+ {
+ if (_context.TryResolve (referencedMethod) is not MethodDefinition method)
+ return;
+
+ if (!CompilerGeneratedNames.IsLambdaOrLocalFunction (method.Name))
+ return;
+
+ methodsInGroup = MethodLattice.Meet (methodsInGroup, new (method));
+ }
+
+ protected virtual void Scan (MethodBody methodBody, ref ValueSet<MethodProxy> methodsInGroup)
{
MethodDefinition thisMethod = methodBody.Method;
@@ -329,7 +400,6 @@ namespace Mono.Linker.Dataflow
break;
case Code.Arglist:
- case Code.Ldftn:
case Code.Sizeof:
case Code.Ldc_I8:
case Code.Ldc_R4:
@@ -337,6 +407,11 @@ namespace Mono.Linker.Dataflow
PushUnknown (currentStack);
break;
+ case Code.Ldftn:
+ TrackNestedFunctionReference ((MethodReference) operation.Operand, ref methodsInGroup);
+ PushUnknown (currentStack);
+ break;
+
case Code.Ldarg:
case Code.Ldarg_0:
case Code.Ldarg_1:
@@ -559,6 +634,7 @@ namespace Mono.Linker.Dataflow
case Code.Call:
case Code.Callvirt:
case Code.Newobj:
+ TrackNestedFunctionReference ((MethodReference) operation.Operand, ref methodsInGroup);
HandleCall (methodBody, operation, currentStack, locals, curBasicBlock);
break;
diff --git a/src/linker/Linker.Dataflow/MethodProxy.cs b/src/linker/Linker.Dataflow/MethodProxy.cs
index a903f5b57..2b2209566 100644
--- a/src/linker/Linker.Dataflow/MethodProxy.cs
+++ b/src/linker/Linker.Dataflow/MethodProxy.cs
@@ -1,13 +1,14 @@
// 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.Collections.Immutable;
using Mono.Cecil;
using Mono.Linker;
namespace ILLink.Shared.TypeSystemProxy
{
- readonly partial struct MethodProxy
+ readonly partial struct MethodProxy : IEquatable<MethodProxy>
{
public MethodProxy (MethodDefinition method) => Method = method;
@@ -53,5 +54,11 @@ namespace ILLink.Shared.TypeSystemProxy
public override string ToString () => Method.ToString ();
public ReferenceKind ParameterReferenceKind (int index) => Method.ParameterReferenceKind (index);
+
+ public bool Equals (MethodProxy other) => Method.Equals (other.Method);
+
+ public override bool Equals (object? obj) => obj is MethodProxy other && Equals (other);
+
+ public override int GetHashCode () => Method.GetHashCode ();
}
}
diff --git a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs
index 8ab26982c..713868572 100644
--- a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs
+++ b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs
@@ -7,6 +7,7 @@ using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using ILLink.Shared;
+using ILLink.Shared.DataFlow;
using ILLink.Shared.TrimAnalysis;
using ILLink.Shared.TypeSystemProxy;
using Mono.Cecil;
@@ -22,7 +23,7 @@ namespace Mono.Linker.Dataflow
MessageOrigin _origin;
readonly FlowAnnotations _annotations;
readonly ReflectionMarker _reflectionMarker;
- readonly TrimAnalysisPatternStore TrimAnalysisPatterns;
+ public readonly TrimAnalysisPatternStore TrimAnalysisPatterns;
public static bool RequiresReflectionMethodBodyScannerForCallSite (LinkContext context, MethodReference calledMethod)
{
@@ -61,9 +62,18 @@ namespace Mono.Linker.Dataflow
TrimAnalysisPatterns = new TrimAnalysisPatternStore (context);
}
- public void ScanAndProcessReturnValue (MethodBody methodBody)
+ public override void InterproceduralScan (MethodBody methodBody)
{
- Scan (methodBody);
+ base.InterproceduralScan (methodBody);
+
+ var reflectionMarker = new ReflectionMarker (_context, _markStep, enabled: true);
+ TrimAnalysisPatterns.MarkAndProduceDiagnostics (reflectionMarker, _markStep);
+ }
+
+ protected override void Scan (MethodBody methodBody, ref ValueSet<MethodProxy> methodsInGroup)
+ {
+ _origin = new MessageOrigin (methodBody.Method);
+ base.Scan (methodBody, ref methodsInGroup);
if (!methodBody.Method.ReturnsVoid ()) {
var method = methodBody.Method;
@@ -71,13 +81,6 @@ namespace Mono.Linker.Dataflow
if (methodReturnValue.DynamicallyAccessedMemberTypes != 0)
HandleAssignmentPattern (_origin, ReturnValue, methodReturnValue);
}
-
- Debug.Assert (_origin.Provider == methodBody.Method);
- var reflectionMarker = new ReflectionMarker (_context, _markStep, enabled: true);
- TrimAnalysisPatterns.MarkAndProduceDiagnostics (
- !_context.Annotations.ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode (methodBody.Method),
- reflectionMarker,
- _markStep);
}
protected override void WarnAboutInvalidILInMethod (MethodBody method, int ilOffset)
@@ -258,7 +261,8 @@ namespace Mono.Linker.Dataflow
diagnosticContext.AddDiagnostic (DiagnosticId.CorrectnessOfCOMCannotBeGuaranteed, calledMethodDefinition.GetDisplayName ());
}
}
- markStep.CheckAndReportRequiresUnreferencedCode (calledMethodDefinition, diagnosticContext);
+ if (context.Annotations.DoesMethodRequireUnreferencedCode (calledMethodDefinition, out RequiresUnreferencedCodeAttribute? requiresUnreferencedCode))
+ MarkStep.ReportRequiresUnreferencedCode (calledMethodDefinition.GetDisplayName (), requiresUnreferencedCode, diagnosticContext);
return handleCallAction.Invoke (calledMethodDefinition, instanceValue, argumentValues, out methodReturnValue, out _);
}
diff --git a/src/linker/Linker.Dataflow/TrimAnalysisAssignmentPattern.cs b/src/linker/Linker.Dataflow/TrimAnalysisAssignmentPattern.cs
index ff981e99a..401330e55 100644
--- a/src/linker/Linker.Dataflow/TrimAnalysisAssignmentPattern.cs
+++ b/src/linker/Linker.Dataflow/TrimAnalysisAssignmentPattern.cs
@@ -3,7 +3,6 @@
using System;
using ILLink.Shared.TrimAnalysis;
-
using MultiValue = ILLink.Shared.DataFlow.ValueSet<ILLink.Shared.DataFlow.SingleValue>;
namespace Mono.Linker.Dataflow
@@ -21,14 +20,16 @@ namespace Mono.Linker.Dataflow
Origin = origin;
}
- public void MarkAndProduceDiagnostics (bool diagnosticsEnabled, ReflectionMarker reflectionMarker, LinkContext context)
+ public void MarkAndProduceDiagnostics (ReflectionMarker reflectionMarker, LinkContext context)
{
+ bool diagnosticsEnabled = !context.Annotations.ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode (Origin.Provider);
+ var diagnosticContext = new DiagnosticContext (Origin, diagnosticsEnabled, context);
+
foreach (var sourceValue in Source) {
foreach (var targetValue in Target) {
if (targetValue is not ValueWithDynamicallyAccessedMembers targetWithDynamicallyAccessedMembers)
throw new NotImplementedException ();
- var diagnosticContext = new DiagnosticContext (Origin, diagnosticsEnabled, context);
var requireDynamicallyAccessedMembersAction = new RequireDynamicallyAccessedMembersAction (reflectionMarker, diagnosticContext);
requireDynamicallyAccessedMembersAction.Invoke (sourceValue, targetWithDynamicallyAccessedMembers);
}
diff --git a/src/linker/Linker.Dataflow/TrimAnalysisMethodCallPattern.cs b/src/linker/Linker.Dataflow/TrimAnalysisMethodCallPattern.cs
index 7e089efa6..1f71d7e49 100644
--- a/src/linker/Linker.Dataflow/TrimAnalysisMethodCallPattern.cs
+++ b/src/linker/Linker.Dataflow/TrimAnalysisMethodCallPattern.cs
@@ -42,8 +42,9 @@ namespace Mono.Linker.Dataflow
Origin = origin;
}
- public void MarkAndProduceDiagnostics (bool diagnosticsEnabled, ReflectionMarker reflectionMarker, MarkStep markStep, LinkContext context)
+ public void MarkAndProduceDiagnostics (ReflectionMarker reflectionMarker, MarkStep markStep, LinkContext context)
{
+ bool diagnosticsEnabled = !context.Annotations.ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode (Origin.Provider);
var diagnosticContext = new DiagnosticContext (Origin, diagnosticsEnabled, context);
ReflectionMethodBodyScanner.HandleCall (Operation, CalledMethod, Instance, Arguments,
diagnosticContext,
diff --git a/src/linker/Linker.Dataflow/TrimAnalysisPatternStore.cs b/src/linker/Linker.Dataflow/TrimAnalysisPatternStore.cs
index 658c8c3fc..2e7796ef3 100644
--- a/src/linker/Linker.Dataflow/TrimAnalysisPatternStore.cs
+++ b/src/linker/Linker.Dataflow/TrimAnalysisPatternStore.cs
@@ -35,13 +35,13 @@ namespace Mono.Linker.Dataflow
MethodCallPatterns.Add (pattern.Origin, pattern);
}
- public void MarkAndProduceDiagnostics (bool enableReflectionPatternReporting, ReflectionMarker reflectionMarker, MarkStep markStep)
+ public void MarkAndProduceDiagnostics (ReflectionMarker reflectionMarker, MarkStep markStep)
{
foreach (var pattern in AssignmentPatterns.Values)
- pattern.MarkAndProduceDiagnostics (enableReflectionPatternReporting, reflectionMarker, _context);
+ pattern.MarkAndProduceDiagnostics (reflectionMarker, _context);
foreach (var pattern in MethodCallPatterns.Values)
- pattern.MarkAndProduceDiagnostics (enableReflectionPatternReporting, reflectionMarker, markStep, _context);
+ pattern.MarkAndProduceDiagnostics (reflectionMarker, markStep, _context);
}
}
} \ No newline at end of file
diff --git a/src/linker/Linker.Steps/MarkStep.cs b/src/linker/Linker.Steps/MarkStep.cs
index 73f869468..c3def477d 100644
--- a/src/linker/Linker.Steps/MarkStep.cs
+++ b/src/linker/Linker.Steps/MarkStep.cs
@@ -69,6 +69,11 @@ namespace Mono.Linker.Steps
protected List<(MethodBody, MarkScopeStack.Scope)> _unreachableBodies;
readonly List<(TypeDefinition Type, MethodBody Body, Instruction Instr)> _pending_isinst_instr;
+
+ // Stores, for compiler-generated methods only, whether they require the reflection
+ // method body scanner.
+ readonly Dictionary<MethodBody, bool> _compilerGeneratedMethodRequiresScanner;
+
UnreachableBlocksOptimizer? _unreachableBlocksOptimizer;
UnreachableBlocksOptimizer UnreachableBlocksOptimizer {
get {
@@ -228,6 +233,7 @@ namespace Mono.Linker.Steps
_unreachableBodies = new List<(MethodBody, MarkScopeStack.Scope)> ();
_pending_isinst_instr = new List<(TypeDefinition, MethodBody, Instruction)> ();
_entireTypesMarked = new HashSet<TypeDefinition> ();
+ _compilerGeneratedMethodRequiresScanner = new Dictionary<MethodBody, bool> ();
}
public AnnotationStore Annotations => Context.Annotations;
@@ -1627,6 +1633,20 @@ namespace Mono.Linker.Steps
if (reportOnMember)
origin = new MessageOrigin (member);
+ // Warn on reflection access to compiler-generated code. This includes MoveNext methods.
+ if (member is MethodDefinition method) {
+ if (ShouldWarnForReflectionAccessToCompilerGeneratedCode (method)) {
+ var id = reportOnMember ? DiagnosticId.DynamicallyAccessedMembersOnTypeReferencesCompilerGeneratedMember : DiagnosticId.DynamicallyAccessedMembersOnTypeReferencesCompilerGeneratedMemberOnBase;
+ Context.LogWarning (origin, id, type.GetDisplayName (), method.GetDisplayName ());
+ }
+
+ // All override methods should have the same annotations as their base methods
+ // (else we will produce warning IL2046 or IL2092 or some other warning).
+ // When marking override methods via DynamicallyAccessedMembers, we should only issue a warning for the base method.
+ if (method.IsVirtual && Annotations.GetBaseMethods (method) != null)
+ return;
+ }
+
if (Annotations.DoesMemberRequireUnreferencedCode (member, out RequiresUnreferencedCodeAttribute? requiresUnreferencedCodeAttribute)) {
var id = reportOnMember ? DiagnosticId.DynamicallyAccessedMembersOnTypeReferencesMemberWithRequiresUnreferencedCode : DiagnosticId.DynamicallyAccessedMembersOnTypeReferencesMemberOnBaseWithRequiresUnreferencedCode;
Context.LogWarning (origin, id, type.GetDisplayName (),
@@ -2810,8 +2830,34 @@ namespace Mono.Linker.Steps
return method;
}
+ bool ShouldWarnForReflectionAccessToCompilerGeneratedCode (MethodDefinition method)
+ {
+ if (!CompilerGeneratedState.IsNestedFunctionOrStateMachineMember (method) || method.Body == null)
+ return false;
+
+ // No need to warn if it's already covered by the Requires attribute or explicit annotations on the method.
+ if (Annotations.DoesMethodRequireUnreferencedCode (method, out _) || Annotations.FlowAnnotations.ShouldWarnWhenAccessedForReflection (method))
+ return false;
+
+ // Warn only if it has potential dataflow issues, as approximated by our check to see if it requires
+ // the reflection scanner. Checking this will also mark direct dependencies of the method body, if it
+ // hasn't been marked already. A cache ensures this only happens once for the method, whether or not
+ // it is accessed via reflection.
+ return MarkAndCheckRequiresReflectionMethodBodyScanner (method.Body);
+ }
+
void ProcessAnalysisAnnotationsForMethod (MethodDefinition method, DependencyKind dependencyKind, in MessageOrigin origin)
{
+ // There are only two ways to get there such that the origin isn't the same as the top of the scopestack.
+ // - For DAM on type, the current scope is the caller of GetType, while the origin is the type itself.
+ // - For warnings produced inside compiler-generated code, the current scope is the user code that
+ // owns the compiler-generated code, while the origin is the compiler-generated code.
+ // In either case any warnings produced here should use the origin instead of the scopestack.
+ if (origin.Provider != ScopeStack.CurrentScope.Origin.Provider) {
+ Debug.Assert (dependencyKind == DependencyKind.DynamicallyAccessedMemberOnType ||
+ (origin.Provider is MethodDefinition originMethod && CompilerGeneratedState.IsNestedFunctionOrStateMachineMember (originMethod)));
+ }
+
switch (dependencyKind) {
// DirectCall, VirtualCall and NewObj are handled by ReflectionMethodBodyScanner
// This is necessary since the ReflectionMethodBodyScanner has intrinsic handling for some
@@ -2866,35 +2912,44 @@ namespace Mono.Linker.Steps
case DependencyKind.KeptForSpecialAttribute:
return;
- case DependencyKind.DynamicallyAccessedMember:
case DependencyKind.DynamicallyAccessedMemberOnType:
- // All override methods should have the same annotations as their base methods
- // (else we will produce warning IL2046 or IL2092 or some other warning).
- // When marking override methods via DynamicallyAccessedMembers, we should only issue a warning for the base method.
- if (method.IsVirtual && Annotations.GetBaseMethods (method) != null)
- return;
- break;
+ // DynamicallyAccessedMembers on type gets special treatment so that the warning origin
+ // is the type or the annotated member.
+ ReportWarningsForTypeHierarchyReflectionAccess (method, origin);
+ return;
default:
// All other cases have the potential of us missing a warning if we don't report it
// It is possible that in some cases we may report the same warning twice, but that's better than not reporting it.
break;
- }
+ };
- if (dependencyKind == DependencyKind.DynamicallyAccessedMemberOnType) {
- // DynamicallyAccessedMembers on type gets special treatment so that the warning origin
- // is the type or the annotated member.
- ReportWarningsForTypeHierarchyReflectionAccess (method, origin);
+ if (Annotations.ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode (origin.Provider))
return;
- }
- CheckAndReportRequiresUnreferencedCode (method, new DiagnosticContext (ScopeStack.CurrentScope.Origin, diagnosticsEnabled: true, Context));
+ // Warn about reflection access to compiler-generated code.
+ // This must happen before the check for virtual methods, because it should include the
+ // virtual MoveNext method of state machines.
+ switch (dependencyKind) {
+ case DependencyKind.AccessedViaReflection:
+ case DependencyKind.DynamicallyAccessedMember:
+ if (ShouldWarnForReflectionAccessToCompilerGeneratedCode (method))
+ Context.LogWarning (origin, DiagnosticId.CompilerGeneratedMemberAccessedViaReflection, method.GetDisplayName ());
+ break;
+ }
- if (Annotations.FlowAnnotations.ShouldWarnWhenAccessedForReflection (method)) {
- // If the current scope has analysis warnings suppressed, don't generate any
- if (Annotations.ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode (ScopeStack.CurrentScope.Origin.Provider))
+ if (dependencyKind == DependencyKind.DynamicallyAccessedMember) {
+ // All override methods should have the same annotations as their base methods
+ // (else we will produce warning IL2046 or IL2092 or some other warning).
+ // When marking override methods via DynamicallyAccessedMembers, we should only issue a warning for the base method.
+ if (method.IsVirtual && Annotations.GetBaseMethods (method) != null)
return;
+ }
+
+ if (Annotations.DoesMethodRequireUnreferencedCode (method, out RequiresUnreferencedCodeAttribute? requiresUnreferencedCode))
+ ReportRequiresUnreferencedCode (method.GetDisplayName (), requiresUnreferencedCode, new DiagnosticContext (origin, diagnosticsEnabled: true, Context));
+ if (Annotations.FlowAnnotations.ShouldWarnWhenAccessedForReflection (method)) {
// ReflectionMethodBodyScanner handles more cases for data flow annotations
// so don't warn for those.
switch (dependencyKind) {
@@ -2906,24 +2961,11 @@ namespace Mono.Linker.Steps
break;
}
- Context.LogWarning (ScopeStack.CurrentScope.Origin, DiagnosticId.DynamicallyAccessedMembersMethodAccessedViaReflection, method.GetDisplayName ());
+ Context.LogWarning (origin, DiagnosticId.DynamicallyAccessedMembersMethodAccessedViaReflection, method.GetDisplayName ());
}
}
- internal void CheckAndReportRequiresUnreferencedCode (MethodDefinition method, in DiagnosticContext diagnosticContext)
- {
- // If the caller of a method is already marked with `RequiresUnreferencedCodeAttribute` a new warning should not
- // be produced for the callee.
- if (Annotations.ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode (diagnosticContext.Origin.Provider))
- return;
-
- if (!Annotations.DoesMethodRequireUnreferencedCode (method, out RequiresUnreferencedCodeAttribute? requiresUnreferencedCode))
- return;
-
- ReportRequiresUnreferencedCode (method.GetDisplayName (), requiresUnreferencedCode, diagnosticContext);
- }
-
- private static void ReportRequiresUnreferencedCode (string displayName, RequiresUnreferencedCodeAttribute requiresUnreferencedCode, in DiagnosticContext diagnosticContext)
+ internal static void ReportRequiresUnreferencedCode (string displayName, RequiresUnreferencedCodeAttribute requiresUnreferencedCode, in DiagnosticContext diagnosticContext)
{
string arg1 = MessageFormat.FormatRequiresAttributeMessageArg (requiresUnreferencedCode.Message);
string arg2 = MessageFormat.FormatRequiresAttributeUrlArg (requiresUnreferencedCode.Url);
@@ -3329,6 +3371,24 @@ namespace Mono.Linker.Steps
return;
}
+ bool requiresReflectionMethodBodyScanner = MarkAndCheckRequiresReflectionMethodBodyScanner (body);
+
+ // Data-flow (reflection scanning) for compiler-generated methods will happen as part of the
+ // data-flow scan of the user-defined method which uses this compiler-generated method.
+ if (CompilerGeneratedState.IsNestedFunctionOrStateMachineMember (body.Method))
+ return;
+
+ MarkReflectionLikeDependencies (body, requiresReflectionMethodBodyScanner);
+ }
+
+ bool MarkAndCheckRequiresReflectionMethodBodyScanner (MethodBody body)
+ {
+ // This may get called multiple times for compiler-generated code: once for
+ // reflection access, and once as part of the interprocedural scan of the user method.
+ // This check ensures that we only do the work once.
+ if (_compilerGeneratedMethodRequiresScanner.TryGetValue (body, out bool requiresReflectionMethodBodyScanner))
+ return requiresReflectionMethodBodyScanner;
+
foreach (VariableDefinition var in body.Variables)
MarkType (var.VariableType, new DependencyInfo (DependencyKind.VariableType, body.Method));
@@ -3336,16 +3396,20 @@ namespace Mono.Linker.Steps
if (eh.HandlerType == ExceptionHandlerType.Catch)
MarkType (eh.CatchType, new DependencyInfo (DependencyKind.CatchType, body.Method));
- bool requiresReflectionMethodBodyScanner =
+ requiresReflectionMethodBodyScanner =
ReflectionMethodBodyScanner.RequiresReflectionMethodBodyScannerForMethodBody (Context, body.Method);
+ using var _ = ScopeStack.PushScope (new MessageOrigin (body.Method));
foreach (Instruction instruction in body.Instructions)
MarkInstruction (instruction, body.Method, ref requiresReflectionMethodBodyScanner);
MarkInterfacesNeededByBodyStack (body);
- MarkReflectionLikeDependencies (body, requiresReflectionMethodBodyScanner);
+ if (CompilerGeneratedState.IsNestedFunctionOrStateMachineMember (body.Method))
+ _compilerGeneratedMethodRequiresScanner.Add (body, requiresReflectionMethodBodyScanner);
PostMarkMethodBody (body);
+
+ return requiresReflectionMethodBodyScanner;
}
bool IsUnreachableBody (MethodBody body)
@@ -3510,10 +3574,36 @@ namespace Mono.Linker.Steps
//
protected virtual void MarkReflectionLikeDependencies (MethodBody body, bool requiresReflectionMethodBodyScanner)
{
- if (requiresReflectionMethodBodyScanner) {
- var scanner = new ReflectionMethodBodyScanner (Context, this, ScopeStack.CurrentScope.Origin);
- scanner.ScanAndProcessReturnValue (body);
+ // requiresReflectionMethodBodyScanner tells us whether the method body itself requires a dataflow scan.
+
+ // If the method body owns any compiler-generated code, we might still need to do a scan of it together with
+ // all of the compiler-generated code it owns, so first check any compiler-generated callees.
+ if (Context.CompilerGeneratedState.TryGetCompilerGeneratedCalleesForUserMethod (body.Method, out List<IMemberDefinition>? compilerGeneratedCallees)) {
+ foreach (var compilerGeneratedCallee in compilerGeneratedCallees) {
+ switch (compilerGeneratedCallee) {
+ case MethodDefinition nestedFunction:
+ if (nestedFunction.Body is MethodBody nestedBody)
+ requiresReflectionMethodBodyScanner |= MarkAndCheckRequiresReflectionMethodBodyScanner (nestedBody);
+ break;
+ case TypeDefinition stateMachineType:
+ foreach (var method in stateMachineType.Methods) {
+ if (method.Body is MethodBody stateMachineBody)
+ requiresReflectionMethodBodyScanner |= MarkAndCheckRequiresReflectionMethodBodyScanner (stateMachineBody);
+ }
+ break;
+ default:
+ throw new InvalidOperationException ();
+ }
+ }
}
+
+ if (!requiresReflectionMethodBodyScanner)
+ return;
+
+ Debug.Assert (ScopeStack.CurrentScope.Origin.Provider == body.Method);
+ var scanner = new ReflectionMethodBodyScanner (Context, this, ScopeStack.CurrentScope.Origin);
+ scanner.InterproceduralScan (body);
+
}
protected class AttributeProviderPair
diff --git a/src/linker/Linker/CompilerGeneratedState.cs b/src/linker/Linker/CompilerGeneratedState.cs
index d67d8133e..f6293caf4 100644
--- a/src/linker/Linker/CompilerGeneratedState.cs
+++ b/src/linker/Linker/CompilerGeneratedState.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+using System.Linq;
using ILLink.Shared;
using Mono.Cecil;
using Mono.Cecil.Cil;
@@ -25,7 +26,10 @@ namespace Mono.Linker
readonly Dictionary<MethodDefinition, MethodDefinition> _compilerGeneratedMethodToUserCodeMethod;
- readonly HashSet<TypeDefinition> _typesWithPopulatedCache;
+ // For each type that has had its cache populated, stores a map of methods which have corresponding
+ // compiler-generated members (either methods or state machine types) to those compiler-generated members,
+ // or null if the type has no methods with compiler-generated members.
+ readonly Dictionary<TypeDefinition, Dictionary<MethodDefinition, List<IMemberDefinition>>?> _cachedTypeToCompilerGeneratedMembers;
public CompilerGeneratedState (LinkContext context)
{
@@ -33,7 +37,7 @@ namespace Mono.Linker
_compilerGeneratedTypeToUserCodeMethod = new Dictionary<TypeDefinition, MethodDefinition> ();
_generatedTypeToTypeArgumentInfo = new Dictionary<TypeDefinition, TypeArgumentInfo> ();
_compilerGeneratedMethodToUserCodeMethod = new Dictionary<MethodDefinition, MethodDefinition> ();
- _typesWithPopulatedCache = new HashSet<TypeDefinition> ();
+ _cachedTypeToCompilerGeneratedMembers = new Dictionary<TypeDefinition, Dictionary<MethodDefinition, List<IMemberDefinition>>?> ();
}
static IEnumerable<TypeDefinition> GetCompilerGeneratedNestedTypes (TypeDefinition type)
@@ -49,6 +53,40 @@ namespace Mono.Linker
}
}
+ // "Nested function" refers to lambdas and local functions.
+ public static bool IsNestedFunctionOrStateMachineMember (IMemberDefinition member)
+ {
+ if (member is MethodDefinition method && CompilerGeneratedNames.IsLambdaOrLocalFunction (method.Name))
+ return true;
+
+ if (member.DeclaringType is not TypeDefinition declaringType)
+ return false;
+
+ return CompilerGeneratedNames.IsStateMachineType (declaringType.Name);
+ }
+
+ public static bool TryGetStateMachineType (MethodDefinition method, [NotNullWhen (true)] out TypeDefinition? stateMachineType)
+ {
+ stateMachineType = null;
+ // Discover state machine methods.
+ if (!method.HasCustomAttributes)
+ return false;
+
+ foreach (var attribute in method.CustomAttributes) {
+ if (attribute.AttributeType.Namespace != "System.Runtime.CompilerServices")
+ continue;
+
+ switch (attribute.AttributeType.Name) {
+ case "AsyncIteratorStateMachineAttribute":
+ case "AsyncStateMachineAttribute":
+ case "IteratorStateMachineAttribute":
+ stateMachineType = GetFirstConstructorArgumentAsType (attribute);
+ return stateMachineType != null;
+ }
+ }
+ return false;
+ }
+
/// <summary>
/// Walks the type and its descendents to find Roslyn-compiler generated
/// code and gather information to map it back to original user code. If
@@ -71,11 +109,11 @@ namespace Mono.Linker
return null;
// Avoid repeat scans of the same type
- if (!_typesWithPopulatedCache.Add (type))
+ if (_cachedTypeToCompilerGeneratedMembers.ContainsKey (type))
return type;
var callGraph = new CompilerGeneratedCallGraph ();
- var callingMethods = new HashSet<MethodDefinition> ();
+ var userDefinedMethods = new HashSet<MethodDefinition> ();
void ProcessMethod (MethodDefinition method)
{
@@ -83,7 +121,7 @@ namespace Mono.Linker
if (!CompilerGeneratedNames.IsLambdaOrLocalFunction (method.Name)) {
if (!isStateMachineMember) {
// If it's not a nested function, track as an entry point to the call graph.
- var added = callingMethods.Add (method);
+ var added = userDefinedMethods.Add (method);
Debug.Assert (added);
}
} else {
@@ -127,34 +165,19 @@ namespace Mono.Linker
}
}
- // Discover state machine methods.
- if (!method.HasCustomAttributes)
- return;
+ if (TryGetStateMachineType (method, out TypeDefinition? stateMachineType)) {
+ Debug.Assert (stateMachineType.DeclaringType == type ||
+ (CompilerGeneratedNames.IsGeneratedMemberName (stateMachineType.DeclaringType.Name) &&
+ stateMachineType.DeclaringType.DeclaringType == type));
+ callGraph.TrackCall (method, stateMachineType);
- foreach (var attribute in method.CustomAttributes) {
- if (attribute.AttributeType.Namespace != "System.Runtime.CompilerServices")
- continue;
-
- switch (attribute.AttributeType.Name) {
- case "AsyncIteratorStateMachineAttribute":
- case "AsyncStateMachineAttribute":
- case "IteratorStateMachineAttribute":
- TypeDefinition? stateMachineType = GetFirstConstructorArgumentAsType (attribute);
- if (stateMachineType == null)
- break;
- Debug.Assert (stateMachineType.DeclaringType == type ||
- (CompilerGeneratedNames.IsGeneratedMemberName (stateMachineType.DeclaringType.Name) &&
- stateMachineType.DeclaringType.DeclaringType == type));
- callGraph.TrackCall (method, stateMachineType);
- if (!_compilerGeneratedTypeToUserCodeMethod.TryAdd (stateMachineType, method)) {
- var alreadyAssociatedMethod = _compilerGeneratedTypeToUserCodeMethod[stateMachineType];
- _context.LogWarning (new MessageOrigin (method), DiagnosticId.MethodsAreAssociatedWithStateMachine, method.GetDisplayName (), alreadyAssociatedMethod.GetDisplayName (), stateMachineType.GetDisplayName ());
- }
- // Already warned above if multiple methods map to the same type
- // Fill in null for argument providers now, the real providers will be filled in later
- _ = _generatedTypeToTypeArgumentInfo.TryAdd (stateMachineType, new TypeArgumentInfo (method, null));
- break;
+ if (!_compilerGeneratedTypeToUserCodeMethod.TryAdd (stateMachineType, method)) {
+ var alreadyAssociatedMethod = _compilerGeneratedTypeToUserCodeMethod[stateMachineType];
+ _context.LogWarning (new MessageOrigin (method), DiagnosticId.MethodsAreAssociatedWithStateMachine, method.GetDisplayName (), alreadyAssociatedMethod.GetDisplayName (), stateMachineType.GetDisplayName ());
}
+ // Already warned above if multiple methods map to the same type
+ // Fill in null for argument providers now, the real providers will be filled in later
+ _ = _generatedTypeToTypeArgumentInfo.TryAdd (stateMachineType, new TypeArgumentInfo (method, null));
}
}
@@ -184,8 +207,17 @@ namespace Mono.Linker
// Note: this only discovers nested functions which are referenced from the user
// code or its referenced nested functions. There is no reliable way to determine from
// IL which user code an unused nested function belongs to.
- foreach (var userDefinedMethod in callingMethods) {
- foreach (var compilerGeneratedMember in callGraph.GetReachableMembers (userDefinedMethod)) {
+
+ Dictionary<MethodDefinition, List<IMemberDefinition>>? compilerGeneratedCallees = null;
+ foreach (var userDefinedMethod in userDefinedMethods) {
+ var callees = callGraph.GetReachableMembers (userDefinedMethod);
+ if (!callees.Any ())
+ continue;
+
+ compilerGeneratedCallees ??= new Dictionary<MethodDefinition, List<IMemberDefinition>> ();
+ compilerGeneratedCallees.Add (userDefinedMethod, new List<IMemberDefinition> (callees));
+
+ foreach (var compilerGeneratedMember in callees) {
switch (compilerGeneratedMember) {
case MethodDefinition nestedFunction:
Debug.Assert (CompilerGeneratedNames.IsLambdaOrLocalFunction (nestedFunction.Name));
@@ -216,6 +248,7 @@ namespace Mono.Linker
MapGeneratedTypeTypeParameters (generatedType);
}
+ _cachedTypeToCompilerGeneratedMembers.Add (type, compilerGeneratedCallees);
return type;
/// <summary>
@@ -305,6 +338,19 @@ namespace Mono.Linker
return attribute.ConstructorArguments[0].Value as TypeDefinition;
}
+ public bool TryGetCompilerGeneratedCalleesForUserMethod (MethodDefinition method, [NotNullWhen (true)] out List<IMemberDefinition>? callees)
+ {
+ callees = null;
+ if (IsNestedFunctionOrStateMachineMember (method))
+ return false;
+
+ var typeToCache = PopulateCacheForType (method.DeclaringType);
+ if (typeToCache is null)
+ return false;
+
+ return _cachedTypeToCompilerGeneratedMembers[typeToCache]?.TryGetValue (method, out callees) == true;
+ }
+
/// <summary>
/// Gets the attributes on the "original" method of a generated type, i.e. the
/// attributes on the corresponding type parameters from the owning method.
@@ -343,7 +389,7 @@ namespace Mono.Linker
if (_compilerGeneratedTypeToUserCodeMethod.TryGetValue (sourceType, out owningMethod))
return true;
- if (!CompilerGeneratedNames.IsGeneratedMemberName (sourceMember.Name) && !CompilerGeneratedNames.IsGeneratedMemberName (sourceType.Name))
+ if (!IsNestedFunctionOrStateMachineMember (sourceMember))
return false;
// sourceType is a state machine type, or the type containing a lambda or local function.
diff --git a/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs b/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs
index b92038a6a..5e7ccb3dd 100644
--- a/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs
+++ b/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs
@@ -71,6 +71,12 @@ namespace ILLink.RoslynAnalyzer.Tests
}
[Fact]
+ public Task CompilerGeneratedCodeAccessedViaReflection ()
+ {
+ return RunTest ();
+ }
+
+ [Fact]
public Task DynamicDependencyDataflow ()
{
return RunTest (nameof (DynamicDependencyDataflow));
diff --git a/test/ILLink.RoslynAnalyzer.Tests/RequiresCapabilityTests.cs b/test/ILLink.RoslynAnalyzer.Tests/RequiresCapabilityTests.cs
index 52873a9d0..ad36b4884 100644
--- a/test/ILLink.RoslynAnalyzer.Tests/RequiresCapabilityTests.cs
+++ b/test/ILLink.RoslynAnalyzer.Tests/RequiresCapabilityTests.cs
@@ -6,7 +6,7 @@ using Xunit;
namespace ILLink.RoslynAnalyzer.Tests
{
- public sealed class RequiresCapabilityTests : LinkerTestBase
+ public sealed partial class RequiresCapabilityTests : LinkerTestBase
{
protected override string TestSuiteName => "RequiresCapability";
@@ -17,6 +17,12 @@ namespace ILLink.RoslynAnalyzer.Tests
}
[Fact]
+ public Task ReflectionAccessFromCompilerGeneratedCode ()
+ {
+ return RunTest ();
+ }
+
+ [Fact]
public Task RequiresAccessedThrough ()
{
return RunTest (nameof (RequiresAccessedThrough));
diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/CompilerGeneratedCodeAccessedViaReflection.cs b/test/Mono.Linker.Tests.Cases/DataFlow/CompilerGeneratedCodeAccessedViaReflection.cs
new file mode 100644
index 000000000..1b5f65c7e
--- /dev/null
+++ b/test/Mono.Linker.Tests.Cases/DataFlow/CompilerGeneratedCodeAccessedViaReflection.cs
@@ -0,0 +1,523 @@
+// 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.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using Mono.Linker.Tests.Cases.DataFlow;
+using Mono.Linker.Tests.Cases.Expectations.Assertions;
+using Mono.Linker.Tests.Cases.Expectations.Helpers;
+
+namespace Mono.Linker.Tests.Cases.DataFlow
+{
+ [SkipKeptItemsValidation]
+ [ExpectedNoWarnings]
+ class CompilerGeneratedCodeAccessedViaReflection
+ {
+ public static void Main ()
+ {
+ IteratorStateMachines.Test ();
+ AsyncStateMachines.Test ();
+ AsyncIteratorStateMachines.Test ();
+ Lambdas.Test ();
+ LocalFunctions.Test ();
+ }
+
+ class BaseTypeWithIteratorStateMachines
+ {
+ // Annotations aren't propagated to hoisted locals: https://github.com/dotnet/linker/issues/2001
+ [ExpectedWarning ("IL2077", nameof (DataFlowTypeExtensions.RequiresAll), CompilerGeneratedCode = true)]
+ public static IEnumerable<int> BaseIteratorWithCorrectDataflow ()
+ {
+ var t = GetAll ();
+ yield return 0;
+ t.RequiresAll ();
+ }
+ }
+
+ [ExpectedWarning ("IL2120", "<" + nameof (BaseIteratorWithCorrectDataflow) + ">", "MoveNext",
+ ProducedBy = ProducedBy.Trimmer)]
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)]
+ class IteratorStateMachines : BaseTypeWithIteratorStateMachines
+ {
+ public static IEnumerable<int> IteratorWithoutDataflow ()
+ {
+ yield return 0;
+ }
+
+ [ExpectedWarning ("IL2026", "--MethodWithRequires--", CompilerGeneratedCode = true)]
+ [ExpectedWarning ("IL3002", "--MethodWithRequires--",
+ ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3050", "--MethodWithRequires--",
+ ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL2119", "<" + nameof (IteratorCallsMethodWithRequires) + ">", "MoveNext", CompilerGeneratedCode = true)]
+ public static IEnumerable<int> IteratorCallsMethodWithRequires ()
+ {
+ yield return 0;
+ MethodWithRequires ();
+ }
+
+ [ExpectedWarning ("IL2119", "<" + nameof (IteratorWithCorrectDataflow) + ">", "MoveNext", CompilerGeneratedCode = true)]
+ // Annotations aren't propagated to hoisted locals: https://github.com/dotnet/linker/issues/2001
+ [ExpectedWarning ("IL2077", nameof (DataFlowTypeExtensions.RequiresAll), CompilerGeneratedCode = true,
+ ProducedBy = ProducedBy.Trimmer)]
+ public static IEnumerable<int> IteratorWithCorrectDataflow ()
+ {
+ var t = GetAll ();
+ yield return 0;
+ t.RequiresAll ();
+ }
+
+ [ExpectedWarning ("IL2119", "<" + nameof (IteratorWithProblematicDataflow) + ">", "MoveNext", CompilerGeneratedCode = true)]
+ // Annotations aren't propagated to hoisted locals: https://github.com/dotnet/linker/issues/2001
+ [ExpectedWarning ("IL2077", nameof (DataFlowTypeExtensions.RequiresAll), CompilerGeneratedCode = true,
+ ProducedBy = ProducedBy.Trimmer)]
+ public static IEnumerable<int> IteratorWithProblematicDataflow ()
+ {
+ var t = GetWithPublicMethods ();
+ yield return 0;
+ t.RequiresAll ();
+ }
+
+ [ExpectedWarning ("IL2112", nameof (RUCTypeWithIterators) + "()", "--RUCTypeWithIterators--", CompilerGeneratedCode = true)]
+ [RequiresUnreferencedCode ("--RUCTypeWithIterators--")]
+ class RUCTypeWithIterators
+ {
+ [ExpectedWarning ("IL2112", nameof (StaticIteratorCallsMethodWithRequires), "--RUCTypeWithIterators--",
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2112", "<" + nameof (StaticIteratorCallsMethodWithRequires) + ">", "--RUCTypeWithIterators--", CompilerGeneratedCode = true,
+ ProducedBy = ProducedBy.Trimmer)] // state machine ctor
+ [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ public static IEnumerable<int> StaticIteratorCallsMethodWithRequires ()
+ {
+ yield return 0;
+ MethodWithRequires ();
+ }
+
+ // BUG: this should also give IL2112 for the InstanceIteratorCallsMethodWithRequires state machine constructor.
+ // https://github.com/dotnet/linker/issues/2806
+ // [ExpectedWarning ("IL2026", "<" + nameof (RUCTypeWithIterators.InstanceIteratorCallsMethodWithRequires) + ">")]
+ // With that, the IL2119 warning should also go away.
+ [ExpectedWarning ("IL2119", "<" + nameof (InstanceIteratorCallsMethodWithRequires) + ">", "MoveNext", CompilerGeneratedCode = true,
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ public IEnumerable<int> InstanceIteratorCallsMethodWithRequires ()
+ {
+ yield return 0;
+ MethodWithRequires ();
+ }
+ }
+
+ [ExpectedWarning ("IL2118", "<" + nameof (IteratorWithProblematicDataflow) + ">", "MoveNext",
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2118", "<" + nameof (IteratorCallsMethodWithRequires) + ">", "MoveNext",
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2118", "<" + nameof (IteratorWithCorrectDataflow) + ">", "MoveNext",
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2118", "<" + nameof (BaseIteratorWithCorrectDataflow) + ">", "MoveNext",
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2026", nameof (RUCTypeWithIterators) + "()", "--RUCTypeWithIterators--")]
+ // Expect to see warnings about RUC on type, for all static state machine members.
+ [ExpectedWarning ("IL2026", nameof (RUCTypeWithIterators.StaticIteratorCallsMethodWithRequires) + "()", "--RUCTypeWithIterators--")]
+ [ExpectedWarning ("IL2026", "<" + nameof (RUCTypeWithIterators.StaticIteratorCallsMethodWithRequires) + ">",
+ ProducedBy = ProducedBy.Trimmer)]
+ // BUG: this should also give IL2026 for the InstanceIteratorCallsMethodWithRequires state machine constructor.
+ // https://github.com/dotnet/linker/issues/2806
+ // [ExpectedWarning ("IL2026", "<" + nameof (RUCTypeWithIterators.InstanceIteratorCallsMethodWithRequires) + ">")]
+ // With that, the IL2118 warning should also go away.
+ [ExpectedWarning ("IL2118", "<" + nameof (RUCTypeWithIterators.InstanceIteratorCallsMethodWithRequires) + ">", "MoveNext",
+ ProducedBy = ProducedBy.Trimmer)]
+ public static void Test (IteratorStateMachines test = null)
+ {
+ typeof (IteratorStateMachines).RequiresAll ();
+
+ test.GetType ().RequiresAll ();
+ }
+ }
+
+ class AsyncStateMachines
+ {
+ public static async Task AsyncWithoutDataflow ()
+ {
+ }
+
+ [ExpectedWarning ("IL2026", "--MethodWithRequires--", CompilerGeneratedCode = true)]
+ [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ public static async Task AsyncCallsMethodWithRequires ()
+ {
+ MethodWithRequires ();
+ }
+
+ // Annotations aren't propagated to hoisted locals: https://github.com/dotnet/linker/issues/2001
+ [ExpectedWarning ("IL2077", nameof (DataFlowTypeExtensions.RequiresAll), CompilerGeneratedCode = true,
+ ProducedBy = ProducedBy.Trimmer)]
+ public static async Task AsyncWithCorrectDataflow ()
+ {
+ var t = GetAll ();
+ t.RequiresAll ();
+ }
+
+ // Annotations aren't propagated to hoisted locals: https://github.com/dotnet/linker/issues/2001
+ [ExpectedWarning ("IL2077", nameof (DataFlowTypeExtensions.RequiresAll), CompilerGeneratedCode = true,
+ ProducedBy = ProducedBy.Trimmer)]
+ public static async Task AsyncWithProblematicDataflow ()
+ {
+ var t = GetWithPublicMethods ();
+ t.RequiresAll ();
+ }
+
+ [ExpectedWarning ("IL2118", "<" + nameof (AsyncWithProblematicDataflow) + ">", "MoveNext",
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2118", "<" + nameof (AsyncCallsMethodWithRequires) + ">", "MoveNext",
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2118", "<" + nameof (AsyncWithCorrectDataflow) + ">", "MoveNext",
+ ProducedBy = ProducedBy.Trimmer)]
+ public static void Test ()
+ {
+ typeof (AsyncStateMachines).RequiresAll ();
+ }
+ }
+
+ class AsyncIteratorStateMachines
+ {
+ public static async IAsyncEnumerable<int> AsyncIteratorWithoutDataflow ()
+ {
+ yield return await MethodAsync ();
+ }
+
+ [ExpectedWarning ("IL2026", "--MethodWithRequires--", CompilerGeneratedCode = true)]
+ [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ public static async IAsyncEnumerable<int> AsyncIteratorCallsMethodWithRequires ()
+ {
+ yield return await MethodAsync ();
+ MethodWithRequires ();
+ }
+
+ // Annotations aren't propagated to hoisted locals: https://github.com/dotnet/linker/issues/2001
+ [ExpectedWarning ("IL2077", nameof (DataFlowTypeExtensions.RequiresAll), CompilerGeneratedCode = true,
+ ProducedBy = ProducedBy.Trimmer)]
+ public static async IAsyncEnumerable<int> AsyncIteratorWithCorrectDataflow ()
+ {
+ var t = GetAll ();
+ yield return await MethodAsync ();
+ t.RequiresAll ();
+ }
+
+ // Annotations aren't propagated to hoisted locals: https://github.com/dotnet/linker/issues/2001
+ [ExpectedWarning ("IL2077", nameof (DataFlowTypeExtensions.RequiresAll),
+ nameof (AsyncIteratorWithProblematicDataflow), CompilerGeneratedCode = true,
+ ProducedBy = ProducedBy.Trimmer)]
+ public static async IAsyncEnumerable<int> AsyncIteratorWithProblematicDataflow ()
+ {
+ var t = GetWithPublicMethods ();
+ yield return await MethodAsync ();
+ t.RequiresAll ();
+ }
+
+ [ExpectedWarning ("IL2118", "<" + nameof (AsyncIteratorWithProblematicDataflow) + ">", "MoveNext",
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2118", "<" + nameof (AsyncIteratorCallsMethodWithRequires) + ">", "MoveNext",
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2118", "<" + nameof (AsyncIteratorWithCorrectDataflow) + ">", "MoveNext",
+ ProducedBy = ProducedBy.Trimmer)]
+ public static void Test ()
+ {
+ typeof (AsyncIteratorStateMachines).RequiresAll ();
+ }
+ }
+
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)]
+ class Lambdas
+ {
+ static void LambdaWithoutDataflow ()
+ {
+ var lambda = () => 0;
+ lambda ();
+ }
+
+ static void LambdaCallsMethodWithRequires ()
+ {
+ var lambda =
+ [ExpectedWarning ("IL2026", "--MethodWithRequires--")]
+ [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL2119", "<" + nameof (LambdaCallsMethodWithRequires) + ">",
+ ProducedBy = ProducedBy.Trimmer)]
+ () => MethodWithRequires ();
+ lambda ();
+ }
+
+ static void LambdaWithCorrectDataflow ()
+ {
+ var lambda =
+ // Annotations aren't propagated to hoisted locals: https://github.com/dotnet/linker/issues/2001
+ [ExpectedWarning ("IL2119", "<" + nameof (LambdaWithCorrectDataflow) + ">",
+ ProducedBy = ProducedBy.Trimmer)]
+ () => {
+ var t = GetAll ();
+ t.RequiresAll ();
+ };
+ lambda ();
+ }
+
+ [ExpectedWarning ("IL2111", "<" + nameof (LambdaWithCorrectParameter) + ">",
+ ProducedBy = ProducedBy.Trimmer)]
+ static void LambdaWithCorrectParameter ()
+ {
+ var lambda =
+ [ExpectedWarning ("IL2114", "<" + nameof (LambdaWithCorrectParameter) + ">",
+ ProducedBy = ProducedBy.Trimmer)]
+ ([DynamicallyAccessedMembersAttribute (DynamicallyAccessedMemberTypes.All)] Type t) => {
+ t.RequiresAll ();
+ };
+ lambda (null);
+ }
+
+ static void LambdaWithProblematicDataflow ()
+ {
+ var lambda =
+ [ExpectedWarning ("IL2119", "<" + nameof (LambdaWithProblematicDataflow) + ">",
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions.RequiresAll),
+ ProducedBy = ProducedBy.Trimmer)]
+ () => {
+ var t = GetWithPublicMethods ();
+ t.RequiresAll ();
+ };
+ lambda ();
+ }
+
+ static void LambdaWithCapturedTypeToDAM ()
+ {
+ var t = GetWithPublicMethods ();
+ var lambda =
+ [ExpectedWarning ("IL2119", "<" + nameof (LambdaWithCapturedTypeToDAM) + ">",
+ ProducedBy = ProducedBy.Trimmer)]
+ // Annotations aren't propagated to hoisted locals: https://github.com/dotnet/linker/issues/2001
+ [ExpectedWarning ("IL2077", nameof (DataFlowTypeExtensions.RequiresAll),
+ ProducedBy = ProducedBy.Trimmer)]
+ () => {
+ t.RequiresAll ();
+ };
+ lambda ();
+ }
+
+ static void LambdaWithCapturedInt ()
+ {
+ int i = 0;
+ var lambda =
+ () => i;
+ i++;
+ lambda ();
+ }
+
+ [ExpectedWarning ("IL2112", nameof (RUCTypeWithLambdas) + "()", "--RUCTypeWithLambdas--", CompilerGeneratedCode = true)]
+ [RequiresUnreferencedCode ("--RUCTypeWithLambdas--")]
+ class RUCTypeWithLambdas
+ {
+ public void MethodWithLambdas ()
+ {
+ var lambda =
+ [ExpectedWarning ("IL2119", "<" + nameof (MethodWithLambdas) + ">",
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ () => MethodWithRequires ();
+
+ int i = 0;
+ var lambdaWithCapturedState =
+ [ExpectedWarning ("IL2119", "<" + nameof (MethodWithLambdas) + ">",
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ () => {
+ i++;
+ MethodWithRequires ();
+ };
+
+ lambda ();
+ lambdaWithCapturedState ();
+ }
+ }
+
+ [ExpectedWarning ("IL2118", "<" + nameof (LambdaCallsMethodWithRequires) + ">",
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2118", "<" + nameof (LambdaWithCorrectDataflow) + ">",
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2111", "<" + nameof (LambdaWithCorrectParameter) + ">",
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2118", "<" + nameof (LambdaWithProblematicDataflow) + ">",
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2118", "<" + nameof (LambdaWithCapturedTypeToDAM) + ">",
+ ProducedBy = ProducedBy.Trimmer)]
+ // Expect RUC warnings for static, compiler-generated code warnings for instance.
+ [ExpectedWarning ("IL2026", nameof (RUCTypeWithLambdas) + "()", "--RUCTypeWithLambdas--")]
+ [ExpectedWarning ("IL2118", "<" + nameof (RUCTypeWithLambdas.MethodWithLambdas) + ">",
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2118", "<" + nameof (RUCTypeWithLambdas.MethodWithLambdas) + ">",
+ ProducedBy = ProducedBy.Trimmer)]
+ public static void Test (Lambdas test = null)
+ {
+ typeof (Lambdas).RequiresAll ();
+
+ test.GetType ().RequiresAll ();
+ }
+ }
+
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)]
+ class LocalFunctions
+ {
+ static void LocalFunctionWithoutDataflow ()
+ {
+ int LocalFunction () => 0;
+ LocalFunction ();
+ }
+
+ static void LocalFunctionCallsMethodWithRequires ()
+ {
+ [ExpectedWarning ("IL2026", "--MethodWithRequires--")]
+ [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL2119", "<" + nameof (LocalFunctionCallsMethodWithRequires) + ">",
+ ProducedBy = ProducedBy.Trimmer)]
+ void LocalFunction () => MethodWithRequires ();
+ LocalFunction ();
+ }
+
+ static void LocalFunctionWithCorrectDataflow ()
+ {
+ [ExpectedWarning ("IL2119", "<" + nameof (LocalFunctionWithCorrectDataflow) + ">",
+ ProducedBy = ProducedBy.Trimmer)]
+ void LocalFunction ()
+ {
+ var t = GetAll ();
+ t.RequiresAll ();
+ };
+ LocalFunction ();
+ }
+
+ static void LocalFunctionWithProblematicDataflow ()
+ {
+ [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions.RequiresAll),
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2119", "<" + nameof (LocalFunctionWithProblematicDataflow) + ">",
+ ProducedBy = ProducedBy.Trimmer)]
+ void LocalFunction ()
+ {
+ var t = GetWithPublicMethods ();
+ t.RequiresAll ();
+ };
+ LocalFunction ();
+ }
+
+ static void LocalFunctionWithCapturedTypeToDAM ()
+ {
+ var t = GetAll ();
+ [ExpectedWarning ("IL2119", "<" + nameof (LocalFunctionWithCapturedTypeToDAM) + ">",
+ ProducedBy = ProducedBy.Trimmer)]
+ // Annotations aren't propagated to hoisted locals: https://github.com/dotnet/linker/issues/2001
+ [ExpectedWarning ("IL2077", nameof (DataFlowTypeExtensions.RequiresAll),
+ ProducedBy = ProducedBy.Trimmer)]
+ void LocalFunction ()
+ {
+ t.RequiresAll ();
+ };
+ LocalFunction ();
+ }
+
+ static void LocalFunctionWithCapturedInt ()
+ {
+ int i = 0;
+ int LocalFunction () => i;
+ i++;
+ LocalFunction ();
+ }
+
+ [ExpectedWarning ("IL2112", nameof (RUCTypeWithLocalFunctions) + "()", CompilerGeneratedCode = true)]
+ [RequiresUnreferencedCode ("--RUCTypeWithLocalFunctions--")]
+ class RUCTypeWithLocalFunctions
+ {
+ public void MethodWithLocalFunctions ()
+ {
+ [ExpectedWarning ("IL2112", "<" + nameof (MethodWithLocalFunctions) + ">",
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ void LocalFunction () => MethodWithRequires ();
+
+ [ExpectedWarning ("IL2112", "<" + nameof (MethodWithLocalFunctions) + ">",
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ static void StaticLocalFunction () => MethodWithRequires ();
+
+ int i = 0;
+ [ExpectedWarning ("IL2112", "<" + nameof (MethodWithLocalFunctions) + ">",
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ void LocalFunctionWithCapturedState ()
+ {
+ i++;
+ MethodWithRequires ();
+ }
+
+ LocalFunction ();
+ StaticLocalFunction ();
+ LocalFunctionWithCapturedState ();
+ }
+ }
+
+ [ExpectedWarning ("IL2118", nameof (LocalFunctionCallsMethodWithRequires),
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2118", nameof (LocalFunctionWithCorrectDataflow),
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2118", nameof (LocalFunctionWithProblematicDataflow),
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2118", nameof (LocalFunctionWithCapturedTypeToDAM),
+ ProducedBy = ProducedBy.Trimmer)]
+ // Expect RUC warnings for static, compiler-generated code warnings for instance.
+ [ExpectedWarning ("IL2026", nameof (RUCTypeWithLocalFunctions) + "()", "--RUCTypeWithLocalFunctions--")]
+ [ExpectedWarning ("IL2026", "<" + nameof (RUCTypeWithLocalFunctions.MethodWithLocalFunctions) + ">", "LocalFunctionWithCapturedState",
+ ProducedBy = ProducedBy.Trimmer)] // displayclass ctor
+ [ExpectedWarning ("IL2026", "<" + nameof (RUCTypeWithLocalFunctions.MethodWithLocalFunctions) + ">", "StaticLocalFunction",
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2026", "<" + nameof (RUCTypeWithLocalFunctions.MethodWithLocalFunctions) + ">", "LocalFunction",
+ ProducedBy = ProducedBy.Trimmer)]
+ public static void Test (LocalFunctions test = null)
+ {
+ typeof (LocalFunctions).RequiresAll ();
+
+ test.GetType ().RequiresAll ();
+ }
+ }
+
+ [RequiresUnreferencedCode ("--MethodWithRequires--")]
+ [RequiresAssemblyFiles ("--MethodWithRequires--")]
+ [RequiresDynamicCode ("--MethodWithRequires--")]
+ static void MethodWithRequires ()
+ {
+ }
+
+ static async Task<int> MethodAsync ()
+ {
+ return await Task.FromResult (0);
+ }
+
+
+ [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)]
+ static Type GetWithPublicMethods () => null;
+
+ [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)]
+ static Type GetAll () => null;
+ }
+}
diff --git a/test/Mono.Linker.Tests.Cases/RequiresCapability/ReflectionAccessFromCompilerGeneratedCode.cs b/test/Mono.Linker.Tests.Cases/RequiresCapability/ReflectionAccessFromCompilerGeneratedCode.cs
new file mode 100644
index 000000000..14fd917fb
--- /dev/null
+++ b/test/Mono.Linker.Tests.Cases/RequiresCapability/ReflectionAccessFromCompilerGeneratedCode.cs
@@ -0,0 +1,213 @@
+// 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.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Threading.Tasks;
+using Mono.Linker.Tests.Cases.Expectations.Assertions;
+using Mono.Linker.Tests.Cases.Expectations.Helpers;
+
+namespace Mono.Linker.Tests.Cases.RequiresCapability
+{
+ [SkipKeptItemsValidation]
+ [ExpectedNoWarnings]
+ public class ReflectionAccessFromCompilerGeneratedCode
+ {
+ public static void Main ()
+ {
+ ReflectionAccessFromStateMachine.Test ();
+ ReflectionAccessFromLocalFunction.Test ();
+ ReflectionAccessFromLambda.Test ();
+ }
+
+ class ReflectionAccessFromStateMachine
+ {
+ [ExpectedWarning ("IL2026", "--TypeWithMethodWithRequires.MethodWithRequires--", CompilerGeneratedCode = true,
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2026", "--MethodWithLocalFunctionWithRUC.LocalFunction--", CompilerGeneratedCode = true,
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2118", nameof (TypeWithMethodWithRequires.MethodWithLocalFunctionCallsRUC), "LocalFunction", CompilerGeneratedCode = true,
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2111", nameof (TypeWithMethodWithRequires.MethodWithAnnotations), CompilerGeneratedCode = true,
+ ProducedBy = ProducedBy.Trimmer)]
+ static IEnumerable<int> TestIterator ()
+ {
+ typeof (TypeWithMethodWithRequires).RequiresAll ();
+ yield return 0;
+ }
+
+ [RequiresUnreferencedCode ("--TestIteratorWithRUC--")]
+ static IEnumerable<int> TestIteratorWithRUC ()
+ {
+ typeof (TypeWithMethodWithRequires).RequiresAll ();
+ yield return 0;
+ }
+
+ [ExpectedWarning ("IL2026", "--TypeWithMethodWithRequires.MethodWithRequires--", CompilerGeneratedCode = true,
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2026", "--MethodWithLocalFunctionWithRUC.LocalFunction--", CompilerGeneratedCode = true,
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2118", nameof (TypeWithMethodWithRequires.MethodWithLocalFunctionCallsRUC), "LocalFunction", CompilerGeneratedCode = true,
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2111", nameof (TypeWithMethodWithRequires.MethodWithAnnotations), CompilerGeneratedCode = true,
+ ProducedBy = ProducedBy.Trimmer)]
+ static async void TestAsync ()
+ {
+ typeof (TypeWithMethodWithRequires).RequiresAll ();
+ await MethodAsync ();
+ }
+
+ [RequiresUnreferencedCode ("--TestAsyncWithRUC--")]
+ static async void TestAsyncWithRUC ()
+ {
+ typeof (TypeWithMethodWithRequires).RequiresAll ();
+ await MethodAsync ();
+ }
+
+ [ExpectedWarning ("IL2026", "--TestIteratorWithRUC--")]
+ [ExpectedWarning ("IL2026", "--TestAsyncWithRUC--")]
+ public static void Test ()
+ {
+ TestIterator ();
+ TestIteratorWithRUC ();
+ TestAsync ();
+ TestAsyncWithRUC ();
+ }
+ }
+
+ class ReflectionAccessFromLocalFunction
+ {
+ static void TestLocalFunction ()
+ {
+ [ExpectedWarning ("IL2026", "--TypeWithMethodWithRequires.MethodWithRequires--",
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2026", "--MethodWithLocalFunctionWithRUC.LocalFunction--",
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2118", nameof (TypeWithMethodWithRequires.MethodWithLocalFunctionCallsRUC), "LocalFunction",
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2111", nameof (TypeWithMethodWithRequires.MethodWithAnnotations),
+ ProducedBy = ProducedBy.Trimmer)]
+ void LocalFunction ()
+ {
+ typeof (TypeWithMethodWithRequires).RequiresAll ();
+ }
+ LocalFunction ();
+ }
+
+ [ExpectedWarning ("IL2026", "--LocalFunction--")]
+ static void TestLocalFunctionWithRUC ()
+ {
+ [RequiresUnreferencedCode ("--LocalFunction--")]
+ void LocalFunction ()
+ {
+ typeof (TypeWithMethodWithRequires).RequiresAll ();
+ }
+ LocalFunction ();
+ }
+
+ [RequiresUnreferencedCode ("--TestLocalFunctionInMethodWithRUC--")]
+ static void TestLocalFunctionInMethodWithRUC ()
+ {
+ void LocalFunction ()
+ {
+ typeof (TypeWithMethodWithRequires).RequiresAll ();
+ }
+ LocalFunction ();
+ }
+
+ [ExpectedWarning ("IL2026", "--TestLocalFunctionInMethodWithRUC--")]
+ public static void Test ()
+ {
+ TestLocalFunction ();
+ TestLocalFunctionWithRUC ();
+ TestLocalFunctionInMethodWithRUC ();
+ }
+ }
+
+ class ReflectionAccessFromLambda
+ {
+ static void TestLambda ()
+ {
+ var lambda =
+ [ExpectedWarning ("IL2026", "--TypeWithMethodWithRequires.MethodWithRequires--",
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2026", "--MethodWithLocalFunctionWithRUC.LocalFunction--",
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2118", nameof (TypeWithMethodWithRequires.MethodWithLocalFunctionCallsRUC), "LocalFunction",
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2111", nameof (TypeWithMethodWithRequires.MethodWithAnnotations),
+ ProducedBy = ProducedBy.Trimmer)]
+ () => {
+ typeof (TypeWithMethodWithRequires).RequiresAll ();
+ };
+ lambda ();
+ }
+
+ [ExpectedWarning ("IL2026", "--TestLambdaInMethodWithRUC--")]
+ static void TestLambdaWithRUC ()
+ {
+ var lambda =
+ [RequiresUnreferencedCode ("--TestLambdaInMethodWithRUC--")]
+ () => {
+ typeof (TypeWithMethodWithRequires).RequiresAll ();
+ };
+ lambda ();
+ }
+
+ [RequiresUnreferencedCode ("--TestLambdaInMethodWithRUC--")]
+ static void TestLambdaInMethodWithRUC ()
+ {
+ var lambda =
+ () => {
+ typeof (TypeWithMethodWithRequires).RequiresAll ();
+ };
+ lambda ();
+ }
+
+ [ExpectedWarning ("IL2026", "--TestLambdaInMethodWithRUC--")]
+ public static void Test ()
+ {
+ TestLambda ();
+ TestLambdaWithRUC ();
+ TestLambdaInMethodWithRUC ();
+ }
+ }
+
+ static async Task<int> MethodAsync ()
+ {
+ return await Task.FromResult (0);
+ }
+
+ class TypeWithMethodWithRequires
+ {
+ [RequiresUnreferencedCode ("--TypeWithMethodWithRequires.MethodWithRequires--")]
+ [RequiresAssemblyFiles ("--TypeWithMethodWithRequires.MethodWithRequires--")]
+ [RequiresDynamicCode ("--TypeWithMethodWithRequires.MethodWithRequires--")]
+ public static void MethodWithRequires ()
+ {
+ }
+
+ public static void MethodWithAnnotations ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type t) { }
+
+ [ExpectedWarning ("IL2026", "--MethodWithLocalFunctionWithRUC.LocalFunction--")]
+ public static void MethodWithLocalFunctionWithRUC ()
+ {
+ [RequiresUnreferencedCode ("--MethodWithLocalFunctionWithRUC.LocalFunction--")]
+ void LocalFunction ()
+ { }
+ LocalFunction ();
+ }
+
+ public static void MethodWithLocalFunctionCallsRUC ()
+ {
+ [ExpectedWarning ("IL2026", "--MethodWithRUC--")]
+ void LocalFunction () => MethodWithRUC ();
+ LocalFunction ();
+ }
+ }
+
+ [RequiresUnreferencedCode ("--MethodWithRUC--")]
+ static void MethodWithRUC () { }
+ }
+}
diff --git a/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresInCompilerGeneratedCode.cs b/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresInCompilerGeneratedCode.cs
index d3f277902..79b795c66 100644
--- a/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresInCompilerGeneratedCode.cs
+++ b/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresInCompilerGeneratedCode.cs
@@ -1,4 +1,4 @@
-// Copyright (c) .NET Foundation and contributors. All rights reserved.
+// 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;
@@ -618,7 +618,8 @@ namespace Mono.Linker.Tests.Cases.RequiresCapability
{
LocalFunction ();
- [ExpectedWarning ("IL2026", "--MethodWithRequires--", ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2026", "--MethodWithRequires--",
+ ProducedBy = ProducedBy.Trimmer)]
void LocalFunction () => typeof (RequiresInCompilerGeneratedCode)
.GetMethod ("MethodWithRequires", System.Reflection.BindingFlags.NonPublic)
.Invoke (null, new object[] { });
@@ -656,7 +657,8 @@ namespace Mono.Linker.Tests.Cases.RequiresCapability
{
LocalFunction ();
- [ExpectedWarning ("IL2026", "--TypeWithMethodWithRequires.MethodWithRequires--", ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2026", "--TypeWithMethodWithRequires.MethodWithRequires--",
+ ProducedBy = ProducedBy.Trimmer)]
void LocalFunction () => typeof (TypeWithMethodWithRequires).RequiresNonPublicMethods ();
}
@@ -869,19 +871,38 @@ namespace Mono.Linker.Tests.Cases.RequiresCapability
}
}
- class DynamicallyAccessedLocalFunctionUnused
+ class DynamicallyAccessedLocalFunctionUnusedShouldWarn
+ {
+ [ExpectedWarning ("IL2118", nameof (TestCallMethodWithRequiresInDynamicallyAccessedLocalFunction), "LocalFunction", ProducedBy = ProducedBy.Trimmer)]
+ public static void TestCallMethodWithRequiresInDynamicallyAccessedLocalFunction ()
+ {
+ typeof (DynamicallyAccessedLocalFunctionUnusedShouldWarn).RequiresNonPublicMethods ();
+
+ // The linker isn't able to figure out which user method this local function
+ // belongs to, but it doesn't warn because it is only accessed via reflection.
+ // Instead this warns on the reflection access.
+ [ExpectedWarning ("IL2026", "--MethodWithRequires--",
+ ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3002", "--MethodWithRequires--",
+ ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3050", "--MethodWithRequires--",
+ ProducedBy = ProducedBy.Analyzer)]
+ void LocalFunction () => MethodWithRequires ();
+ }
+ }
+
+ class DynamicallyAccessedLocalFunctionUnusedShouldSuppress
{
[RequiresUnreferencedCode ("Suppress in body")]
[RequiresAssemblyFiles ("Suppress in body")]
[RequiresDynamicCode ("Suppress in body")]
public static void TestCallMethodWithRequiresInDynamicallyAccessedLocalFunction ()
{
- typeof (DynamicallyAccessedLocalFunctionUnused).RequiresNonPublicMethods ();
+ typeof (DynamicallyAccessedLocalFunctionUnusedShouldSuppress).RequiresNonPublicMethods ();
- // This local function is unused except for the dynamic reference above,
- // so the linker isn't able to figure out which user method it belongs to,
- // and the warning is not suppressed.
- [ExpectedWarning ("IL2026", "--MethodWithRequires--", ProducedBy = ProducedBy.Trimmer)]
+ // The linker isn't able to figure out which user method this local function
+ // belongs to, but it doesn't warn because it is only accessed via reflection,
+ // in a RUC scope.
void LocalFunction () => MethodWithRequires ();
}
}
@@ -906,6 +927,26 @@ namespace Mono.Linker.Tests.Cases.RequiresCapability
[ExpectedWarning ("IL2026")]
[ExpectedWarning ("IL3002", ProducedBy = ProducedBy.Analyzer)]
[ExpectedWarning ("IL3050", ProducedBy = ProducedBy.Analyzer)]
+ static void TestSuppressionOnLocalFunctionWithAssignment ()
+ {
+ LocalFunction (); // This will produce a warning since the local function has Requires on it
+
+ [RequiresUnreferencedCode ("Suppress in body")]
+ [RequiresAssemblyFiles ("Suppress in body")]
+ [RequiresDynamicCode ("Suppress in body")]
+ void LocalFunction (Type unknownType = null)
+ {
+ MethodWithRequires ();
+ typeWithNonPublicMethods = unknownType;
+ }
+ }
+
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicMethods)]
+ static Type typeWithNonPublicMethods;
+
+ [ExpectedWarning ("IL2026")]
+ [ExpectedWarning ("IL3002", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3050", ProducedBy = ProducedBy.Analyzer)]
static void TestSuppressionOnLocalFunctionWithNestedLocalFunction ()
{
LocalFunction (); // This will produce a warning since the local function has Requires on it
@@ -994,8 +1035,10 @@ namespace Mono.Linker.Tests.Cases.RequiresCapability
TestGenericLocalFunctionWithAnnotationsAndClosure<TestType> ();
TestCallMethodWithRequiresInLtftnLocalFunction ();
DynamicallyAccessedLocalFunction.TestCallMethodWithRequiresInDynamicallyAccessedLocalFunction ();
- DynamicallyAccessedLocalFunctionUnused.TestCallMethodWithRequiresInDynamicallyAccessedLocalFunction ();
+ DynamicallyAccessedLocalFunctionUnusedShouldWarn.TestCallMethodWithRequiresInDynamicallyAccessedLocalFunction ();
+ DynamicallyAccessedLocalFunctionUnusedShouldSuppress.TestCallMethodWithRequiresInDynamicallyAccessedLocalFunction ();
TestSuppressionOnLocalFunction ();
+ TestSuppressionOnLocalFunctionWithAssignment ();
TestSuppressionOnLocalFunctionWithNestedLocalFunction ();
TestSuppressionOnOuterAndLocalFunction ();
TestSuppressionOnOuterWithSameName.Test ();
@@ -1043,6 +1086,20 @@ namespace Mono.Linker.Tests.Cases.RequiresCapability
lambda ();
}
+ [ExpectedWarning ("IL2026", "--LambdaWithRequires--")]
+ [ExpectedWarning ("IL3002", "--LambdaWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3050", "--LambdaWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ static void TestLambdaWithRequires ()
+ {
+ Action lambda =
+ [RequiresUnreferencedCodeAttribute ("--LambdaWithRequires--")]
+ [RequiresAssemblyFiles ("--LambdaWithRequires--")]
+ [RequiresDynamicCode ("--LambdaWithRequires--")]
+ () => MethodWithRequires ();
+
+ lambda ();
+ }
+
static void TestCallUnused ()
{
Action _ =
@@ -1052,6 +1109,18 @@ namespace Mono.Linker.Tests.Cases.RequiresCapability
() => MethodWithRequires ();
}
+ [ExpectedWarning ("IL2026", "--LambdaWithRequires--")]
+ [ExpectedWarning ("IL3002", "--LambdaWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3050", "--LambdaWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ static void TestLambdaWithRequiresUnused ()
+ {
+ Action _ =
+ [RequiresUnreferencedCode ("--LambdaWithRequires--")]
+ [RequiresAssemblyFiles ("--LambdaWithRequires--")]
+ [RequiresDynamicCode ("--LambdaWithRequires--")]
+ () => MethodWithRequires ();
+ }
+
static void TestCallWithClosure (int p = 0)
{
Action lambda =
@@ -1125,6 +1194,8 @@ namespace Mono.Linker.Tests.Cases.RequiresCapability
{
TestCall ();
TestCallUnused ();
+ TestLambdaWithRequires ();
+ TestLambdaWithRequiresUnused ();
TestCallWithClosure ();
TestCallWithClosureUnused ();
TestReflectionAccess ();
@@ -1390,6 +1461,13 @@ namespace Mono.Linker.Tests.Cases.RequiresCapability
MethodWithGenericWhichRequiresMethods<TypeWithMethodWithRequires> ();
}
+ [ExpectedWarning ("IL2026", "--TypeWithMethodWithRequires.MethodWithRequires--", CompilerGeneratedCode = true)]
+ static IEnumerable<int> TestDynamicallyAccessedMethodViaGenericTypeParameterInIterator ()
+ {
+ yield return 1;
+ new TypeWithGenericWhichRequiresMethods<TypeWithMethodWithRequires> ();
+ }
+
static void TestLocalFunctionInIteratorLocalFunction ()
{
IteratorLocalFunction ();
@@ -1428,6 +1506,7 @@ namespace Mono.Linker.Tests.Cases.RequiresCapability
{
TestIteratorLocalFunctionInAsync ();
TestDynamicallyAccessedMethodViaGenericMethodParameterInIterator ();
+ TestDynamicallyAccessedMethodViaGenericTypeParameterInIterator ();
TestLocalFunctionInIteratorLocalFunction ();
TestLocalFunctionCalledFromIteratorLocalFunctionAndMethod ();
}
@@ -1716,6 +1795,11 @@ namespace Mono.Linker.Tests.Cases.RequiresCapability
// state machine members.
[ExpectedWarning ("IL2026", "Requires to suppress", ProducedBy = ProducedBy.Trimmer)]
[ExpectedWarning ("IL2026", "Requires to suppress", ProducedBy = ProducedBy.Trimmer)]
+ // Linker warns about reflection access to compiler-generated state machine members.
+ [ExpectedWarning ("IL2118", nameof (StateMachinesOnlyReferencedViaReflection), "<" + nameof (TestAsyncOnlyReferencedViaReflectionWhichShouldWarn) + ">", "MoveNext()",
+ ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2118", nameof (StateMachinesOnlyReferencedViaReflection), "<" + nameof (TestIteratorOnlyReferencedViaReflectionWhichShouldWarn) + ">", "MoveNext()",
+ ProducedBy = ProducedBy.Trimmer)]
static void TestAll ()
{
typeof (StateMachinesOnlyReferencedViaReflection).RequiresAll ();
@@ -1751,6 +1835,14 @@ namespace Mono.Linker.Tests.Cases.RequiresCapability
void LocalFunction () => MethodWithRequires ();
}
+ static void TestLocalFunctionWithRequiresOnlyAccessedViaReflection ()
+ {
+ [RequiresUnreferencedCode ("--TestLocalFunctionWithRequiresOnlyAccessedViaReflection--")]
+ [RequiresAssemblyFiles ("--TestLocalFunctionWithRequiresOnlyAccessedViaReflection--")]
+ [RequiresDynamicCode ("--TestLocalFunctionWithRequiresOnlyAccessedViaReflection--")]
+ void LocalFunction () => MethodWithRequires ();
+ }
+
[ExpectedWarning ("IL2026", "LocalFunction")]
[ExpectedWarning ("IL3002", "LocalFunction", ProducedBy = ProducedBy.Analyzer)]
[ExpectedWarning ("IL3050", "LocalFunction", ProducedBy = ProducedBy.Analyzer)]
@@ -1778,6 +1870,16 @@ namespace Mono.Linker.Tests.Cases.RequiresCapability
void LocalFunction () => MethodWithRequires ();
}
+ [RequiresUnreferencedCode ("--TestLocalFunctionInMethodWithRequiresOnlyAccessedViaReflection--")]
+ [RequiresAssemblyFiles ("--TestLocalFunctionInMethodWithRequiresOnlyAccessedViaReflection--")]
+ [RequiresDynamicCode ("--TestLocalFunctionInMethodWithRequiresOnlyAccessedViaReflection--")]
+ static void TestLocalFunctionInMethodWithRequiresOnlyAccessedViaReflection ()
+ {
+ // This is unused, except for the reflection access. Don't warn about MethodWithRequires
+ // because the reflection access will warn about accessing compiler-generated code.
+ void LocalFunction () => MethodWithRequires ();
+ }
+
[RequiresUnreferencedCode ("--TestLocalFunctionWithClosureInMethodWithRequires--")]
[RequiresAssemblyFiles ("--TestLocalFunctionWithClosureInMethodWithRequires--")]
[RequiresDynamicCode ("--TestLocalFunctionWithClosureInMethodWithRequires--")]
@@ -1795,13 +1897,18 @@ namespace Mono.Linker.Tests.Cases.RequiresCapability
// Warnings for Reflection access to methods with Requires
[ExpectedWarning ("IL2026", "--TestLocalFunctionInMethodWithRequires--")]
[ExpectedWarning ("IL2026", "--TestLocalFunctionWithClosureInMethodWithRequires--")]
+ [ExpectedWarning ("IL2026", "--TestLocalFunctionInMethodWithRequiresOnlyAccessedViaReflection--")]
// The linker correctly emits warnings about reflection access to local functions with Requires
// or which inherit Requires from the containing method. The analyzer doesn't bind to local functions
// so does not warn here.
[ExpectedWarning ("IL2026", "--TestLocalFunctionWithRequires--", ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2026", "--TestLocalFunctionWithRequiresOnlyAccessedViaReflection--", ProducedBy = ProducedBy.Trimmer)]
[ExpectedWarning ("IL2026", "--TestLocalFunctionWithClosureWithRequires--", ProducedBy = ProducedBy.Trimmer)]
[ExpectedWarning ("IL2026", "--TestLocalFunctionInMethodWithRequires--", ProducedBy = ProducedBy.Trimmer)]
[ExpectedWarning ("IL2026", "--TestLocalFunctionWithClosureInMethodWithRequires--", ProducedBy = ProducedBy.Trimmer)]
+ // Linker warns about reflection access to compiler-generated code
+ [ExpectedWarning ("IL2118", nameof (LocalFunctionsReferencedViaReflection), nameof (TestLocalFunctionInMethodWithRequiresOnlyAccessedViaReflection),
+ ProducedBy = ProducedBy.Trimmer)]
static void TestAll ()
{
typeof (LocalFunctionsReferencedViaReflection).RequiresAll ();
@@ -1810,11 +1917,16 @@ namespace Mono.Linker.Tests.Cases.RequiresCapability
// Warnings for Reflection access to methods with Requires
[ExpectedWarning ("IL2026", "--TestLocalFunctionInMethodWithRequires--")]
[ExpectedWarning ("IL2026", "--TestLocalFunctionWithClosureInMethodWithRequires--")]
+ [ExpectedWarning ("IL2026", "--TestLocalFunctionInMethodWithRequiresOnlyAccessedViaReflection--")]
// NonPublicMethods warns for local functions not emitted into display classes.
[ExpectedWarning ("IL2026", "--TestLocalFunctionWithRequires--", ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2026", "--TestLocalFunctionWithRequiresOnlyAccessedViaReflection--", ProducedBy = ProducedBy.Trimmer)]
[ExpectedWarning ("IL2026", "--TestLocalFunctionWithClosureWithRequires--", ProducedBy = ProducedBy.Trimmer)]
[ExpectedWarning ("IL2026", "--TestLocalFunctionInMethodWithRequires--", ProducedBy = ProducedBy.Trimmer)]
[ExpectedWarning ("IL2026", "--TestLocalFunctionWithClosureInMethodWithRequires--", ProducedBy = ProducedBy.Trimmer)]
+ // Linker warns about reflection access to compiler-generated code
+ [ExpectedWarning ("IL2118", nameof (LocalFunctionsReferencedViaReflection), "<" + nameof (TestLocalFunctionInMethodWithRequiresOnlyAccessedViaReflection) + ">",
+ ProducedBy = ProducedBy.Trimmer)]
static void TestNonPublicMethods ()
{
typeof (LocalFunctionsReferencedViaReflection).RequiresNonPublicMethods ();
@@ -2053,6 +2165,10 @@ namespace Mono.Linker.Tests.Cases.RequiresCapability
{
}
+ class TypeWithGenericWhichRequiresMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicMethods)] T>
+ {
+ }
+
class TypeWithGenericWhichRequiresNonPublicFields<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicFields)] T> { }
class TestType { }
diff --git a/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnClass.cs b/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnClass.cs
index 9cb6e8a50..a0ecc2e51 100644
--- a/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnClass.cs
+++ b/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnClass.cs
@@ -719,12 +719,30 @@ namespace Mono.Linker.Tests.Cases.RequiresCapability
instance.GetType ().GetField ("publicField");
}
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)]
+ class DAMAnnotatedClassAccessedFromRUCScope
+ {
+ [ExpectedWarning ("IL2112", "DAMAnnotatedClassAccessedFromRUCScope.RUCMethod", ProducedBy = ProducedBy.Trimmer)]
+ [RequiresUnreferencedCode ("--RUCMethod--")]
+ public static void RUCMethod () { }
+ }
+
+ // RUC on the callsite to GetType should not suppress warnings about the
+ // attribute on the type.
+ [RequiresUnreferencedCode ("--TestDAMOnTypeAccessInRUCScope--")]
+ static void TestDAMOnTypeAccessInRUCScope (DAMAnnotatedClassAccessedFromRUCScope instance = null)
+ {
+ instance.GetType ().GetMethod ("RUCMethod");
+ }
+
+ [ExpectedWarning ("IL2026", "--TestDAMOnTypeAccessInRUCScope--")]
public static void Test ()
{
TestDAMAccess ();
TestDirectReflectionAccess ();
TestDynamicDependencyAccess ();
TestDAMOnTypeAccess (null);
+ TestDAMOnTypeAccessInRUCScope ();
}
}
diff --git a/test/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInCompilerGeneratedCode.cs b/test/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInCompilerGeneratedCode.cs
index d1e1cc5f6..0c9bd06b1 100644
--- a/test/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInCompilerGeneratedCode.cs
+++ b/test/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInCompilerGeneratedCode.cs
@@ -279,6 +279,7 @@ namespace Mono.Linker.Tests.Cases.Warnings.WarningSuppression
class DynamicallyAccessedLocalFunction
{
+ [ExpectedWarning ("IL2118", "LocalFunction", ProducedBy = ProducedBy.Trimmer)]
[UnconditionalSuppressMessage ("Test", "IL2026")]
public static void TestCallRUCMethodInDynamicallyAccessedLocalFunction ()
{
@@ -290,21 +291,6 @@ namespace Mono.Linker.Tests.Cases.Warnings.WarningSuppression
}
}
- class DynamicallyAccessedLocalFunctionUnused
- {
- [UnconditionalSuppressMessage ("Test", "IL2026")]
- public static void TestCallRUCMethodInDynamicallyAccessedLocalFunction ()
- {
- typeof (DynamicallyAccessedLocalFunctionUnused).RequiresNonPublicMethods ();
-
- // This local function is unused except for the dynamic reference above,
- // so the linker isn't able to figure out which user method it belongs to,
- // and the warning is not suppressed.
- [ExpectedWarning ("IL2026", "--RequiresUnreferencedCodeMethod--", ProducedBy = ProducedBy.Trimmer)]
- void LocalFunction () => RequiresUnreferencedCodeMethod ();
- }
- }
-
static void TestSuppressionOnLocalFunction ()
{
LocalFunction ();
@@ -370,7 +356,6 @@ namespace Mono.Linker.Tests.Cases.Warnings.WarningSuppression
TestGenericLocalFunctionWithAnnotationsAndClosure<TestType> ();
TestCallRUCMethodInLtftnLocalFunction ();
DynamicallyAccessedLocalFunction.TestCallRUCMethodInDynamicallyAccessedLocalFunction ();
- DynamicallyAccessedLocalFunctionUnused.TestCallRUCMethodInDynamicallyAccessedLocalFunction ();
TestSuppressionOnLocalFunction ();
TestSuppressionOnOuterAndLocalFunction ();
TestSuppressionOnOuterWithSameName.Test ();
@@ -441,10 +426,11 @@ namespace Mono.Linker.Tests.Cases.Warnings.WarningSuppression
class DynamicallyAccessedLambda
{
+ [ExpectedWarning ("IL2118", nameof (TestCallRUCMethodInDynamicallyAccessedLambda), ProducedBy = ProducedBy.Trimmer)]
[UnconditionalSuppressMessage ("Test", "IL2026")]
public static void TestCallRUCMethodInDynamicallyAccessedLambda ()
{
- typeof (DynamicallyAccessedLambda).RequiresNonPublicMethods ();
+ typeof (DynamicallyAccessedLambda).RequiresAll ();
Action lambda = () => RequiresUnreferencedCodeMethod ();
@@ -454,10 +440,11 @@ namespace Mono.Linker.Tests.Cases.Warnings.WarningSuppression
class DynamicallyAccessedLambdaUnused
{
+ [ExpectedWarning ("IL2118", nameof (TestCallRUCMethodInDynamicallyAccessedLambda), ProducedBy = ProducedBy.Trimmer)]
[UnconditionalSuppressMessage ("Test", "IL2026")]
public static void TestCallRUCMethodInDynamicallyAccessedLambda ()
{
- typeof (DynamicallyAccessedLambdaUnused).RequiresNonPublicMethods ();
+ typeof (DynamicallyAccessedLambdaUnused).RequiresAll ();
Action _ = () => RequiresUnreferencedCodeMethod ();
}