Produce IL3000 and IL3001 from NativeAOT compiler (#83253)
authorVitek Karas <10670590+vitek-karas@users.noreply.github.com>
Wed, 15 Mar 2023 09:38:29 +0000 (02:38 -0700)
committerGitHub <noreply@github.com>
Wed, 15 Mar 2023 09:38:29 +0000 (02:38 -0700)
This also implements the suppression of the IL3002 on the affected methods both in the NativeAOT compiler and the analyzer.

Adds single-file specific tests which are skipped for the trimmer.

Fixes: https://github.com/dotnet/runtime/issues/83088
Fixes: https://github.com/dotnet/runtime/issues/82475

13 files changed:
eng/testing/tests.singlefile.targets
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs
src/coreclr/tools/aot/Mono.Linker.Tests/TestCases/TestDatabase.cs
src/coreclr/tools/aot/Mono.Linker.Tests/TestCases/TestSuites.cs
src/tools/illink/src/ILLink.RoslynAnalyzer/RequiresAssemblyFilesAnalyzer.cs
src/tools/illink/src/ILLink.Shared/TrimAnalysis/IntrinsicId.cs
src/tools/illink/src/ILLink.Shared/TrimAnalysis/Intrinsics.cs
src/tools/illink/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresAssemblyFilesAnalyzerTests.cs
src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/SingleFileTests.g.cs [new file with mode: 0644]
src/tools/illink/test/Mono.Linker.Tests.Cases/SingleFile/SingleFileIntrinsics.cs [new file with mode: 0644]
src/tools/illink/test/Mono.Linker.Tests/TestCases/TestDatabase.cs
src/tools/illink/test/Mono.Linker.Tests/TestCases/TestSuites.cs

index 2252731..6bbbcc7 100644 (file)
@@ -29,7 +29,7 @@
     <IlcSdkPath>$(CoreCLRAotSdkDir)</IlcSdkPath>
     <IlcFrameworkPath>$(NetCoreAppCurrentTestHostSharedFrameworkPath)</IlcFrameworkPath>
     <IlcFrameworkNativePath>$(NetCoreAppCurrentTestHostSharedFrameworkPath)</IlcFrameworkNativePath>
-    <NoWarn>$(NoWarn);IL1005;IL3002;IL3003</NoWarn>
+    <NoWarn>$(NoWarn);IL1005;IL3000;IL3001;IL3002;IL3003</NoWarn>
     <TrimMode>partial</TrimMode>
     <SuppressTrimAnalysisWarnings>true</SuppressTrimAnalysisWarnings>
     <SuppressAotAnalysisWarnings>true</SuppressAotAnalysisWarnings>
index 6e55938..db0fdc4 100644 (file)
@@ -533,6 +533,27 @@ namespace ILCompiler.Dataflow
                     }
                     break;
 
+                //
+                // string System.Reflection.Assembly.Location getter
+                // string System.Reflection.AssemblyName.CodeBase getter
+                // string System.Reflection.AssemblyName.EscapedCodeBase getter
+                //
+                case IntrinsicId.Assembly_get_Location:
+                case IntrinsicId.AssemblyName_get_CodeBase:
+                case IntrinsicId.AssemblyName_get_EscapedCodeBase:
+                    diagnosticContext.AddDiagnostic(DiagnosticId.AvoidAssemblyLocationInSingleFile, calledMethod.GetDisplayName());
+                    break;
+
+                //
+                // string System.Reflection.Assembly.GetFile(string)
+                // string System.Reflection.Assembly.GetFiles()
+                // string System.Reflection.Assembly.GetFiles(bool)
+                //
+                case IntrinsicId.Assembly_GetFile:
+                case IntrinsicId.Assembly_GetFiles:
+                    diagnosticContext.AddDiagnostic(DiagnosticId.AvoidAssemblyGetFilesInSingleFile, calledMethod.GetDisplayName());
+                    break;
+
                 default:
                     throw new NotImplementedException("Unhandled intrinsic");
             }
