diff --git a/src/ILLink.RoslynAnalyzer/RequiresISymbolExtensions.cs b/src/ILLink.RoslynAnalyzer/RequiresISymbolExtensions.cs
index aaf80b72536e..769cbff8852b 100644
--- a/src/ILLink.RoslynAnalyzer/RequiresISymbolExtensions.cs
+++ b/src/ILLink.RoslynAnalyzer/RequiresISymbolExtensions.cs
@@ -57,8 +57,13 @@ private static bool IsInRequiresScope (this ISymbol member, string requiresAttri
if (member is ITypeSymbol)
return false;
- if (member.HasAttribute (requiresAttribute) && !member.IsStaticConstructor ())
- return true;
+ while (true) {
+ if (member.HasAttribute (requiresAttribute) && !member.IsStaticConstructor ())
+ return true;
+ if (member.ContainingSymbol is not IMethodSymbol method)
+ break;
+ member = method;
+ }
if (member.ContainingType is ITypeSymbol containingType && containingType.HasAttribute (requiresAttribute))
return true;
diff --git a/src/linker/Linker.Steps/MarkStep.cs b/src/linker/Linker.Steps/MarkStep.cs
index 90deb75047b7..33b0c15580fc 100644
--- a/src/linker/Linker.Steps/MarkStep.cs
+++ b/src/linker/Linker.Steps/MarkStep.cs
@@ -2901,13 +2901,15 @@ internal bool ShouldSuppressAnalysisWarningsForRequiresUnreferencedCode ()
if (originMember is not IMemberDefinition member)
return false;
- MethodDefinition? userDefinedMethod = Context.CompilerGeneratedState.GetUserDefinedMethodForCompilerGeneratedMember (member);
- if (userDefinedMethod == null)
- return false;
-
- Debug.Assert (userDefinedMethod != originMember);
+ MethodDefinition? owningMethod;
+ while (Context.CompilerGeneratedState.TryGetOwningMethodForCompilerGeneratedMember (member, out owningMethod)) {
+ Debug.Assert (owningMethod != member);
+ if (Annotations.IsMethodInRequiresUnreferencedCodeScope (owningMethod))
+ return true;
+ member = owningMethod;
+ }
- return Annotations.IsMethodInRequiresUnreferencedCodeScope (userDefinedMethod);
+ return false;
}
internal void CheckAndReportRequiresUnreferencedCode (MethodDefinition method)
diff --git a/src/linker/Linker/Annotations.cs b/src/linker/Linker/Annotations.cs
index d51c4ea484dd..8845271415fd 100644
--- a/src/linker/Linker/Annotations.cs
+++ b/src/linker/Linker/Annotations.cs
@@ -593,18 +593,21 @@ public bool SetPreservedStaticCtor (TypeDefinition type)
///
/// Unlike only static methods
/// and .ctors are reported as requiring unreferenced code when the declaring type has RUC on it.
- internal bool DoesMethodRequireUnreferencedCode (MethodDefinition method, [NotNullWhen (returnValue: true)] out RequiresUnreferencedCodeAttribute? attribute)
+ internal bool DoesMethodRequireUnreferencedCode (MethodDefinition originalMethod, [NotNullWhen (returnValue: true)] out RequiresUnreferencedCodeAttribute? attribute)
{
- if (method.IsStaticConstructor ()) {
- attribute = null;
- return false;
- }
- if (TryGetLinkerAttribute (method, out attribute))
- return true;
+ MethodDefinition? method = originalMethod;
+ do {
+ if (method.IsStaticConstructor ()) {
+ attribute = null;
+ return false;
+ }
+ if (TryGetLinkerAttribute (method, out attribute))
+ return true;
- if ((method.IsStatic || method.IsConstructor) && method.DeclaringType is not null &&
- TryGetLinkerAttribute (method.DeclaringType, out attribute))
- return true;
+ if ((method.IsStatic || method.IsConstructor) && method.DeclaringType is not null &&
+ TryGetLinkerAttribute (method.DeclaringType, out attribute))
+ return true;
+ } while (context.CompilerGeneratedState.TryGetOwningMethodForCompilerGeneratedMember (method, out method));
return false;
}
diff --git a/src/linker/Linker/CallGraph.cs b/src/linker/Linker/CallGraph.cs
new file mode 100644
index 000000000000..065a20329f59
--- /dev/null
+++ b/src/linker/Linker/CallGraph.cs
@@ -0,0 +1,43 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Generic;
+using Mono.Cecil;
+
+namespace Mono.Linker
+{
+ class CallGraph
+ {
+ readonly Dictionary> callGraph;
+
+ public CallGraph () => callGraph = new Dictionary> ();
+
+ public void TrackCall (MethodDefinition fromMethod, MethodDefinition toMethod)
+ {
+ if (!callGraph.TryGetValue (fromMethod, out HashSet? toMethods)) {
+ toMethods = new HashSet ();
+ callGraph.Add (fromMethod, toMethods);
+ }
+ toMethods.Add (toMethod);
+ }
+
+ public IEnumerable GetReachableMethods (MethodDefinition start)
+ {
+ Queue queue = new ();
+ HashSet visited = new ();
+ visited.Add (start);
+ queue.Enqueue (start);
+ while (queue.TryDequeue (out MethodDefinition? method)) {
+ if (!callGraph.TryGetValue (method, out HashSet? callees))
+ continue;
+
+ foreach (var callee in callees) {
+ if (visited.Add (callee)) {
+ queue.Enqueue (callee);
+ yield return callee;
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/linker/Linker/CompilerGeneratedNames.cs b/src/linker/Linker/CompilerGeneratedNames.cs
new file mode 100644
index 000000000000..9532c1497800
--- /dev/null
+++ b/src/linker/Linker/CompilerGeneratedNames.cs
@@ -0,0 +1,56 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Mono.Linker
+{
+ class CompilerGeneratedNames
+ {
+ internal static bool IsGeneratedMemberName (string memberName)
+ {
+ return memberName.Length > 0 && memberName[0] == '<';
+ }
+
+ internal static bool IsLambdaDisplayClass (string className)
+ {
+ if (!IsGeneratedMemberName (className))
+ return false;
+
+ // This is true for static lambdas (which are emitted into a class like <>c)
+ // and for instance lambdas (which are emitted into a class like <>c__DisplayClass1_0)
+ return className.StartsWith ("<>c");
+ }
+
+ internal static bool IsLambdaOrLocalFunction (string methodName) => IsLambdaMethod (methodName) || IsLocalFunction (methodName);
+
+ // Lambda methods have generated names like "b__0_1" where "UserMethod" is the name
+ // of the original user code that contains the lambda method declaration.
+ internal static bool IsLambdaMethod (string methodName)
+ {
+ if (!IsGeneratedMemberName (methodName))
+ return false;
+
+ int i = methodName.IndexOf ('>', 1);
+ if (i == -1)
+ return false;
+
+ // Ignore the method ordinal/generation and lambda ordinal/generation.
+ return methodName[i + 1] == 'b';
+ }
+
+ // Local functions have generated names like "g__LocalFunction|0_1" where "UserMethod" is the name
+ // of the original user code that contains the lambda method declaration, and "LocalFunction" is the name of
+ // the local function.
+ internal static bool IsLocalFunction (string methodName)
+ {
+ if (!IsGeneratedMemberName (methodName))
+ return false;
+
+ int i = methodName.IndexOf ('>', 1);
+ if (i == -1)
+ return false;
+
+ // Ignore the method ordinal/generation and local function ordinal/generation.
+ return methodName[i + 1] == 'g';
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/linker/Linker/CompilerGeneratedState.cs b/src/linker/Linker/CompilerGeneratedState.cs
index 3c22da1f5478..9e8a76c7d0a5 100644
--- a/src/linker/Linker/CompilerGeneratedState.cs
+++ b/src/linker/Linker/CompilerGeneratedState.cs
@@ -1,9 +1,12 @@
-// Licensed to the .NET Foundation under one or more agreements.
+// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using ILLink.Shared;
using Mono.Cecil;
+using Mono.Cecil.Cil;
namespace Mono.Linker
{
@@ -12,17 +15,29 @@ public class CompilerGeneratedState
{
readonly LinkContext _context;
readonly Dictionary _compilerGeneratedTypeToUserCodeMethod;
+ readonly Dictionary _compilerGeneratedMethodToUserCodeMethod;
readonly HashSet _typesWithPopulatedCache;
public CompilerGeneratedState (LinkContext context)
{
_context = context;
_compilerGeneratedTypeToUserCodeMethod = new Dictionary ();
+ _compilerGeneratedMethodToUserCodeMethod = new Dictionary ();
_typesWithPopulatedCache = new HashSet ();
}
- static bool HasRoslynCompilerGeneratedName (TypeDefinition type) =>
- type.Name.Contains ('<') || (type.DeclaringType != null && HasRoslynCompilerGeneratedName (type.DeclaringType));
+ static IEnumerable GetCompilerGeneratedNestedTypes (TypeDefinition type)
+ {
+ foreach (var nestedType in type.NestedTypes) {
+ if (!CompilerGeneratedNames.IsGeneratedMemberName (nestedType.Name))
+ continue;
+
+ yield return nestedType;
+
+ foreach (var recursiveNestedType in GetCompilerGeneratedNestedTypes (nestedType))
+ yield return recursiveNestedType;
+ }
+ }
void PopulateCacheForType (TypeDefinition type)
{
@@ -30,9 +45,38 @@ void PopulateCacheForType (TypeDefinition type)
if (!_typesWithPopulatedCache.Add (type))
return;
- foreach (MethodDefinition method in type.Methods) {
+ var callGraph = new CallGraph ();
+ var callingMethods = new HashSet ();
+
+ void ProcessMethod (MethodDefinition method)
+ {
+ if (!CompilerGeneratedNames.IsLambdaOrLocalFunction (method.Name)) {
+ // If it's not a nested function, track as an entry point to the call graph.
+ var added = callingMethods.Add (method);
+ Debug.Assert (added);
+ }
+
+ // Discover calls or references to lambdas or local functions. This includes
+ // calls to local functions, and lambda assignments (which use ldftn).
+ if (method.Body != null) {
+ foreach (var instruction in method.Body.Instructions) {
+ if (instruction.OpCode.OperandType != OperandType.InlineMethod)
+ continue;
+
+ MethodDefinition? lambdaOrLocalFunction = _context.TryResolve ((MethodReference) instruction.Operand);
+ if (lambdaOrLocalFunction == null)
+ continue;
+
+ if (!CompilerGeneratedNames.IsLambdaOrLocalFunction (lambdaOrLocalFunction.Name))
+ continue;
+
+ callGraph.TrackCall (method, lambdaOrLocalFunction);
+ }
+ }
+
+ // Discover state machine methods.
if (!method.HasCustomAttributes)
- continue;
+ return;
foreach (var attribute in method.CustomAttributes) {
if (attribute.AttributeType.Namespace != "System.Runtime.CompilerServices")
@@ -43,17 +87,53 @@ void PopulateCacheForType (TypeDefinition type)
case "AsyncStateMachineAttribute":
case "IteratorStateMachineAttribute":
TypeDefinition? stateMachineType = GetFirstConstructorArgumentAsType (attribute);
- if (stateMachineType != null) {
- if (!_compilerGeneratedTypeToUserCodeMethod.TryAdd (stateMachineType, method)) {
- var alreadyAssociatedMethod = _compilerGeneratedTypeToUserCodeMethod[stateMachineType];
- _context.LogWarning (new MessageOrigin (method), DiagnosticId.MethodsAreAssociatedWithStateMachine, method.GetDisplayName (), alreadyAssociatedMethod.GetDisplayName (), stateMachineType.GetDisplayName ());
- }
+ if (stateMachineType == null)
+ break;
+ Debug.Assert (stateMachineType.DeclaringType == type ||
+ (CompilerGeneratedNames.IsGeneratedMemberName (stateMachineType.DeclaringType.Name) &&
+ stateMachineType.DeclaringType.DeclaringType == type));
+ if (!_compilerGeneratedTypeToUserCodeMethod.TryAdd (stateMachineType, method)) {
+ var alreadyAssociatedMethod = _compilerGeneratedTypeToUserCodeMethod[stateMachineType];
+ _context.LogWarning (new MessageOrigin (method), DiagnosticId.MethodsAreAssociatedWithStateMachine, method.GetDisplayName (), alreadyAssociatedMethod.GetDisplayName (), stateMachineType.GetDisplayName ());
}
break;
}
}
}
+
+ // Look for state machine methods, and methods which call local functions.
+ foreach (MethodDefinition method in type.Methods)
+ ProcessMethod (method);
+
+ // Also scan compiler-generated state machine methods (in case they have calls to nested functions),
+ // and nested functions inside compiler-generated closures (in case they call other nested functions).
+
+ // State machines can be emitted into lambda display classes, so we need to go down at least two
+ // levels to find calls from iterator nested functions to other nested functions. We just recurse into
+ // all compiler-generated nested types to avoid depending on implementation details.
+
+ foreach (var nestedType in GetCompilerGeneratedNestedTypes (type)) {
+ foreach (var method in nestedType.Methods)
+ ProcessMethod (method);
+ }
+
+ // Now we've discovered the call graphs for calls to nested functions.
+ // Use this to map back from nested functions to the declaring user methods.
+
+ // Note: This maps all nested functions back to the user code, not to the immediately
+ // declaring local function. The IL doesn't contain enough information in general for
+ // us to determine the nesting of local functions and lambdas.
+
+ // 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 nestedFunction in callGraph.GetReachableMethods (userDefinedMethod)) {
+ Debug.Assert (CompilerGeneratedNames.IsLambdaOrLocalFunction (nestedFunction.Name));
+ _compilerGeneratedMethodToUserCodeMethod.Add (nestedFunction, userDefinedMethod);
+ }
+ }
}
static TypeDefinition? GetFirstConstructorArgumentAsType (CustomAttribute attribute)
@@ -64,27 +144,54 @@ void PopulateCacheForType (TypeDefinition type)
return attribute.ConstructorArguments[0].Value as TypeDefinition;
}
- public MethodDefinition? GetUserDefinedMethodForCompilerGeneratedMember (IMemberDefinition sourceMember)
+ // For state machine types/members, maps back to the state machine method.
+ // For local functions and lambdas, maps back to the owning method in user code (not the declaring
+ // lambda or local function, because the IL doesn't contain enough information to figure this out).
+ public bool TryGetOwningMethodForCompilerGeneratedMember (IMemberDefinition sourceMember, [NotNullWhen (true)] out MethodDefinition? owningMethod)
{
+ owningMethod = null;
if (sourceMember == null)
- return null;
+ return false;
- TypeDefinition compilerGeneratedType = (sourceMember as TypeDefinition) ?? sourceMember.DeclaringType;
- if (_compilerGeneratedTypeToUserCodeMethod.TryGetValue (compilerGeneratedType, out MethodDefinition? userDefinedMethod))
- return userDefinedMethod;
+ MethodDefinition? compilerGeneratedMethod = sourceMember as MethodDefinition;
+ if (compilerGeneratedMethod != null) {
+ if (_compilerGeneratedMethodToUserCodeMethod.TryGetValue (compilerGeneratedMethod, out owningMethod))
+ return true;
+ }
- // Only handle async or iterator state machine
- // So go to the declaring type and check if it's compiler generated (as a perf optimization)
- if (!HasRoslynCompilerGeneratedName (compilerGeneratedType) || compilerGeneratedType.DeclaringType == null)
- return null;
+ TypeDefinition sourceType = (sourceMember as TypeDefinition) ?? sourceMember.DeclaringType;
+
+ if (_compilerGeneratedTypeToUserCodeMethod.TryGetValue (sourceType, out owningMethod))
+ return true;
+
+ if (!CompilerGeneratedNames.IsGeneratedMemberName (sourceMember.Name) && !CompilerGeneratedNames.IsGeneratedMemberName (sourceType.Name))
+ return false;
+
+ // sourceType is a state machine type, or the type containing a lambda or local function.
+ var typeToCache = sourceType;
- // Now go to its declaring type and search all methods to find the one which points to the type as its
+ // Look in the declaring type if this is a compiler-generated type (state machine or display class).
+ // State machines can be emitted into display classes, so we may also need to go one more level up.
+ // To avoid depending on implementation details, we go up until we see a non-compiler-generated type.
+ // This is the counterpart to GetCompilerGeneratedNestedTypes.
+ while (typeToCache != null && CompilerGeneratedNames.IsGeneratedMemberName (typeToCache.Name))
+ typeToCache = typeToCache.DeclaringType;
+
+ if (typeToCache == null)
+ return false;
+
+ // Search all methods to find the one which points to the type as its
// state machine implementation.
- PopulateCacheForType (compilerGeneratedType.DeclaringType);
- if (_compilerGeneratedTypeToUserCodeMethod.TryGetValue (compilerGeneratedType, out userDefinedMethod))
- return userDefinedMethod;
+ PopulateCacheForType (typeToCache);
+ if (compilerGeneratedMethod != null) {
+ if (_compilerGeneratedMethodToUserCodeMethod.TryGetValue (compilerGeneratedMethod, out owningMethod))
+ return true;
+ }
+
+ if (_compilerGeneratedTypeToUserCodeMethod.TryGetValue (sourceType, out owningMethod))
+ return true;
- return null;
+ return false;
}
}
}
diff --git a/src/linker/Linker/UnconditionalSuppressMessageAttributeState.cs b/src/linker/Linker/UnconditionalSuppressMessageAttributeState.cs
index 2820fe35f90a..a06035ff53bf 100644
--- a/src/linker/Linker/UnconditionalSuppressMessageAttributeState.cs
+++ b/src/linker/Linker/UnconditionalSuppressMessageAttributeState.cs
@@ -55,13 +55,15 @@ public bool IsSuppressed (int id, MessageOrigin warningOrigin, out SuppressMessa
if (provider is not IMemberDefinition member)
return false;
- MethodDefinition? userDefinedMethod = _context.CompilerGeneratedState.GetUserDefinedMethodForCompilerGeneratedMember (member);
- if (userDefinedMethod == null)
- return false;
-
- Debug.Assert (userDefinedMethod != provider);
+ MethodDefinition? owningMethod;
+ while (_context.CompilerGeneratedState.TryGetOwningMethodForCompilerGeneratedMember (member, out owningMethod)) {
+ Debug.Assert (owningMethod != member);
+ if (IsSuppressed (id, owningMethod, out info))
+ return true;
+ member = owningMethod;
+ }
- return IsSuppressed (id, userDefinedMethod, out info);
+ return false;
}
bool IsSuppressed (int id, ICustomAttributeProvider warningOrigin, out SuppressMessageInfo info)
diff --git a/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresInCompilerGeneratedCode.cs b/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresInCompilerGeneratedCode.cs
index cadebd061f4b..4be81c17ec23 100644
--- a/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresInCompilerGeneratedCode.cs
+++ b/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresInCompilerGeneratedCode.cs
@@ -1,4 +1,4 @@
-// Licensed to the .NET Foundation under one or more agreements.
+// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
@@ -27,6 +27,8 @@ public static void Main ()
WarnInLocalFunction.Test ();
SuppressInLocalFunction.Test ();
+ WarnInNonNestedLocalFunctionTest ();
+ SuppressInNonNestedLocalFunctionTest ();
WarnInLambda.Test ();
SuppressInLambda.Test ();
@@ -35,6 +37,8 @@ public static void Main ()
SuppressInComplex.Test ();
StateMachinesOnlyReferencedViaReflection.Test ();
+ LocalFunctionsReferencedViaReflection.Test ();
+ LambdasReferencedViaReflection.Test ();
ComplexCases.AsyncBodyCallingMethodWithRequires.Test ();
ComplexCases.GenericAsyncBodyCallingMethodWithRequires.Test ();
@@ -574,6 +578,15 @@ static void TestCall ()
void LocalFunction () => MethodWithRequires ();
}
+ static void TestCallUnused ()
+ {
+ // Analyzer emits warnings for code in unused local functions.
+ [ExpectedWarning ("IL2026", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ void LocalFunction () => MethodWithRequires ();
+ }
+
static void TestCallWithClosure (int p = 0)
{
LocalFunction ();
@@ -588,6 +601,19 @@ void LocalFunction ()
}
}
+ static void TestCallWithClosureUnused (int p = 0)
+ {
+ // Analyzer emits warnings for code in unused local functions.
+ [ExpectedWarning ("IL2026", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ void LocalFunction ()
+ {
+ p++;
+ MethodWithRequires ();
+ }
+ }
+
static void TestReflectionAccess ()
{
LocalFunction ();
@@ -637,7 +663,9 @@ static void TestDynamicallyAccessedMethod ()
public static void Test ()
{
TestCall ();
+ TestCallUnused ();
TestCallWithClosure ();
+ TestCallWithClosureUnused ();
TestReflectionAccess ();
TestLdftn ();
TestLazyDelegate ();
@@ -647,9 +675,6 @@ public static void Test ()
class SuppressInLocalFunction
{
- // Requires doesn't propagate into local functions yet
- // so its suppression effect also doesn't propagate
-
[RequiresUnreferencedCode ("Suppress in body")]
[RequiresAssemblyFiles ("Suppress in body")]
[RequiresDynamicCode ("Suppress in body")]
@@ -657,12 +682,24 @@ static void TestCall ()
{
LocalFunction ();
- [ExpectedWarning ("IL2026")]
- [ExpectedWarning ("IL3002", ProducedBy = ProducedBy.Analyzer)]
- [ExpectedWarning ("IL3050", ProducedBy = ProducedBy.Analyzer)]
void LocalFunction () => MethodWithRequires ();
}
+ [RequiresUnreferencedCode ("Suppress in body")]
+ [RequiresAssemblyFiles ("Suppress in body")]
+ [RequiresDynamicCode ("Suppress in body")]
+ static void TestCallFromNestedLocalFunction ()
+ {
+ LocalFunction ();
+
+ void LocalFunction ()
+ {
+ NestedLocalFunction ();
+
+ void NestedLocalFunction () => MethodWithRequires ();
+ }
+ }
+
[RequiresUnreferencedCode ("Suppress in body")]
[RequiresAssemblyFiles ("Suppress in body")]
[RequiresDynamicCode ("Suppress in body")]
@@ -670,9 +707,6 @@ static void TestCallWithClosure (int p = 0)
{
LocalFunction ();
- [ExpectedWarning ("IL2026")]
- [ExpectedWarning ("IL3002", ProducedBy = ProducedBy.Analyzer)]
- [ExpectedWarning ("IL3050", ProducedBy = ProducedBy.Analyzer)]
void LocalFunction ()
{
p++;
@@ -687,7 +721,6 @@ static async void TestReflectionAccess ()
{
LocalFunction ();
- [ExpectedWarning ("IL2026", ProducedBy = ProducedBy.Trimmer)]
void LocalFunction () => typeof (RequiresInCompilerGeneratedCode)
.GetMethod ("MethodWithRequires", System.Reflection.BindingFlags.NonPublic)
.Invoke (null, new object[] { });
@@ -700,9 +733,6 @@ static async void TestLdftn ()
{
LocalFunction ();
- [ExpectedWarning ("IL2026")]
- [ExpectedWarning ("IL3002", ProducedBy = ProducedBy.Analyzer)]
- [ExpectedWarning ("IL3050", ProducedBy = ProducedBy.Analyzer)]
void LocalFunction ()
{
var action = new Action (MethodWithRequires);
@@ -731,7 +761,6 @@ static async void TestDynamicallyAccessedMethod ()
{
LocalFunction ();
- [ExpectedWarning ("IL2026", ProducedBy = ProducedBy.Trimmer)]
void LocalFunction () => typeof (TypeWithMethodWithRequires).RequiresNonPublicMethods ();
}
@@ -742,7 +771,6 @@ static async void TestMethodParameterWithRequirements (Type unknownType = null)
{
LocalFunction ();
- [ExpectedWarning ("IL2077", ProducedBy = ProducedBy.Trimmer)]
void LocalFunction () => unknownType.RequiresNonPublicMethods ();
}
@@ -753,7 +781,6 @@ static void TestGenericMethodParameterRequirement ()
{
LocalFunction ();
- [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer)]
void LocalFunction () => MethodWithGenericWhichRequiresMethods ();
}
@@ -764,7 +791,6 @@ static void TestGenericTypeParameterRequirement ()
{
LocalFunction ();
- [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer)]
void LocalFunction () => new TypeWithGenericWhichRequiresNonPublicFields ();
}
@@ -788,8 +814,6 @@ static void TestGenericLocalFunctionInner ()
{
LocalFunction ();
- [ExpectedWarning ("IL2087", ProducedBy = ProducedBy.Trimmer)]
- [ExpectedWarning ("IL2087", ProducedBy = ProducedBy.Trimmer)]
void LocalFunction ()
{
typeof (TUnknown).RequiresPublicMethods ();
@@ -827,9 +851,6 @@ static void TestCallMethodWithRequiresInLtftnLocalFunction ()
{
var _ = new Action (LocalFunction);
- [ExpectedWarning ("IL2026")]
- [ExpectedWarning ("IL3002", ProducedBy = ProducedBy.Analyzer)]
- [ExpectedWarning ("IL3050", ProducedBy = ProducedBy.Analyzer)]
void LocalFunction () => MethodWithRequires ();
}
@@ -842,9 +863,25 @@ public static void TestCallMethodWithRequiresInDynamicallyAccessedLocalFunction
{
typeof (DynamicallyAccessedLocalFunction).RequiresNonPublicMethods ();
- [ExpectedWarning ("IL2026")]
- [ExpectedWarning ("IL3002", ProducedBy = ProducedBy.Analyzer)]
- [ExpectedWarning ("IL3050", ProducedBy = ProducedBy.Analyzer)]
+ LocalFunction ();
+
+ void LocalFunction () => MethodWithRequires ();
+ }
+ }
+
+ class DynamicallyAccessedLocalFunctionUnused
+ {
+ [RequiresUnreferencedCode ("Suppress in body")]
+ [RequiresAssemblyFiles ("Suppress in body")]
+ [RequiresDynamicCode ("Suppress in body")]
+ public static void TestCallMethodWithRequiresInDynamicallyAccessedLocalFunction ()
+ {
+ 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", "--MethodWithRequires--", ProducedBy = ProducedBy.Trimmer)]
void LocalFunction () => MethodWithRequires ();
}
}
@@ -852,9 +889,9 @@ public static void TestCallMethodWithRequiresInDynamicallyAccessedLocalFunction
[ExpectedWarning ("IL2026")]
[ExpectedWarning ("IL3002", ProducedBy = ProducedBy.Analyzer)]
[ExpectedWarning ("IL3050", ProducedBy = ProducedBy.Analyzer)]
- static void TestSuppressionLocalFunction ()
+ static void TestSuppressionOnLocalFunction ()
{
- LocalFunction (); // This will produce a warning since the location function has Requires on it
+ LocalFunction (); // This will produce a warning since the local function has Requires on it
[RequiresUnreferencedCode ("Suppress in body")]
[RequiresAssemblyFiles ("Suppress in body")]
@@ -866,6 +903,27 @@ void LocalFunction (Type unknownType = null)
}
}
+ [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
+
+ [RequiresUnreferencedCode ("Suppress in body")]
+ [RequiresAssemblyFiles ("Suppress in body")]
+ [RequiresDynamicCode ("Suppress in body")]
+ void LocalFunction ()
+ {
+ NestedLocalFunction ();
+
+ // The linker doesn't have enough information to associate the Requires on LocalFunction
+ // with this nested local function.
+ [ExpectedWarning ("IL2026", ProducedBy = ProducedBy.Trimmer)]
+ void NestedLocalFunction () => MethodWithRequires ();
+ }
+ }
+
[RequiresUnreferencedCode ("Suppress in body")]
[RequiresAssemblyFiles ("Suppress in body")]
[RequiresDynamicCode ("Suppress in body")]
@@ -883,12 +941,45 @@ void LocalFunction (Type unknownType = null)
}
}
+ class TestSuppressionOnOuterWithSameName
+ {
+ [ExpectedWarning ("IL2026", nameof (Outer) + "()")]
+ [ExpectedWarning ("IL3002", nameof (Outer) + "()", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3050", nameof (Outer) + "()", ProducedBy = ProducedBy.Analyzer)]
+ public static void Test ()
+ {
+ Outer ();
+ Outer (0);
+ }
+
+ [RequiresUnreferencedCode ("Suppress in body")]
+ [RequiresAssemblyFiles ("Suppress in body")]
+ [RequiresDynamicCode ("Suppress in body")]
+ static void Outer ()
+ {
+ // Even though this method has the same name as Outer(int i),
+ // it should not suppress warnings originating from compiler-generated
+ // code for the lambda contained in Outer(int i).
+ }
+
+ static void Outer (int i)
+ {
+ LocalFunction ();
+
+ [ExpectedWarning ("IL2026", "--MethodWithRequires--")]
+ [ExpectedWarning ("IL3002", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3050", ProducedBy = ProducedBy.Analyzer)]
+ void LocalFunction () => MethodWithRequires ();
+ }
+ }
+
[UnconditionalSuppressMessage ("Trimming", "IL2026")]
[UnconditionalSuppressMessage ("SingleFile", "IL3002")]
[UnconditionalSuppressMessage ("AOT", "IL3050")]
public static void Test ()
{
TestCall ();
+ TestCallFromNestedLocalFunction ();
TestCallWithClosure ();
TestReflectionAccess ();
TestLdftn ();
@@ -903,14 +994,56 @@ public static void Test ()
TestGenericLocalFunctionWithAnnotationsAndClosure ();
TestCallMethodWithRequiresInLtftnLocalFunction ();
DynamicallyAccessedLocalFunction.TestCallMethodWithRequiresInDynamicallyAccessedLocalFunction ();
- TestSuppressionLocalFunction ();
+ DynamicallyAccessedLocalFunctionUnused.TestCallMethodWithRequiresInDynamicallyAccessedLocalFunction ();
+ TestSuppressionOnLocalFunction ();
+ TestSuppressionOnLocalFunctionWithNestedLocalFunction ();
TestSuppressionOnOuterAndLocalFunction ();
+ TestSuppressionOnOuterWithSameName.Test ();
}
}
+ static void WarnInNonNestedLocalFunctionTest ()
+ {
+ LocalFunction ();
+
+ [ExpectedWarning ("IL2026", "--MethodWithRequires--")]
+ [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ static void LocalFunction () => MethodWithRequires ();
+ }
+
+ [ExpectedWarning ("IL2026", "--MethodWithNonNestedLocalFunction--")]
+ [ExpectedWarning ("IL3002", "--MethodWithNonNestedLocalFunction--", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3050", "--MethodWithNonNestedLocalFunction--", ProducedBy = ProducedBy.Analyzer)]
+ static void SuppressInNonNestedLocalFunctionTest ()
+ {
+ MethodWithNonNestedLocalFunction ();
+ }
+
+ [RequiresUnreferencedCode ("--MethodWithNonNestedLocalFunction--")]
+ [RequiresAssemblyFiles ("--MethodWithNonNestedLocalFunction--")]
+ [RequiresDynamicCode ("--MethodWithNonNestedLocalFunction--")]
+ static void MethodWithNonNestedLocalFunction ()
+ {
+ LocalFunction ();
+
+ static void LocalFunction () => MethodWithRequires ();
+ }
+
class WarnInLambda
{
static void TestCall ()
+ {
+ Action lambda =
+ [ExpectedWarning ("IL2026", "--MethodWithRequires--")]
+ [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ () => MethodWithRequires ();
+
+ lambda ();
+ }
+
+ static void TestCallUnused ()
{
Action _ =
[ExpectedWarning ("IL2026", "--MethodWithRequires--")]
@@ -920,6 +1053,20 @@ static void TestCall ()
}
static void TestCallWithClosure (int p = 0)
+ {
+ Action lambda =
+ [ExpectedWarning ("IL2026", "--MethodWithRequires--")]
+ [ExpectedWarning ("IL3002", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3050", "--MethodWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ () => {
+ p++;
+ MethodWithRequires ();
+ };
+
+ lambda ();
+ }
+
+ static void TestCallWithClosureUnused (int p = 0)
{
Action _ =
[ExpectedWarning ("IL2026", "--MethodWithRequires--")]
@@ -977,7 +1124,9 @@ static void TestDynamicallyAccessedMethod ()
public static void Test ()
{
TestCall ();
+ TestCallUnused ();
TestCallWithClosure ();
+ TestCallWithClosureUnused ();
TestReflectionAccess ();
TestLdftn ();
TestLazyDelegate ();
@@ -987,24 +1136,25 @@ public static void Test ()
class SuppressInLambda
{
- // Bug https://github.com/dotnet/linker/issues/2001
- // Requires should propagate into lambdas
-
- // C# 10 allows attributes on lambdas
- // - This would be useful as a workaround for the limitation as Requires could be applied to the lambda directly
-
[RequiresUnreferencedCode ("Suppress in body")]
[RequiresAssemblyFiles ("Suppress in body")]
[RequiresDynamicCode ("Suppress in body")]
static void TestCall ()
{
Action _ =
- [ExpectedWarning ("IL2026")]
- [ExpectedWarning ("IL3002", ProducedBy = ProducedBy.Analyzer)]
- [ExpectedWarning ("IL3050", ProducedBy = ProducedBy.Analyzer)]
() => MethodWithRequires ();
}
+ [RequiresUnreferencedCode ("Suppress in body")]
+ [RequiresAssemblyFiles ("Suppress in body")]
+ [RequiresDynamicCode ("Suppress in body")]
+ static void TestCallFromNestedLambda ()
+ {
+ Action lambda = () => {
+ Action nestedLambda = () => MethodWithRequires ();
+ };
+ }
+
[RequiresUnreferencedCode ("Suppress in body")]
[RequiresAssemblyFiles ("Suppress in body")]
[RequiresDynamicCode ("Suppress in body")]
@@ -1012,7 +1162,6 @@ static void TestCallWithReflectionAnalysisWarning ()
{
// This should not produce warning because the Requires
Action _ =
- [ExpectedWarning ("IL2067", ProducedBy = ProducedBy.Trimmer)]
(t) => t.RequiresPublicMethods ();
}
@@ -1022,9 +1171,6 @@ static void TestCallWithReflectionAnalysisWarning ()
static void TestCallWithClosure (int p = 0)
{
Action _ =
- [ExpectedWarning ("IL2026")]
- [ExpectedWarning ("IL3002", ProducedBy = ProducedBy.Analyzer)]
- [ExpectedWarning ("IL3050", ProducedBy = ProducedBy.Analyzer)]
() => {
p++;
MethodWithRequires ();
@@ -1037,8 +1183,6 @@ static void TestCallWithClosure (int p = 0)
static void TestReflectionAccess ()
{
Action _ =
- // Analyzer doesn't recognize reflection access - so doesn't warn in this case
- [ExpectedWarning ("IL2026", ProducedBy = ProducedBy.Trimmer)]
() => {
typeof (RequiresInCompilerGeneratedCode)
.GetMethod ("MethodWithRequires", System.Reflection.BindingFlags.NonPublic)
@@ -1052,9 +1196,6 @@ static void TestReflectionAccess ()
static void TestLdftn ()
{
Action _ =
- [ExpectedWarning ("IL2026")]
- [ExpectedWarning ("IL3002", ProducedBy = ProducedBy.Analyzer)]
- [ExpectedWarning ("IL3050", ProducedBy = ProducedBy.Analyzer)]
() => {
var action = new Action (MethodWithRequires);
};
@@ -1078,8 +1219,6 @@ static void TestLazyDelegate ()
static void TestDynamicallyAccessedMethod ()
{
Action _ =
- // Analyzer doesn't apply DAM - so won't see this warnings
- [ExpectedWarning ("IL2026", ProducedBy = ProducedBy.Trimmer)]
() => {
typeof (TypeWithMethodWithRequires).RequiresNonPublicMethods ();
};
@@ -1091,26 +1230,20 @@ static void TestDynamicallyAccessedMethod ()
static async void TestMethodParameterWithRequirements (Type unknownType = null)
{
Action _ =
- // TODO: Fix the discrepancy between linker and analyzer
- // https://github.com/dotnet/linker/issues/2350
- [ExpectedWarning ("IL2077", ProducedBy = ProducedBy.Trimmer)]
() => unknownType.RequiresNonPublicMethods ();
}
- // The warning is currently not detected by roslyn analyzer since it doesn't analyze DAM yet
- [ExpectedWarning ("IL2091", CompilerGeneratedCode = true, ProducedBy = ProducedBy.Trimmer)]
[RequiresUnreferencedCode ("Suppress in body")]
[RequiresAssemblyFiles ("Suppress in body")]
[RequiresDynamicCode ("Suppress in body")]
static void TestGenericMethodParameterRequirement ()
{
- Action _ = () => {
+ Action _ =
+ () => {
MethodWithGenericWhichRequiresMethods ();
};
}
- // The warning is currently not detected by roslyn analyzer since it doesn't analyze DAM yet
- [ExpectedWarning ("IL2091", CompilerGeneratedCode = true, ProducedBy = ProducedBy.Trimmer)]
[RequiresUnreferencedCode ("Suppress in body")]
[RequiresAssemblyFiles ("Suppress in body")]
[RequiresDynamicCode ("Suppress in body")]
@@ -1121,12 +1254,100 @@ static void TestGenericTypeParameterRequirement ()
};
}
+ [ExpectedWarning ("IL2026")]
+ [ExpectedWarning ("IL3002", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3050", ProducedBy = ProducedBy.Analyzer)]
+ static void TestSuppressionOnLambda ()
+ {
+ var lambda =
+ [RequiresUnreferencedCode ("Suppress in body")]
+ [RequiresAssemblyFiles ("Suppress in body")]
+ [RequiresDynamicCode ("Suppress in body")]
+ () => MethodWithRequires ();
+
+ lambda (); // This will produce a warning since the lambda has Requires on it
+ }
+
+ [ExpectedWarning ("IL2026")]
+ [ExpectedWarning ("IL3002", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3050", ProducedBy = ProducedBy.Analyzer)]
+ static void TestSuppressionOnLambdaWithNestedLambda ()
+ {
+ var lambda =
+ [RequiresUnreferencedCode ("Suppress in body")]
+ [RequiresAssemblyFiles ("Suppress in body")]
+ [RequiresDynamicCode ("Suppress in body")]
+ () => {
+ // The linker doesn't try to associate the Requires on lambda with this nested
+ // lambda. It would be possible to do this because the declaration site will contain
+ // an IL reference to the generated lambda method, unlike local functions.
+ // However, we don't make this association, for consistency with local functions.
+ var nestedLambda =
+ [ExpectedWarning ("IL2026", ProducedBy = ProducedBy.Trimmer)]
+ () => MethodWithRequires ();
+ };
+
+ lambda (); // This will produce a warning since the lambda has Requires on it
+ }
+
+ [RequiresUnreferencedCode ("Suppress in body")]
+ [RequiresAssemblyFiles ("Suppress in body")]
+ [RequiresDynamicCode ("Suppress in body")]
+ static void TestSuppressionOnOuterAndLambda ()
+ {
+ var lambda =
+ [RequiresUnreferencedCode ("Suppress in body")]
+ [RequiresAssemblyFiles ("Suppress in body")]
+ [RequiresDynamicCode ("Suppress in body")]
+ (Type unknownType) => {
+ MethodWithRequires ();
+ unknownType.RequiresNonPublicMethods ();
+ };
+
+ lambda (null);
+ }
+
+ class TestSuppressionOnOuterWithSameName
+ {
+ [ExpectedWarning ("IL2026", nameof (Outer) + "()")]
+ [ExpectedWarning ("IL3002", nameof (Outer) + "()", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3050", nameof (Outer) + "()", ProducedBy = ProducedBy.Analyzer)]
+ public static void Test ()
+ {
+ Outer ();
+ Outer (0);
+ }
+
+ [RequiresUnreferencedCode ("Suppress in body")]
+ [RequiresAssemblyFiles ("Suppress in body")]
+ [RequiresDynamicCode ("Suppress in body")]
+ static void Outer ()
+ {
+ // Even though this method has the same name as Outer(int i),
+ // it should not suppress warnings originating from compiler-generated
+ // code for the lambda contained in Outer(int i).
+ }
+
+ static void Outer (int i)
+ {
+ var lambda =
+ [ExpectedWarning ("IL2026", "--MethodWithRequires--")]
+ [ExpectedWarning ("IL3002", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3050", ProducedBy = ProducedBy.Analyzer)]
+ () => MethodWithRequires ();
+
+ lambda ();
+ }
+ }
+
+
[UnconditionalSuppressMessage ("Trimming", "IL2026")]
[UnconditionalSuppressMessage ("SingleFile", "IL3002")]
[UnconditionalSuppressMessage ("AOT", "IL3050")]
public static void Test ()
{
TestCall ();
+ TestCallFromNestedLambda ();
TestCallWithReflectionAnalysisWarning ();
TestCallWithClosure ();
TestReflectionAccess ();
@@ -1136,6 +1357,10 @@ public static void Test ()
TestMethodParameterWithRequirements ();
TestGenericMethodParameterRequirement ();
TestGenericTypeParameterRequirement ();
+ TestSuppressionOnLambda ();
+ TestSuppressionOnLambdaWithNestedLambda ();
+ TestSuppressionOnOuterAndLambda ();
+ TestSuppressionOnOuterWithSameName.Test ();
}
}
@@ -1174,6 +1399,128 @@ public static void Test ()
class SuppressInComplex
{
+
+ [RequiresUnreferencedCode ("Suppress in body")]
+ [RequiresAssemblyFiles ("Suppress in body")]
+ [RequiresDynamicCode ("Suppress in body")]
+ static void TestIteratorLocalFunction ()
+ {
+ LocalFunction ();
+
+ IEnumerable LocalFunction ()
+ {
+ yield return 0;
+ MethodWithRequires ();
+ yield return 1;
+ }
+ }
+
+ [RequiresUnreferencedCode ("Suppress in body")]
+ [RequiresAssemblyFiles ("Suppress in body")]
+ [RequiresDynamicCode ("Suppress in body")]
+ static void TestAsyncLocalFunction ()
+ {
+ LocalFunction ();
+
+ async Task LocalFunction ()
+ {
+ await MethodAsync ();
+ MethodWithRequires ();
+ return 1;
+ }
+ }
+
+ [RequiresUnreferencedCode ("Suppress in body")]
+ [RequiresAssemblyFiles ("Suppress in body")]
+ [RequiresDynamicCode ("Suppress in body")]
+ static void TestIteratorLocalFunctionWithClosure (int p = 0)
+ {
+ LocalFunction ();
+
+ IEnumerable LocalFunction ()
+ {
+ p++;
+ yield return 0;
+ MethodWithRequires ();
+ yield return 1;
+ }
+ }
+
+ [RequiresUnreferencedCode ("Suppress in body")]
+ [RequiresAssemblyFiles ("Suppress in body")]
+ [RequiresDynamicCode ("Suppress in body")]
+ static void TestAsyncLocalFunctionWithClosure (int p = 0)
+ {
+ LocalFunction ();
+
+ async Task LocalFunction ()
+ {
+ p++;
+ await MethodAsync ();
+ MethodWithRequires ();
+ return 1;
+ }
+ }
+
+ [RequiresUnreferencedCode ("Suppress in body")]
+ [RequiresAssemblyFiles ("Suppress in body")]
+ [RequiresDynamicCode ("Suppress in body")]
+ static void TestCallToLocalFunctionInIteratorLocalFunctionWithClosure (int p = 0)
+ {
+ LocalFunction ();
+
+ IEnumerable LocalFunction ()
+ {
+ p++;
+ yield return 0;
+ LocalFunction2 ();
+ yield return 1;
+
+ void LocalFunction2 ()
+ {
+ MethodWithRequires ();
+ }
+ }
+ }
+
+ [RequiresUnreferencedCode ("Suppress in body")]
+ [RequiresAssemblyFiles ("Suppress in body")]
+ [RequiresDynamicCode ("Suppress in body")]
+ static void TestAsyncLambda ()
+ {
+ Func> _ = async Task () => {
+ await MethodAsync ();
+ MethodWithRequires ();
+ return 1;
+ };
+ }
+
+ [RequiresUnreferencedCode ("Suppress in body")]
+ [RequiresAssemblyFiles ("Suppress in body")]
+ [RequiresDynamicCode ("Suppress in body")]
+ static void TestAsyncLambdaWithClosure (int p = 0)
+ {
+ Func> _ = async Task () => {
+ p++;
+ await MethodAsync ();
+ MethodWithRequires ();
+ return 1;
+ };
+ }
+
+ [RequiresUnreferencedCode ("Suppress in body")]
+ [RequiresAssemblyFiles ("Suppress in body")]
+ [RequiresDynamicCode ("Suppress in body")]
+ static void TestLambdaInAsyncLambdaWithClosure (int p = 0)
+ {
+ Func> _ = async Task () => {
+ p++;
+ await MethodAsync ();
+ var lambda = () => MethodWithRequires ();
+ return 1;
+ };
+ }
+
[RequiresUnreferencedCode ("Suppress in body")]
[RequiresAssemblyFiles ("Suppress in body")]
[RequiresDynamicCode ("Suppress in body")]
@@ -1203,9 +1550,6 @@ static async void TestIteratorLocalFunctionInAsyncWithoutInner ()
LocalFunction ();
await MethodAsync ();
- [ExpectedWarning ("IL2026", CompilerGeneratedCode = true)]
- [ExpectedWarning ("IL3002", ProducedBy = ProducedBy.Analyzer)]
- [ExpectedWarning ("IL3050", ProducedBy = ProducedBy.Analyzer)]
IEnumerable LocalFunction ()
{
yield return 0;
@@ -1228,6 +1572,14 @@ static IEnumerable TestDynamicallyAccessedMethodViaGenericMethodParameterIn
[UnconditionalSuppressMessage ("AOT", "IL3050")]
public static void Test ()
{
+ TestIteratorLocalFunction ();
+ TestAsyncLocalFunction ();
+ TestIteratorLocalFunctionWithClosure ();
+ TestAsyncLocalFunctionWithClosure ();
+ TestCallToLocalFunctionInIteratorLocalFunctionWithClosure ();
+ TestAsyncLambda ();
+ TestAsyncLambdaWithClosure ();
+ TestLambdaInAsyncLambdaWithClosure ();
TestIteratorLocalFunctionInAsync ();
TestIteratorLocalFunctionInAsyncWithoutInner ();
TestDynamicallyAccessedMethodViaGenericMethodParameterInIterator ();
@@ -1274,13 +1626,206 @@ static async void TestAsyncOnlyReferencedViaReflectionWhichShouldWarn ()
[ExpectedWarning ("IL2026", "Requires to suppress")]
[ExpectedWarning ("IL2026", "Requires to suppress")]
- public static void Test ()
+ // Analyzer doesn't emit additional warnings about reflection access to the compiler-generated
+ // state machine members.
+ [ExpectedWarning ("IL2026", "Requires to suppress", ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2026", "Requires to suppress", ProducedBy = ProducedBy.Trimmer)]
+ static void TestAll ()
{
- // This is not a 100% reliable test, since in theory it can be marked in any order and so it could happen that the
- // user method is marked before the nested state machine gets marked. But it's the best we can do right now.
- // (Note that currently linker will mark the state machine first actually so the test is effective).
typeof (StateMachinesOnlyReferencedViaReflection).RequiresAll ();
}
+
+ [ExpectedWarning ("IL2026", "Requires to suppress")]
+ [ExpectedWarning ("IL2026", "Requires to suppress")]
+ // NonPublicMethods doesn't warn for members emitted into compiler-generated state machine types.
+ static void TestNonPublicMethods ()
+ {
+ typeof (StateMachinesOnlyReferencedViaReflection).RequiresNonPublicMethods ();
+ }
+
+ public static void Test ()
+ {
+ TestAll ();
+ TestNonPublicMethods ();
+ }
+ }
+
+ class LocalFunctionsReferencedViaReflection
+ {
+ [ExpectedWarning ("IL2026", "--TestLocalFunctionWithRequires--")]
+ [ExpectedWarning ("IL3002", "--TestLocalFunctionWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3050", "--TestLocalFunctionWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ static void TestLocalFunctionWithRequires ()
+ {
+ LocalFunction ();
+
+ [RequiresUnreferencedCode ("--TestLocalFunctionWithRequires--")]
+ [RequiresAssemblyFiles ("--TestLocalFunctionWithRequires--")]
+ [RequiresDynamicCode ("--TestLocalFunctionWithRequires--")]
+ void LocalFunction () => MethodWithRequires ();
+ }
+
+ [ExpectedWarning ("IL2026", "LocalFunction")]
+ [ExpectedWarning ("IL3002", "LocalFunction", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3050", "LocalFunction", ProducedBy = ProducedBy.Analyzer)]
+ static void TestLocalFunctionWithClosureWithRequires (int p = 0)
+ {
+ LocalFunction ();
+
+ [RequiresUnreferencedCode ("--TestLocalFunctionWithClosureWithRequires--")]
+ [RequiresAssemblyFiles ("--TestLocalFunctionWithClosureWithRequires--")]
+ [RequiresDynamicCode ("--TestLocalFunctionWithClosureWithRequires--")]
+ void LocalFunction ()
+ {
+ p++;
+ MethodWithRequires ();
+ }
+ }
+
+ [RequiresUnreferencedCode ("--TestLocalFunctionInMethodWithRequires--")]
+ [RequiresAssemblyFiles ("--TestLocalFunctionInMethodWithRequires--")]
+ [RequiresDynamicCode ("--TestLocalFunctionInMethodWithRequires--")]
+ static void TestLocalFunctionInMethodWithRequires ()
+ {
+ LocalFunction ();
+
+ void LocalFunction () => MethodWithRequires ();
+ }
+
+ [RequiresUnreferencedCode ("--TestLocalFunctionWithClosureInMethodWithRequires--")]
+ [RequiresAssemblyFiles ("--TestLocalFunctionWithClosureInMethodWithRequires--")]
+ [RequiresDynamicCode ("--TestLocalFunctionWithClosureInMethodWithRequires--")]
+ static void TestLocalFunctionWithClosureInMethodWithRequires (int p = 0)
+ {
+ LocalFunction ();
+
+ void LocalFunction ()
+ {
+ p++;
+ MethodWithRequires ();
+ }
+ }
+
+ // Warnings for Reflection access to methods with Requires
+ [ExpectedWarning ("IL2026", "--TestLocalFunctionInMethodWithRequires--")]
+ [ExpectedWarning ("IL2026", "--TestLocalFunctionWithClosureInMethodWithRequires--")]
+ // 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", "--TestLocalFunctionWithClosureWithRequires--", ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2026", "--TestLocalFunctionInMethodWithRequires--", ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2026", "--TestLocalFunctionWithClosureInMethodWithRequires--", ProducedBy = ProducedBy.Trimmer)]
+ static void TestAll ()
+ {
+ typeof (LocalFunctionsReferencedViaReflection).RequiresAll ();
+ }
+
+ // Warnings for Reflection access to methods with Requires
+ [ExpectedWarning ("IL2026", "--TestLocalFunctionInMethodWithRequires--")]
+ [ExpectedWarning ("IL2026", "--TestLocalFunctionWithClosureInMethodWithRequires--")]
+ // NonPublicMethods warns for local functions not emitted into display classes.
+ [ExpectedWarning ("IL2026", "--TestLocalFunctionWithRequires--", ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2026", "--TestLocalFunctionWithClosureWithRequires--", ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2026", "--TestLocalFunctionInMethodWithRequires--", ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2026", "--TestLocalFunctionWithClosureInMethodWithRequires--", ProducedBy = ProducedBy.Trimmer)]
+ static void TestNonPublicMethods ()
+ {
+ typeof (LocalFunctionsReferencedViaReflection).RequiresNonPublicMethods ();
+ }
+
+ public static void Test ()
+ {
+ TestAll ();
+ TestNonPublicMethods ();
+ }
+ }
+
+ class LambdasReferencedViaReflection
+ {
+ [ExpectedWarning ("IL2026", "--TestLambdaWithRequires--")]
+ [ExpectedWarning ("IL3002", "--TestLambdaWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3050", "--TestLambdaWithRequires--", ProducedBy = ProducedBy.Analyzer)]
+ static void TestLambdaWithRequires ()
+ {
+ var lambda =
+ [RequiresUnreferencedCode ("--TestLambdaWithRequires--")]
+ [RequiresAssemblyFiles ("--TestLambdaWithRequires--")]
+ [RequiresDynamicCode ("--TestLambdaWithRequires--")]
+ () => MethodWithRequires ();
+
+ lambda ();
+ }
+
+ [ExpectedWarning ("IL2026", "Lambda")]
+ [ExpectedWarning ("IL3002", "Lambda", ProducedBy = ProducedBy.Analyzer)]
+ [ExpectedWarning ("IL3050", "Lambda", ProducedBy = ProducedBy.Analyzer)]
+ static void TestLambdaWithClosureWithRequires (int p = 0)
+ {
+ var lambda =
+ [RequiresUnreferencedCode ("--TestLambdaWithClosureWithRequires--")]
+ [RequiresAssemblyFiles ("--TestLambdaWithClosureWithRequires--")]
+ [RequiresDynamicCode ("--TestLambdaWithClosureWithRequires--")]
+ () => {
+ p++;
+ MethodWithRequires ();
+ };
+
+ lambda ();
+ }
+
+ [RequiresUnreferencedCode ("--TestLambdaInMethodWithRequires--")]
+ [RequiresAssemblyFiles ("--TestLambdaInMethodWithRequires--")]
+ [RequiresDynamicCode ("--TestLambdaInMethodWithRequires--")]
+ static void TestLambdaInMethodWithRequires ()
+ {
+ var lambda = () => MethodWithRequires ();
+
+ lambda ();
+ }
+
+ [RequiresUnreferencedCode ("--TestLambdaWithClosureInMethodWithRequires--")]
+ [RequiresAssemblyFiles ("--TestLambdaWithClosureInMethodWithRequires--")]
+ [RequiresDynamicCode ("--TestLambdaWithClosureInMethodWithRequires--")]
+ static void TestLambdaWithClosureInMethodWithRequires (int p = 0)
+ {
+ var lambda = () => {
+ p++;
+ MethodWithRequires ();
+ };
+
+ lambda ();
+ }
+
+ // Warnings for Reflection access to methods with Requires
+ [ExpectedWarning ("IL2026", "--TestLambdaInMethodWithRequires--")]
+ [ExpectedWarning ("IL2026", "--TestLambdaWithClosureInMethodWithRequires--")]
+ // The linker correctly emits warnings about reflection access to lambdas with Requires
+ // or which inherit Requires from the containing method. The analyzer doesn't bind to lambdas
+ // so does not warn here.
+ [ExpectedWarning ("IL2026", "--TestLambdaWithRequires--", ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2026", "--TestLambdaWithClosureWithRequires--", ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2026", "--TestLambdaInMethodWithRequires--", ProducedBy = ProducedBy.Trimmer)]
+ [ExpectedWarning ("IL2026", "--TestLambdaWithClosureInMethodWithRequires--", ProducedBy = ProducedBy.Trimmer)]
+ static void TestAll ()
+ {
+ typeof (LambdasReferencedViaReflection).RequiresAll ();
+ }
+
+ // Warnings for Reflection access to methods with Requires
+ [ExpectedWarning ("IL2026", "--TestLambdaInMethodWithRequires--")]
+ [ExpectedWarning ("IL2026", "--TestLambdaWithClosureInMethodWithRequires--")]
+ // NonPublicMethods doesn't warn for lambdas emitted into display class types.
+ static void TestNonPublicMethods ()
+ {
+ typeof (LambdasReferencedViaReflection).RequiresNonPublicMethods ();
+ }
+
+ public static void Test ()
+ {
+ TestAll ();
+ TestNonPublicMethods ();
+ }
}
class ComplexCases
diff --git a/test/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInCompilerGeneratedCode.cs b/test/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInCompilerGeneratedCode.cs
index c57dc3afd4ce..29fb45818112 100644
--- a/test/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInCompilerGeneratedCode.cs
+++ b/test/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInCompilerGeneratedCode.cs
@@ -1,4 +1,4 @@
-// Licensed to the .NET Foundation under one or more agreements.
+// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
@@ -158,14 +158,17 @@ public static void Test ()
class SuppressInLocalFunction
{
- // Suppression currently doesn't propagate to local functions
-
[UnconditionalSuppressMessage ("Test", "IL2026")]
static void TestCallRUCMethod ()
{
LocalFunction ();
- [ExpectedWarning ("IL2026", ProducedBy = ProducedBy.Trimmer)]
+ void LocalFunction () => RequiresUnreferencedCodeMethod ();
+ }
+
+ [UnconditionalSuppressMessage ("Test", "IL2026")]
+ static void TestCallRUCMethodUnused ()
+ {
void LocalFunction () => RequiresUnreferencedCodeMethod ();
}
@@ -174,7 +177,6 @@ static void TestReflectionAccessRUCMethod ()
{
LocalFunction ();
- [ExpectedWarning ("IL2026", ProducedBy = ProducedBy.Trimmer)]
void LocalFunction () => typeof (SuppressWarningsInCompilerGeneratedCode)
.GetMethod ("RequiresUnreferencedCodeMethod", System.Reflection.BindingFlags.NonPublic)
.Invoke (null, new object[] { });
@@ -185,7 +187,6 @@ static void TestLdftnOnRUCMethod ()
{
LocalFunction ();
- [ExpectedWarning ("IL2026", ProducedBy = ProducedBy.Trimmer)]
void LocalFunction ()
{ var _ = new Action (RequiresUnreferencedCodeMethod); }
}
@@ -195,7 +196,6 @@ static void TestDynamicallyAccessedMethod ()
{
LocalFunction ();
- [ExpectedWarning ("IL2026", ProducedBy = ProducedBy.Trimmer)]
void LocalFunction () => typeof (TypeWithRUCMethod).RequiresNonPublicMethods ();
}
@@ -204,7 +204,6 @@ static void TestMethodParameterWithRequirements (Type unknownType = null)
{
LocalFunction ();
- [ExpectedWarning ("IL2077", ProducedBy = ProducedBy.Trimmer)]
void LocalFunction () => unknownType.RequiresNonPublicMethods ();
}
@@ -213,7 +212,6 @@ static void TestGenericMethodParameterRequirement ()
{
LocalFunction ();
- [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer)]
void LocalFunction () => MethodWithGenericWhichRequiresMethods ();
}
@@ -222,7 +220,6 @@ static void TestGenericTypeParameterRequirement ()
{
LocalFunction ();
- [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer)]
void LocalFunction () => new TypeWithGenericWhichRequiresNonPublicFields ();
}
@@ -242,8 +239,6 @@ static void TestGenericLocalFunctionInner ()
{
LocalFunction ();
- [ExpectedWarning ("IL2087", ProducedBy = ProducedBy.Trimmer)]
- [ExpectedWarning ("IL2087", ProducedBy = ProducedBy.Trimmer)]
void LocalFunction ()
{
typeof (TUnknown).RequiresPublicMethods ();
@@ -279,7 +274,6 @@ static void TestCallRUCMethodInLtftnLocalFunction ()
{
var _ = new Action (LocalFunction);
- [ExpectedWarning ("IL2026", ProducedBy = ProducedBy.Trimmer)]
void LocalFunction () => RequiresUnreferencedCodeMethod ();
}
@@ -290,7 +284,23 @@ public static void TestCallRUCMethodInDynamicallyAccessedLocalFunction ()
{
typeof (DynamicallyAccessedLocalFunction).RequiresNonPublicMethods ();
- [ExpectedWarning ("IL2026", ProducedBy = ProducedBy.Trimmer)]
+ LocalFunction ();
+
+ void LocalFunction () => RequiresUnreferencedCodeMethod ();
+ }
+ }
+
+ 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 ();
}
}
@@ -311,7 +321,6 @@ static void TestSuppressionOnOuterAndLocalFunction ()
{
LocalFunction ();
- [ExpectedWarning ("IL2067", ProducedBy = ProducedBy.Trimmer)]
[UnconditionalSuppressMessage ("Test", "IL2026")] // This supresses the RequiresUnreferencedCodeMethod
void LocalFunction (Type unknownType = null)
{
@@ -320,9 +329,35 @@ void LocalFunction (Type unknownType = null)
}
}
+ class TestSuppressionOnOuterWithSameName
+ {
+ public static void Test ()
+ {
+ Outer ();
+ Outer (0);
+ }
+
+ [UnconditionalSuppressMessage ("Test", "IL2026")]
+ static void Outer ()
+ {
+ // Even though this method has the same name as Outer(int i),
+ // it should not suppress warnings originating from compiler-generated
+ // code for the lambda contained in Outer(int i).
+ }
+
+ static void Outer (int i)
+ {
+ LocalFunction ();
+
+ [ExpectedWarning ("IL2026", "--RequiresUnreferencedCodeMethod--")]
+ void LocalFunction () => RequiresUnreferencedCodeMethod ();
+ }
+ }
+
public static void Test ()
{
TestCallRUCMethod ();
+ TestCallRUCMethodUnused ();
TestReflectionAccessRUCMethod ();
TestLdftnOnRUCMethod ();
TestDynamicallyAccessedMethod ();
@@ -335,20 +370,28 @@ public static void Test ()
TestGenericLocalFunctionWithAnnotationsAndClosure ();
TestCallRUCMethodInLtftnLocalFunction ();
DynamicallyAccessedLocalFunction.TestCallRUCMethodInDynamicallyAccessedLocalFunction ();
+ DynamicallyAccessedLocalFunctionUnused.TestCallRUCMethodInDynamicallyAccessedLocalFunction ();
TestSuppressionOnLocalFunction ();
TestSuppressionOnOuterAndLocalFunction ();
+ TestSuppressionOnOuterWithSameName.Test ();
}
}
class SuppressInLambda
{
- // Suppression currently doesn't propagate to lambdas
-
[UnconditionalSuppressMessage ("Test", "IL2026")]
static void TestCallRUCMethod ()
+ {
+ Action lambda =
+ () => RequiresUnreferencedCodeMethod ();
+
+ lambda ();
+ }
+
+ [UnconditionalSuppressMessage ("Test", "IL2026")]
+ static void TestCallRUCMethodUnused ()
{
Action _ =
- [ExpectedWarning ("IL2026", ProducedBy = ProducedBy.Trimmer)]
() => RequiresUnreferencedCodeMethod ();
}
@@ -356,7 +399,6 @@ static void TestCallRUCMethod ()
static void TestReflectionAccessRUCMethod ()
{
Action _ =
- [ExpectedWarning ("IL2026", ProducedBy = ProducedBy.Trimmer)]
() => typeof (SuppressWarningsInCompilerGeneratedCode)
.GetMethod ("RequiresUnreferencedCodeMethod", System.Reflection.BindingFlags.NonPublic)
.Invoke (null, new object[] { });
@@ -366,7 +408,6 @@ static void TestReflectionAccessRUCMethod ()
static void TestLdftnOnRUCMethod ()
{
Action _ =
- [ExpectedWarning ("IL2026", ProducedBy = ProducedBy.Trimmer)]
() => { var _ = new Action (RequiresUnreferencedCodeMethod); };
}
@@ -374,7 +415,6 @@ static void TestLdftnOnRUCMethod ()
static void TestDynamicallyAccessedMethod ()
{
Action _ =
- [ExpectedWarning ("IL2026", ProducedBy = ProducedBy.Trimmer)]
() => typeof (TypeWithRUCMethod).RequiresNonPublicMethods ();
}
@@ -382,7 +422,6 @@ static void TestDynamicallyAccessedMethod ()
static void TestMethodParameterWithRequirements (Type unknownType = null)
{
Action _ =
- [ExpectedWarning ("IL2077", ProducedBy = ProducedBy.Trimmer)]
() => unknownType.RequiresNonPublicMethods ();
}
@@ -390,7 +429,6 @@ static void TestMethodParameterWithRequirements (Type unknownType = null)
static void TestGenericMethodParameterRequirement ()
{
Action _ =
- [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer)]
() => MethodWithGenericWhichRequiresMethods ();
}
@@ -398,24 +436,119 @@ static void TestGenericMethodParameterRequirement ()
static void TestGenericTypeParameterRequirement ()
{
Action _ =
- [ExpectedWarning ("IL2091", ProducedBy = ProducedBy.Trimmer)]
() => new TypeWithGenericWhichRequiresNonPublicFields ();
}
+ class DynamicallyAccessedLambda
+ {
+ [UnconditionalSuppressMessage ("Test", "IL2026")]
+ public static void TestCallRUCMethodInDynamicallyAccessedLambda ()
+ {
+ typeof (DynamicallyAccessedLambda).RequiresNonPublicMethods ();
+
+ Action lambda = () => RequiresUnreferencedCodeMethod ();
+
+ lambda ();
+ }
+ }
+
+ class DynamicallyAccessedLambdaUnused
+ {
+ [UnconditionalSuppressMessage ("Test", "IL2026")]
+ public static void TestCallRUCMethodInDynamicallyAccessedLambda ()
+ {
+ typeof (DynamicallyAccessedLambdaUnused).RequiresNonPublicMethods ();
+
+ Action _ = () => RequiresUnreferencedCodeMethod ();
+ }
+ }
+
+ static void TestSuppressionOnLambda ()
+ {
+ var lambda =
+ // https://github.com/dotnet/roslyn/issues/59746
+ [ExpectedWarning ("IL2026", ProducedBy = ProducedBy.Analyzer)]
+ [UnconditionalSuppressMessage ("Test", "IL2026")]
+ () => RequiresUnreferencedCodeMethod ();
+
+ lambda ();
+ }
+
+ [UnconditionalSuppressMessage ("Test", "IL2067")]
+ static void TestSuppressionOnOuterAndLambda ()
+ {
+ var lambda =
+ // https://github.com/dotnet/roslyn/issues/59746
+ [ExpectedWarning ("IL2026", ProducedBy = ProducedBy.Analyzer)]
+ [UnconditionalSuppressMessage ("Test", "IL2026")]
+ (Type unknownType) => {
+ RequiresUnreferencedCodeMethod ();
+ unknownType.RequiresNonPublicMethods ();
+ };
+
+ lambda (null);
+ }
+
+ class TestSuppressionOnOuterWithSameName
+ {
+ public static void Test ()
+ {
+ Outer ();
+ Outer (0);
+ }
+
+ [UnconditionalSuppressMessage ("Test", "IL2026")]
+ static void Outer ()
+ {
+ // Even though this method has the same name as Outer(int i),
+ // it should not suppress warnings originating from compiler-generated
+ // code for the lambda contained in Outer(int i).
+ }
+
+ static void Outer (int i)
+ {
+ var lambda =
+ [ExpectedWarning ("IL2026", "--RequiresUnreferencedCodeMethod--")]
+ () => RequiresUnreferencedCodeMethod ();
+
+ lambda ();
+ }
+ }
+
public static void Test ()
{
TestCallRUCMethod ();
+ TestCallRUCMethodUnused ();
TestReflectionAccessRUCMethod ();
TestLdftnOnRUCMethod ();
TestDynamicallyAccessedMethod ();
TestMethodParameterWithRequirements ();
TestGenericMethodParameterRequirement ();
TestGenericTypeParameterRequirement ();
+ DynamicallyAccessedLambda.TestCallRUCMethodInDynamicallyAccessedLambda ();
+ DynamicallyAccessedLambdaUnused.TestCallRUCMethodInDynamicallyAccessedLambda ();
+ TestSuppressionOnLambda ();
+ TestSuppressionOnOuterAndLambda ();
+ TestSuppressionOnOuterWithSameName.Test ();
+
}
}
class SuppressInComplex
{
+ [UnconditionalSuppressMessage ("Test", "IL2026")]
+ static void TestIteratorLocalFunction ()
+ {
+ LocalFunction ();
+
+ IEnumerable LocalFunction ()
+ {
+ yield return 0;
+ RequiresUnreferencedCodeMethod ();
+ yield return 1;
+ }
+ }
+
[UnconditionalSuppressMessage ("Test", "IL2026")]
static async void TestIteratorLocalFunctionInAsync ()
{
@@ -439,7 +572,6 @@ static async void TestIteratorLocalFunctionInAsyncWithoutInner ()
LocalFunction ();
await MethodAsync ();
- [ExpectedWarning ("IL2026", CompilerGeneratedCode = true, ProducedBy = ProducedBy.Trimmer)]
IEnumerable LocalFunction ()
{
yield return 0;
@@ -457,6 +589,7 @@ static IEnumerable TestDynamicallyAccessedMethodViaGenericMethodParameterIn
public static void Test ()
{
+ TestIteratorLocalFunction ();
TestIteratorLocalFunctionInAsync ();
TestIteratorLocalFunctionInAsyncWithoutInner ();
TestDynamicallyAccessedMethodViaGenericMethodParameterInIterator ();