diff options
author | Sven Boemer <sbomer@gmail.com> | 2022-06-30 00:15:31 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-06-30 00:15:31 +0300 |
commit | bc46e445deb1411cc597019d693ddc5b4e5e24f4 (patch) | |
tree | 332512db149b2a81113a8ef277abfa3082eb09c0 /test/Mono.Linker.Tests.Cases | |
parent | e10038dcd0bff151e3367644eb3cf391e56ead28 (diff) |
Add compiler-generated code dataflow analysis (#2842)
This treats fields of display classes and state machines as hoisted
locals. We track all assignments to hoisted locals within a method
group (the set of compiler-generated code reachable from a given user
method). The analysis is technically flow-insensitive, because it
assumes that any assignment to a hoisted local can reach any read of
the same local. This will produce extra warnings in some cases, but it
prevents holes:
- State will "flow" out of nested functions. That is, writes (to
hoisted locals) within nested functions will reach reads in the
enclosing user method
- Lambdas are analyzed at the point of delegate conversion, but with
all possible states of captured variables. So effectively, writes
after the lambda declaration will reach reads within the lambda.
Previously, hoisted locals were treated as unannotated, so they would
produce dataflow warnings if they reached an annotated location. Now
that we analyze hoisted locals, cases where the value satisfies
requirements at the point of consumption won't warn. This means that
accessing these fields (representing hoisted locals) via reflection is
problematic, since it could mutate the values of these fields and
invalidate the correctness analysis. For this reason we now warn on
reflection access to compiler-generated fields.
To prevent noise, we only warn for reflection access to
compiler-generated fields that represent types which may be
annotated - so Type, string, etc. - but not int. This is technically a
hole because ints also participate in dataflow analysis but we are
choosing this tradeoff to avoid excess warnings for integers.
This also includes some cleanup of the type hierarchy logic and
extra comments to make it more clear how this interacts with
warnings for reflection access to compiler-generated code.
Diffstat (limited to 'test/Mono.Linker.Tests.Cases')
4 files changed, 620 insertions, 41 deletions
diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/CompilerGeneratedCodeAccessedViaReflection.cs b/test/Mono.Linker.Tests.Cases/DataFlow/CompilerGeneratedCodeAccessedViaReflection.cs index 1b5f65c7e..804f2a370 100644 --- a/test/Mono.Linker.Tests.Cases/DataFlow/CompilerGeneratedCodeAccessedViaReflection.cs +++ b/test/Mono.Linker.Tests.Cases/DataFlow/CompilerGeneratedCodeAccessedViaReflection.cs @@ -30,8 +30,6 @@ namespace Mono.Linker.Tests.Cases.DataFlow 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 (); @@ -42,6 +40,8 @@ namespace Mono.Linker.Tests.Cases.DataFlow [ExpectedWarning ("IL2120", "<" + nameof (BaseIteratorWithCorrectDataflow) + ">", "MoveNext", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2120", "<" + nameof (BaseIteratorWithCorrectDataflow) + ">", "<t>", + ProducedBy = ProducedBy.Trimmer)] [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] class IteratorStateMachines : BaseTypeWithIteratorStateMachines { @@ -63,25 +63,33 @@ namespace Mono.Linker.Tests.Cases.DataFlow } [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)] + [ExpectedWarning ("IL2119", "<t_IteratorWithCorrectDataflow>", CompilerGeneratedCode = true)] public static IEnumerable<int> IteratorWithCorrectDataflow () { - var t = GetAll (); + var t_IteratorWithCorrectDataflow = GetAll (); yield return 0; - t.RequiresAll (); + t_IteratorWithCorrectDataflow.RequiresAll (); + } + + [ExpectedWarning ("IL2119", "<" + nameof (IteratorWithIntegerDataflow) + ">", "MoveNext", CompilerGeneratedCode = true)] + [ExpectedWarning ("IL2119", "<types>", CompilerGeneratedCode = true)] + public static IEnumerable<int> IteratorWithIntegerDataflow () + { + int integerLocal = 0; + yield return 0; + var types = new Type[] { GetWithPublicMethods (), GetWithPublicFields () }; + types[integerLocal].RequiresPublicMethods (); } [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, + [ExpectedWarning ("IL2119", "<t_IteratorWithProblematicDataflow>", CompilerGeneratedCode = true)] + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), CompilerGeneratedCode = true, ProducedBy = ProducedBy.Trimmer)] public static IEnumerable<int> IteratorWithProblematicDataflow () { - var t = GetWithPublicMethods (); + var t_IteratorWithProblematicDataflow = GetWithPublicMethods (); yield return 0; - t.RequiresAll (); + t_IteratorWithProblematicDataflow.RequiresAll (); } [ExpectedWarning ("IL2112", nameof (RUCTypeWithIterators) + "()", "--RUCTypeWithIterators--", CompilerGeneratedCode = true)] @@ -121,6 +129,8 @@ namespace Mono.Linker.Tests.Cases.DataFlow ProducedBy = ProducedBy.Trimmer)] [ExpectedWarning ("IL2118", "<" + nameof (IteratorWithCorrectDataflow) + ">", "MoveNext", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2118", "<" + nameof (IteratorWithIntegerDataflow) + ">", "MoveNext", + ProducedBy = ProducedBy.Trimmer)] [ExpectedWarning ("IL2118", "<" + nameof (BaseIteratorWithCorrectDataflow) + ">", "MoveNext", ProducedBy = ProducedBy.Trimmer)] [ExpectedWarning ("IL2026", nameof (RUCTypeWithIterators) + "()", "--RUCTypeWithIterators--")] @@ -134,6 +144,18 @@ namespace Mono.Linker.Tests.Cases.DataFlow // With that, the IL2118 warning should also go away. [ExpectedWarning ("IL2118", "<" + nameof (RUCTypeWithIterators.InstanceIteratorCallsMethodWithRequires) + ">", "MoveNext", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2118", "<" + nameof (IteratorWithCorrectDataflow) + ">", "<t_IteratorWithCorrectDataflow>", + ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2118", "<" + nameof (IteratorWithProblematicDataflow) + ">", "<t_IteratorWithProblematicDataflow>", + ProducedBy = ProducedBy.Trimmer)] + // Technically the access to IteratorWithIntegerDataflow should warn about access to the integer + // field integerLocal, but our heuristics only warn if the field type satisfies the + // "IsTypeInterestingForDatafllow" check. This is likely good enough because in most cases the + // compiler-generated code will have other hoisted fields with types that _are_ interesting for dataflow. + [ExpectedWarning ("IL2118", "<" + nameof (IteratorWithIntegerDataflow) + ">", "<types>", + ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2118", "<" + nameof (BaseIteratorWithCorrectDataflow) + ">", "<t>", + ProducedBy = ProducedBy.Trimmer)] public static void Test (IteratorStateMachines test = null) { typeof (IteratorStateMachines).RequiresAll (); @@ -156,22 +178,18 @@ namespace Mono.Linker.Tests.Cases.DataFlow 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 (); + var t_AsyncWithCorrectDataflow = GetAll (); + t_AsyncWithCorrectDataflow.RequiresAll (); } - // Annotations aren't propagated to hoisted locals: https://github.com/dotnet/linker/issues/2001 - [ExpectedWarning ("IL2077", nameof (DataFlowTypeExtensions.RequiresAll), CompilerGeneratedCode = true, + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), CompilerGeneratedCode = true, ProducedBy = ProducedBy.Trimmer)] public static async Task AsyncWithProblematicDataflow () { - var t = GetWithPublicMethods (); - t.RequiresAll (); + var t_AsyncWithProblematicDataflow = GetWithPublicMethods (); + t_AsyncWithProblematicDataflow.RequiresAll (); } [ExpectedWarning ("IL2118", "<" + nameof (AsyncWithProblematicDataflow) + ">", "MoveNext", @@ -180,6 +198,10 @@ namespace Mono.Linker.Tests.Cases.DataFlow ProducedBy = ProducedBy.Trimmer)] [ExpectedWarning ("IL2118", "<" + nameof (AsyncWithCorrectDataflow) + ">", "MoveNext", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2118", "<" + nameof (AsyncWithCorrectDataflow) + ">", "<t_AsyncWithCorrectDataflow>", + ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2118", "<" + nameof (AsyncWithProblematicDataflow) + ">", "<t_AsyncWithProblematicDataflow>", + ProducedBy = ProducedBy.Trimmer)] public static void Test () { typeof (AsyncStateMachines).RequiresAll (); @@ -202,9 +224,6 @@ namespace Mono.Linker.Tests.Cases.DataFlow 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 (); @@ -212,9 +231,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow 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, + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), CompilerGeneratedCode = true, ProducedBy = ProducedBy.Trimmer)] public static async IAsyncEnumerable<int> AsyncIteratorWithProblematicDataflow () { @@ -229,6 +246,10 @@ namespace Mono.Linker.Tests.Cases.DataFlow ProducedBy = ProducedBy.Trimmer)] [ExpectedWarning ("IL2118", "<" + nameof (AsyncIteratorWithCorrectDataflow) + ">", "MoveNext", ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2118", "<" + nameof (AsyncIteratorWithCorrectDataflow) + ">", "<t>", + ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2118", "<" + nameof (AsyncIteratorWithProblematicDataflow) + ">", "<t>", + ProducedBy = ProducedBy.Trimmer)] public static void Test () { typeof (AsyncIteratorStateMachines).RequiresAll (); @@ -259,7 +280,6 @@ namespace Mono.Linker.Tests.Cases.DataFlow 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)] () => { @@ -302,8 +322,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow 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), + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), ProducedBy = ProducedBy.Trimmer)] () => { t.RequiresAll (); @@ -424,9 +443,6 @@ namespace Mono.Linker.Tests.Cases.DataFlow 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 (); @@ -517,6 +533,9 @@ namespace Mono.Linker.Tests.Cases.DataFlow [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] static Type GetWithPublicMethods () => null; + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] + static Type GetWithPublicFields () => null; + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] static Type GetAll () => null; } diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/CompilerGeneratedCodeDataflow.cs b/test/Mono.Linker.Tests.Cases/DataFlow/CompilerGeneratedCodeDataflow.cs new file mode 100644 index 000000000..f42464e54 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/DataFlow/CompilerGeneratedCodeDataflow.cs @@ -0,0 +1,562 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +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; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + [SkipKeptItemsValidation] + [SetupCompileArgument ("/unsafe")] + [ExpectedNoWarnings] + public class CompilerGeneratedCodeDataflow + { + public static void Main () + { + Iterator.Test (); + Async.Test (); + AsyncIterator.Test (); + LocalFunction.Test (); + Lambda.Test (); + Complex.Test (); + } + + class Iterator + { + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + CompilerGeneratedCode = true)] + static IEnumerable<int> FlowAcrossYieldReturn () + { + Type t = GetWithPublicMethods (); + yield return 0; + t.RequiresAll (); + } + + // Linker tracks all assignments of hoisted locals, so this produces warnings. + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresPublicFields), CompilerGeneratedCode = true)] + [ExpectedWarning ("IL2072", nameof (GetWithPublicFields), nameof (DataFlowTypeExtensions.RequiresPublicMethods), CompilerGeneratedCode = true)] + static IEnumerable<int> NoFlowAcrossYieldReturn () + { + Type t = GetWithPublicMethods (); + t.RequiresPublicMethods (); + yield return 0; + t = GetWithPublicFields (); + t.RequiresPublicFields (); + } + + [ExpectedWarning ("IL2067", "publicMethodsType", nameof (DataFlowTypeExtensions.RequiresAll), CompilerGeneratedCode = true)] + static IEnumerable<int> UseParameterBeforeYieldReturn ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type publicMethodsType = null) + { + publicMethodsType.RequiresAll (); + yield return 0; + } + + [ExpectedWarning ("IL2067", "unknownType", nameof (DataFlowTypeExtensions.RequiresAll), CompilerGeneratedCode = true)] + static IEnumerable<int> UseUnannotatedParameterBeforeYieldReturn (Type unknownType = null) + { + unknownType.RequiresAll (); + yield return 0; + } + + [ExpectedWarning ("IL2067", "publicMethodsType", nameof (DataFlowTypeExtensions.RequiresAll), CompilerGeneratedCode = true)] + static IEnumerable<int> FlowParameterAcrossYieldReturn ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type publicMethodsType = null) + { + yield return 0; + publicMethodsType.RequiresAll (); + } + + [ExpectedWarning ("IL2067", "unknownType", nameof (DataFlowTypeExtensions.RequiresAll), CompilerGeneratedCode = true)] + static IEnumerable<int> FlowUnannotatedParameterAcrossYieldReturn (Type unknownType = null) + { + yield return 0; + unknownType.RequiresAll (); + } + + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), CompilerGeneratedCode = true)] + // Linker includes backwards branches for hoisted locals, by virtue of tracking all assignments. + [ExpectedWarning ("IL2072", nameof (GetWithPublicFields), nameof (DataFlowTypeExtensions.RequiresAll), CompilerGeneratedCode = true)] + static IEnumerable<int> FlowAcrossYieldReturnWithBackwardsBranch (int n = 0) + { + Type t = GetWithPublicMethods (); + for (int i = 0; i < n; i++) { + yield return 0; + t.RequiresAll (); + yield return 1; + t = GetWithPublicFields (); + } + } + + public static void Test () + { + FlowAcrossYieldReturn (); + NoFlowAcrossYieldReturn (); + NoFlowAcrossYieldReturn (); + UseParameterBeforeYieldReturn (); + UseUnannotatedParameterBeforeYieldReturn (); + FlowParameterAcrossYieldReturn (); + FlowUnannotatedParameterAcrossYieldReturn (); + FlowAcrossYieldReturnWithBackwardsBranch (); + } + } + + class Async + { + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + CompilerGeneratedCode = true)] + static async void FlowAcrossAwait () + { + Type t = GetWithPublicMethods (); + await MethodAsync (); + t.RequiresAll (); + } + + // Linker tracks all assignments of hoisted locals, so this produces warnings. + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresPublicFields), CompilerGeneratedCode = true)] + [ExpectedWarning ("IL2072", nameof (GetWithPublicFields), nameof (DataFlowTypeExtensions.RequiresPublicMethods), CompilerGeneratedCode = true)] + static async void NoFlowAcrossAwait () + { + Type t = GetWithPublicMethods (); + t.RequiresPublicMethods (); + await MethodAsync (); + t = GetWithPublicFields (); + t.RequiresPublicFields (); + } + + [ExpectedWarning ("IL2067", "publicMethodsType", nameof (DataFlowTypeExtensions.RequiresAll), CompilerGeneratedCode = true)] + static async void FlowParameterAcrossAwait ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type publicMethodsType = null) + { + await MethodAsync (); + publicMethodsType.RequiresAll (); + } + + public static void Test () + { + FlowAcrossAwait (); + NoFlowAcrossAwait (); + FlowParameterAcrossAwait (); + } + } + + class AsyncIterator + { + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), CompilerGeneratedCode = true, + ProducedBy = ProducedBy.Trimmer)] + static async IAsyncEnumerable<int> FlowAcrossAwaitAndYieldReturn () + { + Type t = GetWithPublicMethods (); + await MethodAsync (); + yield return 0; + t.RequiresAll (); + } + + public static void Test () + { + FlowAcrossAwaitAndYieldReturn (); + } + } + + class LocalFunction + { + static void WarningsInBody () + { + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer)] + static void LocalFunction () + { + Type t = GetWithPublicMethods (); + t.RequiresAll (); + } + + LocalFunction (); + } + + static void WarningsInBodyUnused () + { + // Trimmer doesn't warn because this is unused code. + static void LocalFunction () + { + Type t = GetWithPublicMethods (); + t.RequiresAll (); + } + } + + static void ReadCapturedVariable () + { + Type t = GetWithPublicMethods (); + LocalFunction (); + + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer)] + void LocalFunction () + { + t.RequiresAll (); + } + } + + static void ReadMergedCapturedVariable (bool b = false) + { + Type t; + if (b) { + t = GetWithPublicMethods (); + } else { + t = GetWithPublicFields (); + } + + LocalFunction (); + + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2072", nameof (GetWithPublicFields), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer)] + void LocalFunction () => t.RequiresAll (); + } + + static void ReadCapturedVariableInMultipleBranches (bool b = false) + { + Type t; + if (b) { + t = GetWithPublicMethods (); + LocalFunction (); + } else { + t = GetWithPublicFields (); + LocalFunction (); + } + + LocalFunction (); + + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2072", nameof (GetWithPublicFields), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer)] + void LocalFunction () => t.RequiresAll (); + } + + static void ReadCapturedVariableInMultipleBranchesDistinct (bool b = false) + { + Type t; + if (b) { + t = GetWithPublicMethods (); + LocalFunctionRequiresMethods (); + } else { + t = GetWithPublicFields (); + LocalFunctionRequiresFields (); + } + + // We include all writes, including ones that can't reach the local function invocation. + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresPublicFields), + ProducedBy = ProducedBy.Trimmer)] + void LocalFunctionRequiresFields () => t.RequiresPublicFields (); + // We include all writes, including ones that can't reach the local function invocation. + [ExpectedWarning ("IL2072", nameof (GetWithPublicFields), nameof (DataFlowTypeExtensions.RequiresPublicMethods), + ProducedBy = ProducedBy.Trimmer)] + void LocalFunctionRequiresMethods () => t.RequiresPublicMethods (); + } + + static void ReadCapturedVariableWithBackwardsBranch (int i = 0) + { + Type t = GetWithPublicMethods (); + while (true) { + LocalFunction (); + if (i++ == 5) + break; + t = GetWithPublicFields (); + } + + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer)] + // Linker includes backwards branches for hoisted locals, by virtue of tracking all assignments. + [ExpectedWarning ("IL2072", nameof (GetWithPublicFields), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer)] + void LocalFunction () => t.RequiresAll (); + } + + static void ReadCapturedVariableInMultipleFunctions () + { + Type t = GetWithPublicMethods (); + LocalFunction (); + + CallerOfLocalFunction (); + + void CallerOfLocalFunction () + { + t = GetWithPublicFields (); + LocalFunction (); + } + + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2072", nameof (GetWithPublicFields), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer)] + void LocalFunction () => t.RequiresAll (); + } + + static void ReadCapturedVariableInCallGraphCycle () + { + Type t = GetUnknownType (); + A (); + + void A () + { + t = GetWithPublicMethods (); + LocalFunction (); + B (); + } + + void B () + { + t = GetWithPublicFields (); + LocalFunction (); + A (); + } + + [ExpectedWarning ("IL2072", nameof (GetUnknownType), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2072", nameof (GetWithPublicFields), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer)] + void LocalFunction () => t.RequiresAll (); + } + + public static void ReadCapturedParameter (Type tParameter) + { + LocalFunction (); + + [ExpectedWarning ("IL2067", nameof (ReadCapturedParameter), "tParameter", nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer)] + void LocalFunction () => tParameter.RequiresAll (); + } + + public static void ReadCapturedParameterAfterWrite (Type tParameter) + { + tParameter = GetWithPublicMethods (); + LocalFunction (); + + [ExpectedWarning ("IL2067", "tParameter", nameof (DataFlowTypeExtensions.RequiresPublicMethods), + ProducedBy = ProducedBy.Trimmer)] + void LocalFunction () => tParameter.RequiresPublicMethods (); + } + + [ExpectedWarning ("IL2072", nameof (GetWithPublicFields), nameof (DataFlowTypeExtensions.RequiresAll), + // analyzer clears all local state, but trimmer doesn't + ProducedBy = ProducedBy.Trimmer)] + static void ReadCapturedVariableWithUnhoistedLocals () + { + Type t = GetWithPublicMethods (); + Type notCaptured = GetWithPublicFields (); + LocalFunction (); + notCaptured.RequiresAll (); + + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer)] + void LocalFunction () => t.RequiresAll (); + } + + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer)] + // We include all writes, including ones that can't reach the local function invocation. + [ExpectedWarning ("IL2072", nameof (GetWithPublicFields), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer)] + static void WriteCapturedVariable () + { + Type t = GetWithPublicFields (); + LocalFunction (); + t.RequiresAll (); + + void LocalFunction () => t = GetWithPublicMethods (); + } + + public static void Test () + { + WarningsInBody (); + WarningsInBodyUnused (); + ReadCapturedVariable (); + ReadMergedCapturedVariable (); + ReadCapturedVariableInMultipleBranches (); + ReadCapturedVariableInMultipleBranchesDistinct (); + ReadCapturedVariableInMultipleFunctions (); + ReadCapturedVariableInCallGraphCycle (); + ReadCapturedVariableWithBackwardsBranch (); + ReadCapturedParameter (null); + ReadCapturedParameterAfterWrite (null); + ReadCapturedVariableWithUnhoistedLocals (); + WriteCapturedVariable (); + } + } + + class Lambda + { + static void WarningsInBody () + { + var lambda = + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer)] + () => { + Type t = GetWithPublicMethods (); + t.RequiresAll (); + }; + + lambda (); + } + + static void WarningsInBodyUnused () + { + var lambda = + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer)] + () => { + Type t = GetWithPublicMethods (); + t.RequiresAll (); + }; + } + + static void ReadCapturedVariable () + { + Type t = GetWithPublicMethods (); + Action lambda = + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer)] + () => t.RequiresAll (); + lambda (); + } + + static void ReadCapturedVariableAfterWriteAfterDefinition () + { + Type t = GetWithPublicFields (); + + Action lambda = + [ExpectedWarning ("IL2072", nameof (GetWithPublicFields), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer)] + () => t.RequiresAll (); + + t = GetWithPublicMethods (); + lambda (); + } + + public static void ReadCapturedParameter (Type tParameter) + { + var lambda = + [ExpectedWarning ("IL2067", nameof (ReadCapturedParameter), "tParameter", nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer)] + () => tParameter.RequiresAll (); + + lambda (); + } + + public static void ReadCapturedParameterAfterWrite (Type tParameter) + { + tParameter = GetWithPublicMethods (); + var lambda = + // We produce dataflow warnings for the unknown parameter even though it has been overwritten + // with a value that satisfies the requirement. + [ExpectedWarning ("IL2067", "tParameter", nameof (DataFlowTypeExtensions.RequiresPublicMethods), + ProducedBy = ProducedBy.Trimmer)] + () => tParameter.RequiresPublicMethods (); + lambda (); + } + + [ExpectedWarning ("IL2072", nameof (GetWithPublicFields), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer)] + static void ReadCapturedVariableWithUnhoistedLocals () + { + Type t = GetWithPublicMethods (); + Type notCaptured = GetWithPublicFields (); + Action lambda = + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer)] + () => t.RequiresAll (); + lambda (); + notCaptured.RequiresAll (); + } + + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer)] + // We include all writes, including ones that can't reach the local function invocation. + [ExpectedWarning ("IL2072", nameof (GetWithPublicFields), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer)] + static void WriteCapturedVariable () + { + Type t = GetWithPublicFields (); + Action lambda = () => t = GetWithPublicMethods (); + lambda (); + t.RequiresAll (); + } + + public static void Test () + { + WarningsInBody (); + WarningsInBodyUnused (); + ReadCapturedVariable (); + ReadCapturedVariableAfterWriteAfterDefinition (); + ReadCapturedParameter (null); + ReadCapturedParameterAfterWrite (null); + ReadCapturedVariableWithUnhoistedLocals (); + WriteCapturedVariable (); + } + } + + class Complex + { + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), CompilerGeneratedCode = true, + ProducedBy = ProducedBy.Trimmer)] + // Linker merges branches going forward + [ExpectedWarning ("IL2072", nameof (GetWithPublicFields), nameof (DataFlowTypeExtensions.RequiresAll), CompilerGeneratedCode = true, + ProducedBy = ProducedBy.Trimmer)] + static IEnumerable<int> IteratorWithLocalFunctions () + { + Type t = GetWithPublicMethods (); + LocalFunction (); + + yield return 0; + + LocalFunction (); + t = GetWithPublicFields (); + LocalFunction (); + t.RequiresAll (); + + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2072", nameof (GetWithPublicFields), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer)] + void LocalFunction () => t.RequiresAll (); + } + + public static void Test () + { + IteratorWithLocalFunctions (); + } + } + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] + static Type GetAll () => null; + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + static Type GetWithPublicMethods () => null; + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] + static Type GetWithPublicFields () => null; + + static Type GetUnknownType () => null; + + static async Task<int> MethodAsync () + { + return await Task.FromResult (0); + } + + [RequiresUnreferencedCode ("RUC")] + static void RUCMethod () { } + + struct TestStruct + { + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] + public Type TypeWithPublicFields => null; + } + } +}
\ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/Reflection/ExpressionPropertyMethodInfo.cs b/test/Mono.Linker.Tests.Cases/Reflection/ExpressionPropertyMethodInfo.cs index 91a1b345f..95447aa6a 100644 --- a/test/Mono.Linker.Tests.Cases/Reflection/ExpressionPropertyMethodInfo.cs +++ b/test/Mono.Linker.Tests.Cases/Reflection/ExpressionPropertyMethodInfo.cs @@ -89,11 +89,11 @@ namespace Mono.Linker.Tests.Cases.Reflection [Kept] // https://github.com/dotnet/linker/issues/2669 [ExpectedWarning ("IL2026", nameof (StaticPropertyExpressionAccess), ProducedBy = ProducedBy.Trimmer)] - [ExpectedWarning ("IL2026", nameof (StaticPropertyViaReflection))] - [ExpectedWarning ("IL2026", nameof (StaticPropertyViaRuntimeMethod))] + [ExpectedWarning ("IL2026", nameof (StaticPropertyViaReflection), ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2026", nameof (StaticPropertyViaRuntimeMethod), ProducedBy = ProducedBy.Trimmer)] // https://github.com/dotnet/linker/issues/2669 [ExpectedWarning ("IL2026", nameof (InstancePropertyExpressionAccess), ProducedBy = ProducedBy.Trimmer)] - [ExpectedWarning ("IL2026", nameof (InstancePropertyViaReflection))] + [ExpectedWarning ("IL2026", nameof (InstancePropertyViaReflection), ProducedBy = ProducedBy.Trimmer)] public static void Test () { Expression<Func<int>> staticGetter = () => StaticPropertyExpressionAccess; diff --git a/test/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInCompilerGeneratedCode.cs b/test/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInCompilerGeneratedCode.cs index 0c9bd06b1..c0f89607b 100644 --- a/test/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInCompilerGeneratedCode.cs +++ b/test/Mono.Linker.Tests.Cases/Warnings/WarningSuppression/SuppressWarningsInCompilerGeneratedCode.cs @@ -55,8 +55,7 @@ namespace Mono.Linker.Tests.Cases.Warnings.WarningSuppression yield return 0; } - // See https://github.com/dotnet/linker/issues/2587 - [UnconditionalSuppressMessage ("Linker sees the compiler generated property", "IL2077")] + [UnconditionalSuppressMessage ("Test", "IL2067")] static IEnumerable<int> TestMethodParameterWithRequirements (Type unknownType = null) { unknownType.RequiresNonPublicMethods (); @@ -122,8 +121,7 @@ namespace Mono.Linker.Tests.Cases.Warnings.WarningSuppression await MethodAsync (); } - // See https://github.com/dotnet/linker/issues/2587 - [UnconditionalSuppressMessage ("Linker sees the compiler generated property", "IL2077")] + [UnconditionalSuppressMessage ("Test", "IL2067")] static async void TestMethodParameterWithRequirements (Type unknownType = null) { unknownType.RequiresNonPublicMethods (); @@ -199,7 +197,7 @@ namespace Mono.Linker.Tests.Cases.Warnings.WarningSuppression void LocalFunction () => typeof (TypeWithRUCMethod).RequiresNonPublicMethods (); } - [UnconditionalSuppressMessage ("Test", "IL2077")] + [UnconditionalSuppressMessage ("Test", "IL2067")] static void TestMethodParameterWithRequirements (Type unknownType = null) { LocalFunction (); @@ -403,7 +401,7 @@ namespace Mono.Linker.Tests.Cases.Warnings.WarningSuppression () => typeof (TypeWithRUCMethod).RequiresNonPublicMethods (); } - [UnconditionalSuppressMessage ("Test", "IL2077")] + [UnconditionalSuppressMessage ("Test", "IL2067")] static void TestMethodParameterWithRequirements (Type unknownType = null) { Action _ = |