From aad916c7111323d4b5744eb2444252d149c57a8a Mon Sep 17 00:00:00 2001 From: Tlakaelel Axayakatl Ceja Date: Wed, 19 May 2021 13:04:03 -0700 Subject: [PATCH] Enable single file analyzer in the runtime (#50894) Summary: Enables single file analyzer in the runtime by adding the ILLink.Analyzers package and the property EnableSingleFileAnalyzer equals to true Deletes entries IL3000 and IL3001 in the CodeAnalysis.ruleset so the analyzer can actually produce these warnings otherwise the analyzer execution would be skipped. *Note*: tests warnings are controlled by CodeAnalysis.test.ruleset (this PR adds IL3002 to the CodeAnalysis.test.ruleset to disable the warning on tests) Resolve warnings on single file dangerous patterns by: - Annotating incompatible single file patterns with RequiresAssemblyFiles - Suppressing the warning (because there is code that handles the incompatible code) - Fixing the issue on code (remove the incompatible code and replace it with something that won't fail) - Opening an issue to track fix and either annotate/suppress the current behavior --- eng/Analyzers.props | 2 ++ eng/CodeAnalysis.ruleset | 2 -- eng/CodeAnalysis.test.ruleset | 1 + eng/Versions.props | 1 + .../ref/Microsoft.Extensions.DependencyModel.cs | 3 +++ .../Microsoft.Extensions.DependencyModel.csproj | 1 + .../src/DependencyContext.cs | 7 +++++++ .../src/DependencyContextLoader.cs | 4 ++++ .../Microsoft.Extensions.DependencyModel.csproj | 3 +++ .../src/AssemblyResolver.cs | 3 +++ .../src/Microsoft.NETCore.Platforms.csproj | 7 ++++--- .../Microsoft.XmlSerializer.Generator.csproj | 6 +++++- .../System.ComponentModel.Composition.csproj | 5 ++++- .../Composition/Hosting/AssemblyCatalog.cs | 2 ++ .../Design/DesigntimeLicenseContext.cs | 3 +++ ...em.Configuration.ConfigurationManager.csproj | 1 + .../System/Configuration/ClientConfigPaths.cs | 3 +++ .../src/System.Diagnostics.EventLog.csproj | 6 ++++-- .../src/System/Diagnostics/EventLog.cs | 3 +++ .../IO/IsolatedStorage/Helper.Win32Unix.cs | 17 ++++++++++++----- .../src/System/AppContext.AnyOS.cs | 3 +++ .../RequiresAssemblyFilesAttribute.cs | 9 +++++++-- .../src/System/Reflection/Assembly.cs | 5 +++++ .../src/System/Reflection/AssemblyName.cs | 4 ++++ .../Runtime/Loader/AssemblyLoadContext.cs | 6 ++++++ .../src/System/Xml/Serialization/Compilation.cs | 3 +++ .../System/Xml/Serialization/XmlSerializer.cs | 7 ++++++- .../src/System.Reflection.Context.csproj | 3 ++- .../Context/Delegation/DelegatingAssembly.cs | 5 +++++ ...System.Reflection.MetadataLoadContext.csproj | 6 ++++++ .../Assemblies/Ecma/EcmaAssembly.Modules.cs | 3 +++ .../InteropServices/RuntimeEnvironment.cs | 3 +++ .../System.Runtime/ref/System.Runtime.cs | 6 +++++- src/libraries/tests.proj | 1 + .../BrowserDebugProxy/EvaluateExpression.cs | 3 +++ src/tasks/WasmAppBuilder/WasmAppBuilder.csproj | 2 +- 36 files changed, 129 insertions(+), 20 deletions(-) diff --git a/eng/Analyzers.props b/eng/Analyzers.props index 4405ca11687fe..bd9dc5ddb53e8 100644 --- a/eng/Analyzers.props +++ b/eng/Analyzers.props @@ -3,11 +3,13 @@ $(MSBuildThisFileDirectory)CodeAnalysis.ruleset false + true + diff --git a/eng/CodeAnalysis.ruleset b/eng/CodeAnalysis.ruleset index e3626e28f62a9..fe63588a31cd4 100644 --- a/eng/CodeAnalysis.ruleset +++ b/eng/CodeAnalysis.ruleset @@ -254,8 +254,6 @@ - - diff --git a/eng/CodeAnalysis.test.ruleset b/eng/CodeAnalysis.test.ruleset index f1f75fb00a2fa..142a1e8e86384 100644 --- a/eng/CodeAnalysis.test.ruleset +++ b/eng/CodeAnalysis.test.ruleset @@ -255,6 +255,7 @@ + diff --git a/eng/Versions.props b/eng/Versions.props index f78583f8d51aa..38db5e0f63c01 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -162,6 +162,7 @@ 5.0.0-preview-20201009.2 6.0.100-preview.5.21267.3 + $(MicrosoftNETILLinkTasksVersion) 6.0.0-preview.5.21267.1 diff --git a/src/libraries/Microsoft.Extensions.DependencyModel/ref/Microsoft.Extensions.DependencyModel.cs b/src/libraries/Microsoft.Extensions.DependencyModel/ref/Microsoft.Extensions.DependencyModel.cs index 7ca797e7d0a8e..10786ab1afe24 100644 --- a/src/libraries/Microsoft.Extensions.DependencyModel/ref/Microsoft.Extensions.DependencyModel.cs +++ b/src/libraries/Microsoft.Extensions.DependencyModel/ref/Microsoft.Extensions.DependencyModel.cs @@ -62,10 +62,12 @@ public partial class DependencyContext public DependencyContext(Microsoft.Extensions.DependencyModel.TargetInfo target, Microsoft.Extensions.DependencyModel.CompilationOptions compilationOptions, System.Collections.Generic.IEnumerable compileLibraries, System.Collections.Generic.IEnumerable runtimeLibraries, System.Collections.Generic.IEnumerable runtimeGraph) { } public Microsoft.Extensions.DependencyModel.CompilationOptions CompilationOptions { get { throw null; } } public System.Collections.Generic.IReadOnlyList CompileLibraries { get { throw null; } } + [System.Diagnostics.CodeAnalysis.RequiresAssemblyFilesAttribute(Message = "DependencyContext for an assembly from a application published as single-file is not supported. The method will return null. Make sure the calling code can handle this case.")] public static Microsoft.Extensions.DependencyModel.DependencyContext Default { get { throw null; } } public System.Collections.Generic.IReadOnlyList RuntimeGraph { get { throw null; } } public System.Collections.Generic.IReadOnlyList RuntimeLibraries { get { throw null; } } public Microsoft.Extensions.DependencyModel.TargetInfo Target { get { throw null; } } + [System.Diagnostics.CodeAnalysis.RequiresAssemblyFilesAttribute(Message = "DependencyContext for an assembly from a application published as single-file is not supported. The method will return null. Make sure the calling code can handle this case.")] public static Microsoft.Extensions.DependencyModel.DependencyContext Load(System.Reflection.Assembly assembly) { throw null; } public Microsoft.Extensions.DependencyModel.DependencyContext Merge(Microsoft.Extensions.DependencyModel.DependencyContext other) { throw null; } } @@ -95,6 +97,7 @@ public partial class DependencyContextLoader { public DependencyContextLoader() { } public static Microsoft.Extensions.DependencyModel.DependencyContextLoader Default { get { throw null; } } + [System.Diagnostics.CodeAnalysis.RequiresAssemblyFilesAttribute(Message = "DependencyContext for an assembly from a application published as single-file is not supported. The method will return null. Make sure the calling code can handle this case.")] public Microsoft.Extensions.DependencyModel.DependencyContext Load(System.Reflection.Assembly assembly) { throw null; } } public partial class DependencyContextWriter diff --git a/src/libraries/Microsoft.Extensions.DependencyModel/ref/Microsoft.Extensions.DependencyModel.csproj b/src/libraries/Microsoft.Extensions.DependencyModel/ref/Microsoft.Extensions.DependencyModel.csproj index 86069259a9bc9..2901720d3fe1f 100644 --- a/src/libraries/Microsoft.Extensions.DependencyModel/ref/Microsoft.Extensions.DependencyModel.csproj +++ b/src/libraries/Microsoft.Extensions.DependencyModel/ref/Microsoft.Extensions.DependencyModel.csproj @@ -4,5 +4,6 @@ + diff --git a/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContext.cs b/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContext.cs index cb9f01fbd70f4..6e58abf313409 100644 --- a/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContext.cs +++ b/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContext.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; @@ -10,6 +11,9 @@ namespace Microsoft.Extensions.DependencyModel { public class DependencyContext { + + [UnconditionalSuppressMessage("SingleFile", "IL3002:Avoid calling members marked with 'RequiresAssemblyFilesAttribute' when publishing as a single-file", + Justification = "The annotation should be on the static constructor but is Compiler Generated, annotating the caller Default method instead")] private static readonly Lazy _defaultContext = new Lazy(LoadDefault); public DependencyContext(TargetInfo target, @@ -46,6 +50,7 @@ public DependencyContext(TargetInfo target, RuntimeGraph = runtimeGraph.ToArray(); } + [RequiresAssemblyFiles(Message = "DependencyContext for an assembly from a application published as single-file is not supported. The method will return null. Make sure the calling code can handle this case.")] public static DependencyContext Default => _defaultContext.Value; public TargetInfo Target { get; } @@ -74,6 +79,7 @@ public DependencyContext Merge(DependencyContext other) ); } + [RequiresAssemblyFiles(Message = "DependencyContext for an assembly from a application published as single-file is not supported. The method will return null. Make sure the calling code can handle this case.")] private static DependencyContext LoadDefault() { var entryAssembly = Assembly.GetEntryAssembly(); @@ -85,6 +91,7 @@ private static DependencyContext LoadDefault() return Load(entryAssembly); } + [RequiresAssemblyFiles(Message = "DependencyContext for an assembly from a application published as single-file is not supported. The method will return null. Make sure the calling code can handle this case.")] public static DependencyContext Load(Assembly assembly) { return DependencyContextLoader.Default.Load(assembly); diff --git a/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContextLoader.cs b/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContextLoader.cs index a411055df2c5c..2bd5d410d466b 100644 --- a/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContextLoader.cs +++ b/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContextLoader.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection; @@ -50,6 +51,7 @@ private static Stream GetResourceStream(Assembly assembly, string name) return assembly.GetManifestResourceStream(name); } + [RequiresAssemblyFiles(Message = "DependencyContext for an assembly from a application published as single-file is not supported. The method will return null. Make sure the calling code can handle this case.")] public DependencyContext Load(Assembly assembly) { if (assembly == null) @@ -103,6 +105,7 @@ private DependencyContext LoadContext(IDependencyContextReader reader, string lo return null; } + [RequiresAssemblyFiles(Message = "DependencyContext for an assembly from a application published as single-file is not supported. The method will return null. Make sure the calling code can handle this case.")] private DependencyContext LoadAssemblyContext(Assembly assembly, IDependencyContextReader reader) { using (Stream stream = GetResourceStream(assembly, assembly.GetName().Name + DepsJsonExtension)) @@ -125,6 +128,7 @@ private DependencyContext LoadAssemblyContext(Assembly assembly, IDependencyCont return null; } + [RequiresAssemblyFiles(Message = "The use of DependencyContextLoader is not supported when publishing as single-file")] private string GetDepsJsonPath(Assembly assembly) { // Assemblies loaded in memory (e.g. single file) return empty string from Location. diff --git a/src/libraries/Microsoft.Extensions.DependencyModel/src/Microsoft.Extensions.DependencyModel.csproj b/src/libraries/Microsoft.Extensions.DependencyModel/src/Microsoft.Extensions.DependencyModel.csproj index 7d21c765085a3..81b60b7135205 100644 --- a/src/libraries/Microsoft.Extensions.DependencyModel/src/Microsoft.Extensions.DependencyModel.csproj +++ b/src/libraries/Microsoft.Extensions.DependencyModel/src/Microsoft.Extensions.DependencyModel.csproj @@ -11,6 +11,8 @@ Microsoft.Extensions.DependencyModel.DependencyContext + + @@ -29,4 +31,5 @@ Microsoft.Extensions.DependencyModel.DependencyContext + diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/AssemblyResolver.cs b/src/libraries/Microsoft.NETCore.Platforms/src/AssemblyResolver.cs index a0f4aaad29bc0..9427028aa5fe3 100644 --- a/src/libraries/Microsoft.NETCore.Platforms/src/AssemblyResolver.cs +++ b/src/libraries/Microsoft.NETCore.Platforms/src/AssemblyResolver.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection; @@ -27,6 +28,8 @@ public static void Enable() // has run. } + [UnconditionalSuppressMessage("SingleFile", "IL3000:Avoid accessing Assembly file path when publishing as a single file", + Justification = "The code has a fallback to use AppDomain.CurrentDomain.BaseDirectory so it will work correctly in single-file")] private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { // apply any existing policy diff --git a/src/libraries/Microsoft.NETCore.Platforms/src/Microsoft.NETCore.Platforms.csproj b/src/libraries/Microsoft.NETCore.Platforms/src/Microsoft.NETCore.Platforms.csproj index 8dbe46d4b5c3e..8e13e283ca9f1 100644 --- a/src/libraries/Microsoft.NETCore.Platforms/src/Microsoft.NETCore.Platforms.csproj +++ b/src/libraries/Microsoft.NETCore.Platforms/src/Microsoft.NETCore.Platforms.csproj @@ -1,4 +1,4 @@ - + $(NetCoreAppToolCurrent);net472 @@ -21,6 +21,7 @@ + @@ -38,7 +39,7 @@ - + @@ -55,4 +56,4 @@ - \ No newline at end of file + diff --git a/src/libraries/Microsoft.XmlSerializer.Generator/src/Microsoft.XmlSerializer.Generator.csproj b/src/libraries/Microsoft.XmlSerializer.Generator/src/Microsoft.XmlSerializer.Generator.csproj index a560c12aaf67c..6c22b92bfba5e 100644 --- a/src/libraries/Microsoft.XmlSerializer.Generator/src/Microsoft.XmlSerializer.Generator.csproj +++ b/src/libraries/Microsoft.XmlSerializer.Generator/src/Microsoft.XmlSerializer.Generator.csproj @@ -6,9 +6,13 @@ FxResources.$(AssemblyName.Replace('-', '_')).SR Exe netstandard2.0 + false + + - \ No newline at end of file + + diff --git a/src/libraries/System.ComponentModel.Composition/src/System.ComponentModel.Composition.csproj b/src/libraries/System.ComponentModel.Composition/src/System.ComponentModel.Composition.csproj index 6284508e40e9a..d60b2055cb977 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System.ComponentModel.Composition.csproj +++ b/src/libraries/System.ComponentModel.Composition/src/System.ComponentModel.Composition.csproj @@ -182,6 +182,9 @@ + + + @@ -211,4 +214,4 @@ - \ No newline at end of file + diff --git a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/AssemblyCatalog.cs b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/AssemblyCatalog.cs index 338661efe93db..d35b5df575d1f 100644 --- a/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/AssemblyCatalog.cs +++ b/src/libraries/System.ComponentModel.Composition/src/System/ComponentModel/Composition/Hosting/AssemblyCatalog.cs @@ -543,6 +543,8 @@ private void ThrowIfDisposed() private string GetDisplayName() => $"{GetType().Name} (Assembly=\"{Assembly.FullName}\")"; // NOLOC + [UnconditionalSuppressMessage("SingleFile", "IL3000: Avoid accessing Assembly file path when publishing as a single file", + Justification = "Setting a CodeBase is single file compatible")] private static Assembly LoadAssembly(string codeBase) { Requires.NotNullOrEmpty(codeBase, nameof(codeBase)); diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesigntimeLicenseContext.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesigntimeLicenseContext.cs index 64a4498443efd..758af025e35b4 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesigntimeLicenseContext.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/Design/DesigntimeLicenseContext.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection; using System.Collections; @@ -57,6 +58,8 @@ private string GetLocalPath(string fileName) return uri.LocalPath + uri.Fragment; } + [UnconditionalSuppressMessage("SingleFile", "IL3000: Avoid accessing Assembly file path when publishing as a single file", + Justification = "Suppressing the warning until gets fixed, see https://github.com/dotnet/runtime/issues/50821")] public override string GetSavedLicenseKey(Type type, Assembly resourceAssembly) { if (_savedLicenseKeys == null || _savedLicenseKeys[type.AssemblyQualifiedName] == null) diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System.Configuration.ConfigurationManager.csproj b/src/libraries/System.Configuration.ConfigurationManager/src/System.Configuration.ConfigurationManager.csproj index ef35470847194..a7fcc9e7abaeb 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System.Configuration.ConfigurationManager.csproj +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System.Configuration.ConfigurationManager.csproj @@ -251,6 +251,7 @@ + diff --git a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ClientConfigPaths.cs b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ClientConfigPaths.cs index 0f41dd3a3732b..a92612ca0b0f4 100644 --- a/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ClientConfigPaths.cs +++ b/src/libraries/System.Configuration.ConfigurationManager/src/System/Configuration/ClientConfigPaths.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Reflection; @@ -28,6 +29,8 @@ internal sealed class ClientConfigPaths private readonly bool _includesUserConfig; private string _companyName; + [UnconditionalSuppressMessage("SingleFile", "IL3000: Avoid accessing Assembly file path when publishing as a single file", + Justification = "Code handles single file case")] private ClientConfigPaths(string exePath, bool includeUserConfig) { _includesUserConfig = includeUserConfig; diff --git a/src/libraries/System.Diagnostics.EventLog/src/System.Diagnostics.EventLog.csproj b/src/libraries/System.Diagnostics.EventLog/src/System.Diagnostics.EventLog.csproj index 23b766558c448..33436282ae22e 100644 --- a/src/libraries/System.Diagnostics.EventLog/src/System.Diagnostics.EventLog.csproj +++ b/src/libraries/System.Diagnostics.EventLog/src/System.Diagnostics.EventLog.csproj @@ -128,7 +128,9 @@ - + + + @@ -138,4 +140,4 @@ Condition="'$(TargetsWindows)' == 'true'" DependsOnTargets="ResolveProjectReferences" BeforeTargets="GetFilesToPackage" /> - \ No newline at end of file + diff --git a/src/libraries/System.Diagnostics.EventLog/src/System/Diagnostics/EventLog.cs b/src/libraries/System.Diagnostics.EventLog/src/System/Diagnostics/EventLog.cs index e96de7a6f9867..8115e3d5c2678 100644 --- a/src/libraries/System.Diagnostics.EventLog/src/System/Diagnostics/EventLog.cs +++ b/src/libraries/System.Diagnostics.EventLog/src/System/Diagnostics/EventLog.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Runtime.CompilerServices; @@ -691,6 +692,8 @@ internal static RegistryKey GetEventLogRegKey(string machine, bool writable) return null; } + [UnconditionalSuppressMessage("SingleFile", "IL3000: Avoid accessing Assembly file path when publishing as a single file", + Justification = "The code handles if the path is null by calling AppContext.BaseDirectory")] internal static string GetDllPath(string machineName) { string dllPath = Path.Combine(NetFrameworkUtils.GetLatestBuildDllDirectory(machineName), DllName); diff --git a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.Win32Unix.cs b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.Win32Unix.cs index 91758fc6c0242..5483a382ac674 100644 --- a/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.Win32Unix.cs +++ b/src/libraries/System.IO.IsolatedStorage/src/System/IO/IsolatedStorage/Helper.Win32Unix.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Security; using System.Threading; @@ -27,6 +28,8 @@ internal static string GetDataDirectory(IsolatedStorageScope scope) return dataDirectory; } + [UnconditionalSuppressMessage("SingleFile", "IL3000:Avoid accessing Assembly file path when publishing as a single file", + Justification = "Code handles single-file deployment by using the information of the .exe file")] internal static void GetDefaultIdentityAndHash(out object identity, out string hash, char separator) { // In .NET Framework IsolatedStorage uses identity from System.Security.Policy.Evidence to build @@ -53,9 +56,6 @@ internal static void GetDefaultIdentityAndHash(out object identity, out string h throw new IsolatedStorageException(SR.IsolatedStorage_Init); AssemblyName assemblyName = assembly.GetName(); -#pragma warning disable SYSLIB0012 - Uri codeBase = new Uri(assembly.CodeBase!); -#pragma warning restore SYSLIB0012 hash = IdentityHelper.GetNormalizedStrongNameHash(assemblyName)!; if (hash != null) @@ -65,8 +65,15 @@ internal static void GetDefaultIdentityAndHash(out object identity, out string h } else { - hash = "Url" + separator + IdentityHelper.GetNormalizedUriHash(codeBase); - identity = codeBase; + string? location = assembly.Location; + // In case of SingleFile deployment, Assembly.Location is empty. + if (location == string.Empty) + location = Environment.ProcessPath; + if (string.IsNullOrEmpty(location)) + throw new IsolatedStorageException(SR.IsolatedStorage_Init); + Uri locationUri = new Uri(location); + hash = "Url" + separator + IdentityHelper.GetNormalizedUriHash(locationUri); + identity = locationUri; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/AppContext.AnyOS.cs b/src/libraries/System.Private.CoreLib/src/System/AppContext.AnyOS.cs index e497fdf6d395b..c8b5fcba415f5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/AppContext.AnyOS.cs +++ b/src/libraries/System.Private.CoreLib/src/System/AppContext.AnyOS.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection; @@ -8,6 +9,8 @@ namespace System { public static partial class AppContext { + [UnconditionalSuppressMessage("SingleFile", "IL3000: Avoid accessing Assembly file path when publishing as a single file", + Justification = "Single File apps should always set APP_CONTEXT_BASE_DIRECTORY therefore code handles Assembly.Location equals null")] private static string GetBaseDirectoryCore() { // Fallback path for hosts that do not set APP_CONTEXT_BASE_DIRECTORY explicitly diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/RequiresAssemblyFilesAttribute.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/RequiresAssemblyFilesAttribute.cs index 2b7bbcb6b9952..358bf3136da83 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/RequiresAssemblyFilesAttribute.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/RequiresAssemblyFilesAttribute.cs @@ -12,7 +12,12 @@ namespace System.Diagnostics.CodeAnalysis AttributeTargets.Property, Inherited = false, AllowMultiple = false)] - public sealed class RequiresAssemblyFilesAttribute : Attribute +#if SYSTEM_PRIVATE_CORELIB + public +#else + internal +#endif + sealed class RequiresAssemblyFilesAttribute : Attribute { /// /// Initializes a new instance of the class. @@ -32,4 +37,4 @@ public RequiresAssemblyFilesAttribute() { } /// public string? Url { get; set; } } -} \ No newline at end of file +} diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/Assembly.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/Assembly.cs index ccc60df7818bc..6baab555151b4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/Assembly.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/Assembly.cs @@ -81,6 +81,7 @@ public virtual IEnumerable ExportedTypes [RequiresUnreferencedCode("Types might be removed")] public virtual Type[] GetForwardedTypes() { throw NotImplemented.ByDesign; } + [RequiresAssemblyFiles(Message = "The code will throw for assemblies embedded in a single-file app")] public virtual string? CodeBase => throw NotImplemented.ByDesign; public virtual MethodInfo? EntryPoint => throw NotImplemented.ByDesign; public virtual string? FullName => throw NotImplemented.ByDesign; @@ -115,6 +116,7 @@ public virtual IEnumerable ExportedTypes public virtual object[] GetCustomAttributes(bool inherit) { throw NotImplemented.ByDesign; } public virtual object[] GetCustomAttributes(Type attributeType, bool inherit) { throw NotImplemented.ByDesign; } + [RequiresAssemblyFiles(Message = "The code will throw for assemblies embedded in a single-file app")] public virtual string EscapedCodeBase => AssemblyName.EscapeCodeBase(CodeBase); [RequiresUnreferencedCode("Assembly.CreateInstance is not supported with trimming. Use Type.GetType instead.")] @@ -152,6 +154,7 @@ public virtual IEnumerable ExportedTypes public virtual Assembly GetSatelliteAssembly(CultureInfo culture, Version? version) { throw NotImplemented.ByDesign; } public virtual FileStream? GetFile(string name) { throw NotImplemented.ByDesign; } + [RequiresAssemblyFiles(Message = "The code will throw for assemblies embedded in a single-file app")] public virtual FileStream[] GetFiles() => GetFiles(getResourceModules: false); public virtual FileStream[] GetFiles(bool getResourceModules) { throw NotImplemented.ByDesign; } @@ -268,6 +271,8 @@ public static Assembly LoadFile(string path) } [RequiresUnreferencedCode("Types and members the loaded assembly depends on might be removed")] + [UnconditionalSuppressMessage("SingleFile", "IL3000:Avoid accessing Assembly file path when publishing as a single file", + Justification = "The assembly is loaded by specifying a path outside of the single-file bundle, the location of the path will not be empty if the path exist, otherwise it will be handled as null")] private static Assembly? LoadFromResolveHandler(object? sender, ResolveEventArgs args) { Assembly? requestingAssembly = args.RequestingAssembly; diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs index 6b0ca27a948b5..0853bfff0685a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyName.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Configuration.Assemblies; +using System.Diagnostics.CodeAnalysis; using System.Runtime.Serialization; using System.Text; using CultureInfo = System.Globalization.CultureInfo; @@ -59,10 +60,12 @@ public string? CultureName public string? CodeBase { + [RequiresAssemblyFiles(Message = "The code will return an empty string for assemblies embedded in a single-file app")] get => _codeBase; set => _codeBase = value; } + [RequiresAssemblyFiles(Message = "The code will return an empty string for assemblies embedded in a single-file app")] public string? EscapedCodeBase { get @@ -258,6 +261,7 @@ public static bool ReferenceMatchesDefinition(AssemblyName? reference, AssemblyN return refName.Equals(defName, StringComparison.OrdinalIgnoreCase); } + [RequiresAssemblyFiles(Message = "The code will return an empty string for assemblies embedded in a single-file app")] internal static string EscapeCodeBase(string? codebase) { if (codebase == null) diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs index dfa08abd5fc57..e990d5e491869 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Loader/AssemblyLoadContext.cs @@ -609,6 +609,8 @@ public void Dispose() return context.ResolveUsingLoad(assemblyName); } + [UnconditionalSuppressMessage("SingleFile", "IL3000: Avoid accessing Assembly file path when publishing as a single file", + Justification = "The code handles the Assembly.Location equals null")] private Assembly? GetFirstResolvedAssemblyFromResolvingEvent(AssemblyName assemblyName) { Assembly? resolvedAssembly = null; @@ -721,6 +723,8 @@ private static void OnAssemblyLoad(RuntimeAssembly assembly) return InvokeResolveEvent(AssemblyResolve, assembly, assemblyFullName); } + [UnconditionalSuppressMessage("SingleFile", "IL3000: Avoid accessing Assembly file path when publishing as a single file", + Justification = "The code handles the Assembly.Location equals null")] private static RuntimeAssembly? InvokeResolveEvent(ResolveEventHandler? eventHandler, RuntimeAssembly assembly, string name) { if (eventHandler == null) @@ -752,6 +756,8 @@ private static void OnAssemblyLoad(RuntimeAssembly assembly) [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "Satellite assemblies have no code in them and loading is not a problem")] + [UnconditionalSuppressMessage("SingleFile", "IL3000: Avoid accessing Assembly file path when publishing as a single file", + Justification = "This call is fine because native call runs before this and checks BindSatelliteResourceFromBundle")] private Assembly? ResolveSatelliteAssembly(AssemblyName assemblyName) { // Called by native runtime when CultureName is not empty diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Compilation.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Compilation.cs index a3b6334d08b5f..6fd1d1c83e81a 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Compilation.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/Compilation.cs @@ -140,6 +140,9 @@ internal void InitAssemblyMethods(XmlMapping[] xmlMappings) // SxS: This method does not take any resource name and does not expose any resources to the caller. // It's OK to suppress the SxS warning. [RequiresUnreferencedCode("calls LoadFile")] + [UnconditionalSuppressMessage("SingleFile", "IL3000: Avoid accessing Assembly file path when publishing as a single file", + Justification = "Annotating this as dangerous will make the core of the serializer to be marked as not safe, instead " + + "this pattern is only dangerous if using sgen only. See https://github.com/dotnet/runtime/issues/50820")] internal static Assembly? LoadGeneratedAssembly(Type type, string? defaultNamespace, out XmlSerializerImplementation? contract) { Assembly? serializer = null; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs index 2e6ecc3d16d6d..9241eadc04026 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializer.cs @@ -629,6 +629,8 @@ private static XmlSerializer[] GetReflectionBasedSerializers(XmlMapping[] mappin } [RequiresUnreferencedCode("calls GenerateSerializerToStream")] + [UnconditionalSuppressMessage("SingleFile", "IL3000: Avoid accessing Assembly file path when publishing as a single file", + Justification = "Code is used on diagnostics so we fallback to print assembly.FullName if assembly.Location is empty")] internal static bool GenerateSerializer(Type[]? types, XmlMapping[] mappings, Stream stream) { if (types == null || types.Length == 0) @@ -660,7 +662,10 @@ internal static bool GenerateSerializer(Type[]? types, XmlMapping[] mappings, St } else if (type.Assembly != assembly) { - throw new ArgumentException(SR.Format(SR.XmlPregenOrphanType, type.FullName, assembly.Location), nameof(types)); + string? nameOrLocation = assembly.Location; + if (nameOrLocation == string.Empty) + nameOrLocation = assembly.FullName; + throw new ArgumentException(SR.Format(SR.XmlPregenOrphanType, type.FullName, nameOrLocation), nameof(types)); } } diff --git a/src/libraries/System.Reflection.Context/src/System.Reflection.Context.csproj b/src/libraries/System.Reflection.Context/src/System.Reflection.Context.csproj index 8ca7664c3e265..09daf8ff6c46d 100644 --- a/src/libraries/System.Reflection.Context/src/System.Reflection.Context.csproj +++ b/src/libraries/System.Reflection.Context/src/System.Reflection.Context.csproj @@ -63,5 +63,6 @@ + - \ No newline at end of file + diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingAssembly.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingAssembly.cs index 689c85667228d..64c98012957c7 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingAssembly.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingAssembly.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Security; @@ -21,6 +22,7 @@ public DelegatingAssembly(Assembly assembly) UnderlyingAssembly = assembly; } + [RequiresAssemblyFiles(Message = "Calling 'System.Reflection.Assembly.Location' 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'", Url = "https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/il3000")] public override string Location { get { return UnderlyingAssembly.Location; } @@ -98,16 +100,19 @@ public override Type[] GetExportedTypes() return UnderlyingAssembly.GetExportedTypes(); } + [RequiresAssemblyFiles(Message = "Calling 'System.Reflection.Assembly.GetFile(string)' will throw for assemblies embedded in a single-file app", Url = "https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/il3001")] public override FileStream GetFile(string name) { return UnderlyingAssembly.GetFile(name); } + [RequiresAssemblyFiles(Message = "Calling 'System.Reflection.Assembly.GetFiles()' will throw for assemblies embedded in a single-file app", Url = "https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/il3001")] public override FileStream[] GetFiles() { return UnderlyingAssembly.GetFiles(); } + [RequiresAssemblyFiles(Message = "Calling 'System.Reflection.Assembly.GetFiles(bool)' will throw for assemblies embedded in a single-file app", Url = "https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/il3001")] public override FileStream[] GetFiles(bool getResourceModules) { return UnderlyingAssembly.GetFiles(getResourceModules); diff --git a/src/libraries/System.Reflection.MetadataLoadContext/src/System.Reflection.MetadataLoadContext.csproj b/src/libraries/System.Reflection.MetadataLoadContext/src/System.Reflection.MetadataLoadContext.csproj index bf769cd262db5..d021572a0e0c3 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/src/System.Reflection.MetadataLoadContext.csproj +++ b/src/libraries/System.Reflection.MetadataLoadContext/src/System.Reflection.MetadataLoadContext.csproj @@ -155,11 +155,17 @@ + + + + + + diff --git a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Assemblies/Ecma/EcmaAssembly.Modules.cs b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Assemblies/Ecma/EcmaAssembly.Modules.cs index 1866969c819de..c7cb4bd3143d8 100644 --- a/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Assemblies/Ecma/EcmaAssembly.Modules.cs +++ b/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/Assemblies/Ecma/EcmaAssembly.Modules.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection.Metadata; using System.Reflection.PortableExecutable; @@ -32,6 +33,8 @@ protected sealed override RoModule LoadModule(string moduleName, bool containsMe throw new FileNotFoundException(SR.Format(SR.FileNotFoundModule, moduleName)); } + [UnconditionalSuppressMessage("SingleFile", "IL3000: Avoid accessing Assembly file path when publishing as a single file", + Justification = "The code has a fallback using a ModuleResolveEventHandler")] private FileStream? FindModuleNextToAssembly(string moduleName) { Assembly containingAssembly = this; diff --git a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/RuntimeEnvironment.cs b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/RuntimeEnvironment.cs index 18b4113958cb3..5c4c8c7e53771 100644 --- a/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/RuntimeEnvironment.cs +++ b/src/libraries/System.Runtime.InteropServices/src/System/Runtime/InteropServices/RuntimeEnvironment.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection; @@ -13,6 +14,8 @@ public static class RuntimeEnvironment public static bool FromGlobalAccessCache(Assembly a) => false; + [UnconditionalSuppressMessage("SingleFile", "IL3000: Avoid accessing Assembly file path when publishing as a single file", + Justification = "This call is fine because the code handles the Assembly.Location equals null by calling AppDomain.CurrentDomain.BaseDirectory")] public static string GetRuntimeDirectory() { string? runtimeDirectory = typeof(object).Assembly.Location; diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 963f67ea75f71..a78df2b946602 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -7886,11 +7886,13 @@ public abstract partial class Assembly : System.Reflection.ICustomAttributeProvi { protected Assembly() { } [System.ObsoleteAttribute("Assembly.CodeBase and Assembly.EscapedCodeBase are only included for .NET Framework compatibility. Use Assembly.Location instead.", DiagnosticId = "SYSLIB0012", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] + [System.Diagnostics.CodeAnalysis.RequiresAssemblyFilesAttribute(Message="The code will throw for assemblies embedded in a single-file app")] public virtual string? CodeBase { get { throw null; } } public virtual System.Collections.Generic.IEnumerable CustomAttributes { get { throw null; } } public virtual System.Collections.Generic.IEnumerable DefinedTypes { [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Types might be removed")] get { throw null; } } public virtual System.Reflection.MethodInfo? EntryPoint { get { throw null; } } [System.ObsoleteAttribute("Assembly.CodeBase and Assembly.EscapedCodeBase are only included for .NET Framework compatibility. Use Assembly.Location instead.", DiagnosticId = "SYSLIB0012", UrlFormat = "https://aka.ms/dotnet-warnings/{0}")] + [System.Diagnostics.CodeAnalysis.RequiresAssemblyFilesAttribute(Message="The code will throw for assemblies embedded in a single-file app")] public virtual string EscapedCodeBase { get { throw null; } } public virtual System.Collections.Generic.IEnumerable ExportedTypes { [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Types might be removed")] get { throw null; } } public virtual string? FullName { get { throw null; } } @@ -7925,6 +7927,7 @@ public virtual event System.Reflection.ModuleResolveEventHandler? ModuleResolve [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Types might be removed")] public virtual System.Type[] GetExportedTypes() { throw null; } public virtual System.IO.FileStream? GetFile(string name) { throw null; } + [System.Diagnostics.CodeAnalysis.RequiresAssemblyFiles(Message = "The code will throw for assemblies embedded in a single-file app")] public virtual System.IO.FileStream[] GetFiles() { throw null; } public virtual System.IO.FileStream[] GetFiles(bool getResourceModules) { throw null; } [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute("Types might be removed")] @@ -8093,10 +8096,11 @@ public sealed partial class AssemblyName : System.ICloneable, System.Runtime.Ser { public AssemblyName() { } public AssemblyName(string assemblyName) { } - public string? CodeBase { get { throw null; } set { } } + public string? CodeBase { [System.Diagnostics.CodeAnalysis.RequiresAssemblyFilesAttribute(Message = "The code will return an empty string for assemblies embedded in a single-file app")] get { throw null; } set { } } public System.Reflection.AssemblyContentType ContentType { get { throw null; } set { } } public System.Globalization.CultureInfo? CultureInfo { get { throw null; } set { } } public string? CultureName { get { throw null; } set { } } + [System.Diagnostics.CodeAnalysis.RequiresAssemblyFilesAttribute(Message="The code will return an empty string for assemblies embedded in a single-file app")] public string? EscapedCodeBase { get { throw null; } } public System.Reflection.AssemblyNameFlags Flags { get { throw null; } set { } } public string FullName { get { throw null; } } diff --git a/src/libraries/tests.proj b/src/libraries/tests.proj index 096343ab2e8d3..11e851a2cee92 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -342,6 +342,7 @@ + diff --git a/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs b/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs index 0a32cc96c3e78..73de23d6fcc42 100644 --- a/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs +++ b/src/mono/wasm/debugger/BrowserDebugProxy/EvaluateExpression.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Reflection; @@ -210,6 +211,8 @@ private static async Task> ResolveIdentifiers(IEnumerable CompileAndRunTheExpression(string expression, MemberReferenceResolver resolver, CancellationToken token) { expression = expression.Trim(); diff --git a/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj b/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj index e7641b3ab1aad..23074621b8973 100644 --- a/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj +++ b/src/tasks/WasmAppBuilder/WasmAppBuilder.csproj @@ -3,9 +3,9 @@ $(NetCoreAppToolCurrent);$(TargetFrameworkForNETFramework) enable $(NoWarn),CA1050 - $(NoWarn),CS8604,CS8602 + false