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
<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>
}
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");
}
return TestNamesBySuiteName ();
}
+ public static IEnumerable<object[]> SingleFile ()
+ {
+ return TestNamesBySuiteName ();
+ }
+
public static IEnumerable<object[]> Warnings ()
{
return TestNamesBySuiteName ();
}
[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)
{
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;
/// </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,
&& 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")
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;
// 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
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'.
--- /dev/null
+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
--- /dev/null
+// 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);
+ }
+ }
+}
return NUnitCasesBySuiteName ("Serialization");
}
+ public static IEnumerable<TestCaseData> SingleFileTests ()
+ {
+ return NUnitCasesBySuiteName ("SingleFile");
+ }
+
public static IEnumerable<TestCaseData> StaticsTests ()
{
return NUnitCasesBySuiteName ("Statics");
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)
{