index ec395f2..fc9b8f2 100644 (file)
@@ -44,6 +44,11 @@ namespace Mono.Linker.Tests.TestCases
                        return TestNamesBySuiteName ();
                }
 
+               public static IEnumerable<object[]> SingleFile ()
+               {
+                       return TestNamesBySuiteName ();
+               }
+
                public static IEnumerable<object[]> Warnings ()
                {
                        return TestNamesBySuiteName ();
index 4fe12fa..add1386 100644 (file)
@@ -52,6 +52,13 @@ namespace Mono.Linker.Tests.TestCases
                }
 
                [Theory]
+               [MemberData (nameof (TestDatabase.SingleFile), MemberType = typeof (TestDatabase))]
+               public void SingleFile (string t)
+               {
+                       Run (t);
+               }
+
+               [Theory]
                [MemberData (nameof (TestDatabase.Warnings), MemberType = typeof (TestDatabase))]
                public void Warnings (string t)
                {
index d739bcc..1b6a712 100644 (file)
@@ -80,9 +80,19 @@ namespace ILLink.RoslynAnalyzer
 
                protected override bool ReportSpecialIncompatibleMembersDiagnostic (OperationAnalysisContext operationContext, ImmutableArray<ISymbol> dangerousPatterns, ISymbol member)
                {
-                       if (member is IMethodSymbol && ImmutableArrayOperations.Contains (dangerousPatterns, member, SymbolEqualityComparer.Default)) {
-                               operationContext.ReportDiagnostic (Diagnostic.Create (s_getFilesRule, operationContext.Operation.Syntax.GetLocation (), member.GetDisplayName ()));
-                               return true;
+                       if (member is IMethodSymbol method) {
+                               if (ImmutableArrayOperations.Contains (dangerousPatterns, member, SymbolEqualityComparer.Default)) {
+                                       operationContext.ReportDiagnostic (Diagnostic.Create (s_getFilesRule, operationContext.Operation.Syntax.GetLocation (), member.GetDisplayName ()));
+                                       return true;
+                               }
+                               else if (method.AssociatedSymbol is ISymbol associatedSymbol &&
+                                       ImmutableArrayOperations.Contains (dangerousPatterns, associatedSymbol, SymbolEqualityComparer.Default)) {
+                                       // The getters for CodeBase and EscapedCodeBase have RAF attribute on them
+                                       // so our caller will produce the RAF warning (IL3002) by default. Since we handle these properties specifically
+                                       // here and produce different warning (IL3000) we don't want the caller to produce IL3002.
+                                       // So we need to return true from here for the getters, to suppress the RAF warning.
+                                       return true;
+                               }
                        } else if (member is IPropertySymbol && ImmutableArrayOperations.Contains (dangerousPatterns, member, SymbolEqualityComparer.Default)) {
                                operationContext.ReportDiagnostic (Diagnostic.Create (s_locationRule, operationContext.Operation.Syntax.GetLocation (), member.GetDisplayName ()));
                                return true;
index 700ed6a..4392397 100644 (file)
@@ -288,6 +288,27 @@ namespace ILLink.Shared.TrimAnalysis
                /// </summary>
                Assembly_CreateInstance,
                /// <summary>
+               /// <see cref="System.Reflection.Assembly.Location"/>
+               /// </summary>
+               Assembly_get_Location,
+               /// <summary>
+               /// <see cref="System.Reflection.Assembly.GetFile(string)"/>
+               /// </summary>
+               Assembly_GetFile,
+               /// <summary>
+               /// <see cref="System.Reflection.Assembly.GetFiles()"/>
+               /// <see cref="System.Reflection.Assembly.GetFiles(bool)"/>
+               /// </summary>
+               Assembly_GetFiles,
+               /// <summary>
+               /// <see cref="System.Reflection.AssemblyName.CodeBase"/>
+               /// </summary>
+               AssemblyName_get_CodeBase,
+               /// <summary>
+               /// <see cref="System.Reflection.AssemblyName.EscapedCodeBase"/>
+               /// </summary>
+               AssemblyName_get_EscapedCodeBase,
+               /// <summary>
                /// <see cref="System.Reflection.RuntimeReflectionExtensions.GetRuntimeEvent(System.Type, string)"/>
                /// </summary>
                RuntimeReflectionExtensions_GetRuntimeEvent,
index fa71f74..42c7d00 100644 (file)
@@ -356,6 +356,29 @@ namespace ILLink.Shared.TrimAnalysis
                                        && calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.String")
                                        => IntrinsicId.Assembly_CreateInstance,
 
+                               // System.Reflection.Assembly.Location getter
+                               "get_Location" when calledMethod.IsDeclaredOnType ("System.Reflection.Assembly")
+                                       => IntrinsicId.Assembly_get_Location,
+
+                               // System.Reflection.Assembly.GetFile (string)
+                               "GetFile" when calledMethod.IsDeclaredOnType ("System.Reflection.Assembly")
+                                       && calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.String")
+                                       => IntrinsicId.Assembly_GetFile,
+
+                               // System.Reflection.Assembly.GetFiles ()
+                               // System.Reflection.Assembly.GetFiles (bool)
+                               "GetFiles" when calledMethod.IsDeclaredOnType ("System.Reflection.Assembly")
+                                       && (calledMethod.HasMetadataParametersCount (0) || calledMethod.HasParameterOfType ((ParameterIndex) 1, "System.Boolean"))
+                                       => IntrinsicId.Assembly_GetFiles,
+
+                               // System.Reflection.AssemblyName.CodeBase getter
+                               "get_CodeBase" when calledMethod.IsDeclaredOnType ("System.Reflection.AssemblyName")
+                                       => IntrinsicId.AssemblyName_get_CodeBase,
+
+                               // System.Reflection.AssemblyName.EscapedCodeBase getter
+                               "get_EscapedCodeBase" when calledMethod.IsDeclaredOnType ("System.Reflection.AssemblyName")
+                                       => IntrinsicId.AssemblyName_get_EscapedCodeBase,
+
                                // System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor (RuntimeTypeHandle type)
                                "RunClassConstructor" when calledMethod.IsDeclaredOnType ("System.Runtime.CompilerServices.RuntimeHelpers")
                                        && calledMethod.HasParameterOfType ((ParameterIndex) 0, "System.RuntimeTypeHandle")
index 1c77240..0b357b5 100644 (file)
@@ -269,6 +269,11 @@ namespace Mono.Linker.Dataflow
                        case IntrinsicId.Marshal_PtrToStructure:
                        case IntrinsicId.Marshal_DestroyStructure:
                        case IntrinsicId.Marshal_GetDelegateForFunctionPointer:
+                       case IntrinsicId.Assembly_get_Location:
+                       case IntrinsicId.Assembly_GetFile:
+                       case IntrinsicId.Assembly_GetFiles:
+                       case IntrinsicId.AssemblyName_get_CodeBase:
+                       case IntrinsicId.AssemblyName_get_EscapedCodeBase:
                                // These intrinsics are not interesting for trimmer (they are interesting for AOT and that's why they are recognized)
                                break;
 
@@ -339,7 +344,7 @@ namespace Mono.Linker.Dataflow
                        //    For all other cases, the trimming tools would have already produced a warning.
 
                        default:
-                               throw new NotImplementedException ("Unhandled intrinsic");
+                               throw new NotImplementedException ($"Unhandled intrinsic: {intrinsicId}");
                        }
 
                        // If we get here, we handled this as an intrinsic.  As a convenience, if the code above
index 89c8f46..ecea646 100644 (file)
@@ -291,8 +291,6 @@ build_property.{MSBuildPropertyOptionNames.EnableSingleFileAnalyzer} = true")));
                                DiagnosticResult.CompilerWarning ("SYSLIB0044").WithSpan (7, 7, 7, 17).WithArguments ("System.Reflection.AssemblyName.CodeBase", "AssemblyName.CodeBase and AssemblyName.EscapedCodeBase are obsolete. Using them for loading an assembly is not supported."),
                                // (8,7): warning SYSLIB0044: 'AssemblyName.EscapedCodeBase' is obsolete: 'AssemblyName.CodeBase and AssemblyName.EscapedCodeBase are obsolete. Using them for loading an assembly is not supported.'
                                DiagnosticResult.CompilerWarning ("SYSLIB0044").WithSpan (8, 7, 8, 24).WithArguments ("System.Reflection.AssemblyName.EscapedCodeBase", "AssemblyName.CodeBase and AssemblyName.EscapedCodeBase are obsolete. Using them for loading an assembly is not supported."),
-                               // (7,7): warning IL3002: Using member 'System.Reflection.AssemblyName.CodeBase.get' which has 'RequiresAssemblyFilesAttribute' can break functionality when embedded in a single-file app. The code will return an empty string for assemblies embedded in a single-file app.
-                               VerifyCS.Diagnostic (DiagnosticId.RequiresAssemblyFiles).WithSpan (7, 7, 7, 17).WithArguments ("System.Reflection.AssemblyName.CodeBase.get", " The code will return an empty string for assemblies embedded in a single-file app.", ""),
                                // (7,7): warning IL3000: 'System.Reflection.AssemblyName.CodeBase' always returns an empty string for assemblies embedded in a single-file app. If the path to the app directory is needed, consider calling 'System.AppContext.BaseDirectory'.
                                VerifyCS.Diagnostic (DiagnosticId.AvoidAssemblyLocationInSingleFile).WithSpan (7, 7, 7, 17).WithArguments ("System.Reflection.AssemblyName.CodeBase"),
                                // (8,7): warning IL3000: 'System.Reflection.AssemblyName.EscapedCodeBase' always returns an empty string for assemblies embedded in a single-file app. If the path to the app directory is needed, consider calling 'System.AppContext.BaseDirectory'.
diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/SingleFileTests.g.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/SingleFileTests.g.cs
new file mode 100644 (file)
index 0000000..9c63f46
--- /dev/null
@@ -0,0 +1,19 @@
+using System;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace ILLink.RoslynAnalyzer.Tests
+{
+       public sealed partial class SingleFileTests : LinkerTestBase
+       {
+
+               protected override string TestSuiteName => "SingleFile";
+
+               [Fact]
+               public Task SingleFileIntrinsics ()
+               {
+                       return RunTest (allowMissingWarnings: true);
+               }
+
+       }
+}
\ No newline at end of file
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/SingleFile/SingleFileIntrinsics.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/SingleFile/SingleFileIntrinsics.cs
new file mode 100644 (file)
index 0000000..1a3b8b4
--- /dev/null
@@ -0,0 +1,94 @@
+// 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 Mono.Linker.Tests.Cases.Expectations.Assertions;
+
+namespace Mono.Linker.Tests.Cases.SingleFile
+{
+       [IgnoreTestCase ("Ignore in illink since it doesn't implement any single-file related functionality", IgnoredBy = Tool.Trimmer)]
+       [SkipKeptItemsValidation]
+       [ExpectedNoWarnings]
+       public class SingleFileIntrinsics
+       {
+               // Some of the test methods have RAF on them, it's not the point of this test to verify that behavior
+               [UnconditionalSuppressMessage("test", "IL3002")]
+               public static void Main ()
+               {
+                       TestAssemblyLocation ();
+                       TestAssemblyLocationSuppressedByRAF ();
+                       TestAssemblyNameCodeBase ();
+                       TestAssemblyNameCodeBaseSuppressedByRAF ();
+                       TestAssemblyNameEscapedCodeBase ();
+                       TestAssemblyNameEscapedCodeBaseSuppressedByRAF ();
+                       TestAssemblyGetFile ();
+                       TestAssemblyGetFileSuppressedByRAF ();
+                       TestAssemblyGetFiles ();
+                       TestAssemblyGetFilesSuppressedByRAF ();
+               }
+
+               [ExpectedWarning("IL3000", ProducedBy = Tool.Analyzer | Tool.NativeAot)]
+               static void TestAssemblyLocation()
+               {
+                       var a = typeof (SingleFileIntrinsics).Assembly.Location;
+               }
+
+               [RequiresAssemblyFiles("test")]
+               static void TestAssemblyLocationSuppressedByRAF()
+               {
+                       var a = typeof (SingleFileIntrinsics).Assembly.Location;
+               }
+
+               [ExpectedWarning ("IL3000", ProducedBy = Tool.Analyzer | Tool.NativeAot)]
+               static void TestAssemblyNameCodeBase()
+               {
+                       var a = typeof (SingleFileIntrinsics).Assembly.GetName ().CodeBase;
+               }
+
+               [RequiresAssemblyFiles ("test")]
+               static void TestAssemblyNameCodeBaseSuppressedByRAF ()
+               {
+                       var a = typeof (SingleFileIntrinsics).Assembly.GetName ().CodeBase;
+               }
+
+               [ExpectedWarning ("IL3000", ProducedBy = Tool.Analyzer | Tool.NativeAot)]
+               static void TestAssemblyNameEscapedCodeBase ()
+               {
+                       var a = typeof (SingleFileIntrinsics).Assembly.GetName ().EscapedCodeBase;
+               }
+
+               [RequiresAssemblyFiles ("test")]
+               static void TestAssemblyNameEscapedCodeBaseSuppressedByRAF ()
+               {
+                       var a = typeof (SingleFileIntrinsics).Assembly.GetName ().EscapedCodeBase;
+               }
+
+               [ExpectedWarning ("IL3001", ProducedBy = Tool.Analyzer | Tool.NativeAot)]
+               static void TestAssemblyGetFile()
+               {
+                       var a = typeof (SingleFileIntrinsics).Assembly.GetFile ("unknown");
+               }
+
+               [RequiresAssemblyFiles ("test")]
+               static void TestAssemblyGetFileSuppressedByRAF ()
+               {
+                       var a = typeof (SingleFileIntrinsics).Assembly.GetFile ("unknown");
+               }
+
+               [ExpectedWarning ("IL3001", ProducedBy = Tool.Analyzer | Tool.NativeAot)]
+               [ExpectedWarning ("IL3001", ProducedBy = Tool.Analyzer | Tool.NativeAot)]
+               static void TestAssemblyGetFiles ()
+               {
+                       var a = typeof (SingleFileIntrinsics).Assembly.GetFiles ();
+                       a = typeof (SingleFileIntrinsics).Assembly.GetFiles (true);
+               }
+
+               [RequiresAssemblyFiles ("test")]
+               static void TestAssemblyGetFilesSuppressedByRAF ()
+               {
+                       var a = typeof (SingleFileIntrinsics).Assembly.GetFiles ();
+                       a = typeof (SingleFileIntrinsics).Assembly.GetFiles (true);
+               }
+       }
+}
index 6eec8ce..855bc8c 100644 (file)
@@ -174,6 +174,11 @@ namespace Mono.Linker.Tests.TestCases
                        return NUnitCasesBySuiteName ("Serialization");
                }
 
+               public static IEnumerable<TestCaseData> SingleFileTests ()
+               {
+                       return NUnitCasesBySuiteName ("SingleFile");
+               }
+
                public static IEnumerable<TestCaseData> StaticsTests ()
                {
                        return NUnitCasesBySuiteName ("Statics");
index 91e174c..32f4540 100644 (file)
@@ -208,6 +208,12 @@ namespace Mono.Linker.Tests.TestCases
                        Run (testCase);
                }
 
+               [TestCaseSource (typeof (TestDatabase), nameof (TestDatabase.SingleFileTests))]
+               public void SingleFileTests (TestCase testCase)
+               {
+                       Run (testCase);
+               }
+
                [TestCaseSource (typeof (TestDatabase), nameof (TestDatabase.StaticsTests))]
                public void StaticsTests (TestCase testCase)
                {