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 a716f2d29f655..303f6e82d8351 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 12c4068842242..6ee3ca727a64b 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -7885,11 +7885,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; } } @@ -7924,6 +7926,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")] @@ -8092,10 +8095,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 5530af517ee69..82fb892cb7064 100644 --- a/src/libraries/tests.proj +++ b/src/libraries/tests.proj @@ -357,6 +357,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