diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/DataFlow/LocalDataFlowVisitor.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/DataFlow/LocalDataFlowVisitor.cs index 4a3df554fe87d2..8068c73c51be4c 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/DataFlow/LocalDataFlowVisitor.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/DataFlow/LocalDataFlowVisitor.cs @@ -272,19 +272,24 @@ TValue ProcessSingleTargetAssignment (IOperation targetOperation, IAssignmentOpe Debug.Assert (operation.Target is not IFlowCaptureReferenceOperation); break; case IInvalidOperation: - // This can happen for a field assignment in an attribute instance. - // TODO: validate against the field attributes. + // This can happen for a field assignment in an attribute instance. + // TODO: validate against the field attributes. case IInstanceReferenceOperation: - // Assignment to 'this' is not tracked currently. - // Not relevant for trimming dataflow. + // Assignment to 'this' is not tracked currently. + // Not relevant for trimming dataflow. case IInvocationOperation: - // This can happen for an assignment to a ref return. Skip for now. - // The analyzer doesn't handle refs yet. This should be fixed once the analyzer - // also produces warnings for ref params/locals/returns. - // https://github.com/dotnet/linker/issues/2632 - // https://github.com/dotnet/linker/issues/2158 + // This can happen for an assignment to a ref return. Skip for now. + // The analyzer doesn't handle refs yet. This should be fixed once the analyzer + // also produces warnings for ref params/locals/returns. + // https://github.com/dotnet/linker/issues/2632 + // https://github.com/dotnet/linker/issues/2158 Visit (targetOperation, state); break; + case IDynamicMemberReferenceOperation: + case IDynamicIndexerAccessOperation: + // Not yet implemented in analyzer: + // https://github.com/dotnet/runtime/issues/94057 + break; // Keep these cases in sync with those in CapturedReferenceValue, for any that // can show up in a flow capture reference (for example, where the right-hand side diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs index c258ccf5a0ccb9..0b054541f82f8b 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs @@ -124,6 +124,12 @@ public Task DynamicDependencyDataflow () return RunTest (nameof (DynamicDependencyDataflow)); } + [Fact] + public Task DynamicObjects () + { + return RunTest (); + } + [Fact] public Task EmptyArrayIntrinsicsDataFlow () { diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs index fc728cec172020..e86f90926eb524 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs @@ -372,83 +372,6 @@ private int M2 { return VerifyRequiresUnreferencedCodeCodeFix (src, fix, diag, Array.Empty ()); } - [Fact] - public Task InvocationOnDynamicType () - { - var source = $$""" - using System; - class C - { - static void M0 () - { - dynamic dynamicField = "Some string"; - Console.WriteLine (dynamicField); - } - - static void M1 () - { - MethodWithDynamicArgDoNothing (0); - MethodWithDynamicArgDoNothing ("Some string"); - MethodWithDynamicArg(-1); - } - - static void MethodWithDynamicArgDoNothing (dynamic arg) - { - } - - static void MethodWithDynamicArg (dynamic arg) - { - arg.MethodWithDynamicArg (arg); - } - } - """; - - return VerifyRequiresUnreferencedCodeAnalyzer (source, - // (8,3): warning IL2026: Invoking members on dynamic types is not trimming safe. Types or members might have been removed by the trimmer. - VerifyCS.Diagnostic (dynamicInvocationDiagnosticDescriptor).WithSpan (7, 3, 7, 35), - // (24,3): warning IL2026: Invoking members on dynamic types is not trimming safe. Types or members might have been removed by the trimmer. - VerifyCS.Diagnostic (dynamicInvocationDiagnosticDescriptor).WithSpan (23, 3, 23, 33)); - } - - [Fact] - public Task DynamicInRequiresUnreferencedCodeClass () - { - var source = $$""" - using System.Diagnostics.CodeAnalysis; - - [RequiresUnreferencedCode("message")] - class ClassWithRequires - { - public static void MethodWithDynamicArg (dynamic arg) - { - arg.DynamicInvocation (); - } - } - """; - - return VerifyRequiresUnreferencedCodeAnalyzer (source); - } - - [Fact] - public Task InvocationOnDynamicTypeInMethodWithRUCDoesNotWarnTwoTimes () - { - var source = $$""" - using System; - using System.Diagnostics.CodeAnalysis; - class C - { - [RequiresUnreferencedCode ("We should only see the warning related to this annotation, and none about the dynamic type.")] - static void M0 () - { - dynamic dynamicField = "Some string"; - Console.WriteLine (dynamicField); - } - } - """; - - return VerifyRequiresUnreferencedCodeAnalyzer (source); - } - [Fact] public Task TestMakeGenericMethodUsage () { diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/DynamicObjects.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/DynamicObjects.cs new file mode 100644 index 00000000000000..6dec20faecf548 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/DynamicObjects.cs @@ -0,0 +1,171 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + [SkipKeptItemsValidation] + [ExpectedNoWarnings] + [Reference ("Microsoft.CSharp.dll")] + public class DynamicObjects + { + public static void Main () + { + InvocationOnDynamicType.Test (); + DynamicMemberReference.Test (); + DynamicIndexerAccess.Test (); + DynamicInRequiresUnreferencedCodeClass.Test (); + InvocationOnDynamicTypeInMethodWithRUCDoesNotWarnTwoTimes.Test (); + } + + class InvocationOnDynamicType + { + // Analyzer hole: https://github.com/dotnet/runtime/issues/94057 + [ExpectedWarning ("IL2026", "Invoking members on dynamic types is not trimming-compatible.", ProducedBy = Tool.Analyzer)] + [ExpectedWarning ("IL2026", "Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember", ProducedBy = Tool.Trimmer | Tool.NativeAot)] + [ExpectedWarning ("IL3050", ProducedBy = Tool.NativeAot)] + static void DynamicArgument () + { + dynamic dynamicObject = "Some string"; + Console.WriteLine (dynamicObject); + } + + static void DynamicParameter () + { + MethodWithDynamicParameterDoNothing (0); + MethodWithDynamicParameterDoNothing ("Some string"); + MethodWithDynamicParameter(-1); + } + + static void MethodWithDynamicParameterDoNothing (dynamic arg) + { + } + + // Analyzer hole: https://github.com/dotnet/runtime/issues/94057 + [ExpectedWarning ("IL2026", "Invoking members on dynamic types is not trimming-compatible.", ProducedBy = Tool.Analyzer)] + [ExpectedWarning ("IL2026", "Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember", ProducedBy = Tool.Trimmer | Tool.NativeAot)] + [ExpectedWarning ("IL3050", ProducedBy = Tool.NativeAot)] + static void MethodWithDynamicParameter (dynamic arg) + { + arg.MethodWithDynamicParameter (arg); + } + + [ExpectedWarning ("IL2026", "Microsoft.CSharp.RuntimeBinder.Binder.InvokeConstructor", ProducedBy = Tool.Trimmer | Tool.NativeAot)] + [ExpectedWarning ("IL3050", ProducedBy = Tool.NativeAot)] + // TODO: analyzer hole! + static void ObjectCreationDynamicArgument () + { + dynamic dynamicObject = "Some string"; + var x = new ClassWithDynamicCtor (dynamicObject); + } + + class ClassWithDynamicCtor + { + public ClassWithDynamicCtor (dynamic arg) + { + } + } + + public static void Test () + { + DynamicArgument (); + DynamicParameter (); + ObjectCreationDynamicArgument (); + } + } + + class DynamicMemberReference + { + // Analyzer hole: https://github.com/dotnet/runtime/issues/94057 + [ExpectedWarning ("IL2026", "Microsoft.CSharp.RuntimeBinder.Binder.GetMember", ProducedBy = Tool.Trimmer | Tool.NativeAot)] + [ExpectedWarning ("IL3050", ProducedBy = Tool.NativeAot)] + static void Read (dynamic d) + { + var x = d.Member; + } + + // Analyzer hole: https://github.com/dotnet/runtime/issues/94057 + [ExpectedWarning ("IL2026", "Microsoft.CSharp.RuntimeBinder.Binder.SetMember", ProducedBy = Tool.Trimmer | Tool.NativeAot)] + [ExpectedWarning ("IL3050", ProducedBy = Tool.NativeAot)] + static void Write (dynamic d) + { + d.Member = 0; + } + + public static void Test () + { + Read (null); + Write (null); + } + } + + class DynamicIndexerAccess + { + // Analyzer hole: https://github.com/dotnet/runtime/issues/94057 + [ExpectedWarning ("IL2026", "Microsoft.CSharp.RuntimeBinder.Binder.GetIndex", ProducedBy = Tool.Trimmer | Tool.NativeAot)] + [ExpectedWarning ("IL3050", ProducedBy = Tool.NativeAot)] + static void Read (dynamic d) + { + var x = d[0]; + } + + // Analyzer hole: https://github.com/dotnet/runtime/issues/94057 + [ExpectedWarning ("IL2026", "Microsoft.CSharp.RuntimeBinder.Binder.SetIndex", ProducedBy = Tool.Trimmer | Tool.NativeAot)] + [ExpectedWarning ("IL3050", ProducedBy = Tool.NativeAot)] + static void Write (dynamic d) + { + d[0] = 0; + } + + public static void Test () + { + Read (null); + Write (null); + } + } + + class DynamicInRequiresUnreferencedCodeClass + { + [RequiresUnreferencedCode("message")] + class ClassWithRequires + { + // Analyzer hole: https://github.com/dotnet/runtime/issues/94057 + [ExpectedWarning ("IL3050", ProducedBy = Tool.NativeAot)] + public static void MethodWithDynamicArg (dynamic arg) + { + arg.DynamicInvocation (); + } + } + + [ExpectedWarning ("IL2026", nameof (ClassWithRequires))] + public static void Test () + { + ClassWithRequires.MethodWithDynamicArg (null); + } + } + + class InvocationOnDynamicTypeInMethodWithRUCDoesNotWarnTwoTimes () + { + // Analyzer hole: https://github.com/dotnet/runtime/issues/94057 + [RequiresUnreferencedCode ("We should only see the warning related to this annotation, and none about the dynamic type.")] + [RequiresDynamicCode ("We should only see the warning related to this annotation, and none about the dynamic type.")] + static void MethodWithRequires () + { + dynamic dynamicField = "Some string"; + Console.WriteLine (dynamicField); + } + + [ExpectedWarning ("IL2026", nameof (MethodWithRequires))] + [ExpectedWarning ("IL3050", nameof (MethodWithRequires), ProducedBy = Tool.Analyzer | Tool.NativeAot)] + public static void Test () + { + MethodWithRequires (); + } + } + } +}