From 5c79b801d7483e96d08970adec16d431f3db5a08 Mon Sep 17 00:00:00 2001 From: Mateo Torres Ruiz Date: Wed, 9 Dec 2020 11:18:59 -0800 Subject: [PATCH 1/5] Add design doc for single-file unsupported --- docs/design/single-file-unsupported.md | 63 ++++++++++++++++++++++++ src/linker/Resources/Strings.Designer.cs | 50 +++++++++---------- 2 files changed, 88 insertions(+), 25 deletions(-) create mode 100644 docs/design/single-file-unsupported.md diff --git a/docs/design/single-file-unsupported.md b/docs/design/single-file-unsupported.md new file mode 100644 index 000000000000..198ac135d392 --- /dev/null +++ b/docs/design/single-file-unsupported.md @@ -0,0 +1,63 @@ +# Single-file unsupported attribute + +Starting with .NET Core 3.0 users have the ability to bundle all files used by their applications into a single executable through the publish functionality, `PublishSingleFile`. Although most applications can be agnostic of whether they will be deployed as single-file, there exist some APIs that are known to be incompatible with this deployment mode and thus can cause single-file apps to crash. + +While the existing single-file analyzer can help us emit warnings whenever incompatible runtime library APIs are called from within user's code in a project that has been marked with the intent of being published as single-file (through setting `PublishSingleFile` to true in its .csproj), developers can still take a dependency on third-party components implementing functionality that inadvertently make their application no longer compatible with single-file. + +## User Experience + +For library authors, the new attribute will allow them to easily annotate APIs known to be problematic when used in the context of a single-file application. + +```C# + +``` + +Consumers of this library who want to publish their application as a single-file binary will commonly do it through setting the corresponding property in the application’s .csproj: + +```XML + + true + +``` + +If the user now makes use of the problematic API, a warning will be produced: + +> ILLink: Single-file warning ILXXXX: Foo.Bar(): This method is not compatible with single-file. Url. +```C# +[SingleFileUnsupported("This method is not compatible with single-file", "https://help")] +Bar() +{ + Assembly executingAssembly = Assembly.GetExecutingAssembly(); + var codeBase = executingAssembly.CodeBase; + ... +} +``` +The linker will exercise implicit suppression of all other single-file related warnings produced by code within the incompatible method. + +If a user is confident that the used API does not pose a problem, the known warning suppression mechanisms can be used. + +## Goal + +By adding this attribute, we expect to improve users experience by giving library authors a way to annotate their single-file incompatible APIs such that consumers get useful diagnostics when using them. Developers who build applications meant to be published as single-file binaries should not have to wait until running their program to find out that they are using incompatible APIs. + +## Design + +```C# +namespace System.Diagnostics.CodeAnalysis +{ + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor, Inherited = false, AllowMultiple = false)] + public sealed class SingleFileUnsupportedAttribute : Attribute + { + public SingleFileUnsupportedAttribute(string message) + { + Message = message; + } + + public string Message { get; } + + public string Url { get; set; } + } +} +``` + +## Q & A \ No newline at end of file diff --git a/src/linker/Resources/Strings.Designer.cs b/src/linker/Resources/Strings.Designer.cs index 43e5829115ff..8db417aabd5d 100644 --- a/src/linker/Resources/Strings.Designer.cs +++ b/src/linker/Resources/Strings.Designer.cs @@ -61,7 +61,7 @@ internal Strings() { } /// - /// Looks up a localized string similar to '{0}' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to '{1}'. The parameter '{2}' of method '{3}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' argument does not satisfy {4} in call to '{1}'. The parameter '{2}' of method '{3}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2067 { get { @@ -70,7 +70,7 @@ internal static string IL2067 { } /// - /// Looks up a localized string similar to '{0}' method return value does not satisfy 'DynamicallyAccessedMembersAttribute' requirements. The parameter '{1}' of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' method return value does not satisfy {3} requirements. The parameter '{1}' of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2068 { get { @@ -79,7 +79,7 @@ internal static string IL2068 { } /// - /// Looks up a localized string similar to '{0}' field does not satisfy 'DynamicallyAccessedMembersAttribute' requirements. The parameter '{1}' of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to value stored in field '{0}' does not satisfy {3} requirements. The parameter '{1}' of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2069 { get { @@ -88,7 +88,7 @@ internal static string IL2069 { } /// - /// Looks up a localized string similar to 'this' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to '{0}'. The parameter '{1}' of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to 'this' argument does not satisfy {3} in call to '{0}'. The parameter '{1}' of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2070 { get { @@ -97,7 +97,7 @@ internal static string IL2070 { } /// - /// Looks up a localized string similar to '{0}' generic argument does not satisfy 'DynamicallyAccessedMembersAttribute' in '{1}'. The parameter '{2}' of method '{3}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' generic argument does not satisfy {4} in '{1}'. The parameter '{2}' of method '{3}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2071 { get { @@ -106,7 +106,7 @@ internal static string IL2071 { } /// - /// Looks up a localized string similar to '{0}' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to '{1}'. The return value of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' argument does not satisfy {3} in call to '{1}'. The return value of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2072 { get { @@ -115,7 +115,7 @@ internal static string IL2072 { } /// - /// Looks up a localized string similar to '{0}' method return value does not satisfy 'DynamicallyAccessedMembersAttribute' requirements. The return value of method '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' method return value does not satisfy {2} requirements. The return value of method '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2073 { get { @@ -124,7 +124,7 @@ internal static string IL2073 { } /// - /// Looks up a localized string similar to '{0}' field does not satisfy 'DynamicallyAccessedMembersAttribute' requirements. The return value of method '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to value stored in field '{0}' does not satisfy {2} requirements. The return value of method '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2074 { get { @@ -133,7 +133,7 @@ internal static string IL2074 { } /// - /// Looks up a localized string similar to 'this' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to '{0}'. The return value of method '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to 'this' argument does not satisfy {2} in call to '{0}'. The return value of method '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2075 { get { @@ -142,7 +142,7 @@ internal static string IL2075 { } /// - /// Looks up a localized string similar to '{0}' generic argument does not satisfy 'DynamicallyAccessedMembersAttribute' in '{1}'. The return value of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' generic argument does not satisfy {3} in '{1}'. The return value of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2076 { get { @@ -151,7 +151,7 @@ internal static string IL2076 { } /// - /// Looks up a localized string similar to '{0}' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to '{1}'. The field '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' argument does not satisfy {3} in call to '{1}'. The field '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2077 { get { @@ -160,7 +160,7 @@ internal static string IL2077 { } /// - /// Looks up a localized string similar to '{0}' method return value does not satisfy 'DynamicallyAccessedMembersAttribute' requirements. The field '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' method return value does not satisfy {2} requirements. The field '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2078 { get { @@ -169,7 +169,7 @@ internal static string IL2078 { } /// - /// Looks up a localized string similar to '{0}' field does not satisfy 'DynamicallyAccessedMembersAttribute' requirements. The field '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to value stored in field '{0}' does not satisfy {2} requirements. The field '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2079 { get { @@ -178,7 +178,7 @@ internal static string IL2079 { } /// - /// Looks up a localized string similar to 'this' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to '{0}'. The field '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to 'this' argument does not satisfy {2} in call to '{0}'. The field '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2080 { get { @@ -187,7 +187,7 @@ internal static string IL2080 { } /// - /// Looks up a localized string similar to '{0}' generic argument does not satisfy 'DynamicallyAccessedMembersAttribute' in '{1}'. The field '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' generic argument does not satisfy {3} in '{1}'. The field '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2081 { get { @@ -196,7 +196,7 @@ internal static string IL2081 { } /// - /// Looks up a localized string similar to '{0}' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to '{1}'. The implicit 'this' argument of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' argument does not satisfy {3} in call to '{1}'. The implicit 'this' argument of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2082 { get { @@ -205,7 +205,7 @@ internal static string IL2082 { } /// - /// Looks up a localized string similar to '{0}' method return value does not satisfy 'DynamicallyAccessedMembersAttribute' requirements. The implicit 'this' argument of method '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' method return value does not satisfy {2} requirements. The implicit 'this' argument of method '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2083 { get { @@ -214,7 +214,7 @@ internal static string IL2083 { } /// - /// Looks up a localized string similar to '{0}' field does not satisfy 'DynamicallyAccessedMembersAttribute' requirements. The implicit 'this' argument of method '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to value stored in field '{0}' does not satisfy {2} requirements. The implicit 'this' argument of method '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2084 { get { @@ -223,7 +223,7 @@ internal static string IL2084 { } /// - /// Looks up a localized string similar to 'this' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to '{0}'. The implicit 'this' argument of method '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to 'this' argument does not satisfy {2} in call to '{0}'. The implicit 'this' argument of method '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2085 { get { @@ -232,7 +232,7 @@ internal static string IL2085 { } /// - /// Looks up a localized string similar to '{0}' generic argument does not satisfy 'DynamicallyAccessedMembersAttribute' in '{1}'. The implicit 'this' argument of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' generic argument does not satisfy {3} in '{1}'. The implicit 'this' argument of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2086 { get { @@ -241,7 +241,7 @@ internal static string IL2086 { } /// - /// Looks up a localized string similar to '{0}' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to '{1}'. The generic parameter '{2}' of '{3}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' argument does not satisfy {4} in call to '{1}'. The generic parameter '{2}' of '{3}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2087 { get { @@ -250,7 +250,7 @@ internal static string IL2087 { } /// - /// Looks up a localized string similar to '{0}' method return value does not satisfy 'DynamicallyAccessedMembersAttribute' requirements. The generic parameter '{1}' of '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' method return value does not satisfy {3} requirements. The generic parameter '{1}' of '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2088 { get { @@ -259,7 +259,7 @@ internal static string IL2088 { } /// - /// Looks up a localized string similar to '{0}' field does not satisfy 'DynamicallyAccessedMembersAttribute' requirements. The generic parameter '{1}' of '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to value stored in field '{0}' does not satisfy {3} requirements. The generic parameter '{1}' of '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2089 { get { @@ -268,7 +268,7 @@ internal static string IL2089 { } /// - /// Looks up a localized string similar to 'this' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to '{0}'. The generic parameter '{1}' of '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to 'this' argument does not satisfy {3} in call to '{0}'. The generic parameter '{1}' of '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2090 { get { @@ -277,7 +277,7 @@ internal static string IL2090 { } /// - /// Looks up a localized string similar to '{0}' generic argument does not satisfy 'DynamicallyAccessedMembersAttribute' in '{1}'. The generic parameter '{2}' of '{3}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' generic argument does not satisfy {4} in '{1}'. The generic parameter '{2}' of '{3}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2091 { get { From b6004a0d4ab1c655ab9229424b4c199dc10f8e71 Mon Sep 17 00:00:00 2001 From: Mateo Torres Ruiz Date: Wed, 9 Dec 2020 11:35:33 -0800 Subject: [PATCH 2/5] Revert file state --- src/linker/Resources/Strings.Designer.cs | 50 ++++++++++++------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/linker/Resources/Strings.Designer.cs b/src/linker/Resources/Strings.Designer.cs index 8db417aabd5d..43e5829115ff 100644 --- a/src/linker/Resources/Strings.Designer.cs +++ b/src/linker/Resources/Strings.Designer.cs @@ -61,7 +61,7 @@ internal Strings() { } /// - /// Looks up a localized string similar to '{0}' argument does not satisfy {4} in call to '{1}'. The parameter '{2}' of method '{3}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to '{1}'. The parameter '{2}' of method '{3}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2067 { get { @@ -70,7 +70,7 @@ internal static string IL2067 { } /// - /// Looks up a localized string similar to '{0}' method return value does not satisfy {3} requirements. The parameter '{1}' of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' method return value does not satisfy 'DynamicallyAccessedMembersAttribute' requirements. The parameter '{1}' of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2068 { get { @@ -79,7 +79,7 @@ internal static string IL2068 { } /// - /// Looks up a localized string similar to value stored in field '{0}' does not satisfy {3} requirements. The parameter '{1}' of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' field does not satisfy 'DynamicallyAccessedMembersAttribute' requirements. The parameter '{1}' of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2069 { get { @@ -88,7 +88,7 @@ internal static string IL2069 { } /// - /// Looks up a localized string similar to 'this' argument does not satisfy {3} in call to '{0}'. The parameter '{1}' of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to 'this' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to '{0}'. The parameter '{1}' of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2070 { get { @@ -97,7 +97,7 @@ internal static string IL2070 { } /// - /// Looks up a localized string similar to '{0}' generic argument does not satisfy {4} in '{1}'. The parameter '{2}' of method '{3}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' generic argument does not satisfy 'DynamicallyAccessedMembersAttribute' in '{1}'. The parameter '{2}' of method '{3}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2071 { get { @@ -106,7 +106,7 @@ internal static string IL2071 { } /// - /// Looks up a localized string similar to '{0}' argument does not satisfy {3} in call to '{1}'. The return value of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to '{1}'. The return value of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2072 { get { @@ -115,7 +115,7 @@ internal static string IL2072 { } /// - /// Looks up a localized string similar to '{0}' method return value does not satisfy {2} requirements. The return value of method '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' method return value does not satisfy 'DynamicallyAccessedMembersAttribute' requirements. The return value of method '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2073 { get { @@ -124,7 +124,7 @@ internal static string IL2073 { } /// - /// Looks up a localized string similar to value stored in field '{0}' does not satisfy {2} requirements. The return value of method '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' field does not satisfy 'DynamicallyAccessedMembersAttribute' requirements. The return value of method '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2074 { get { @@ -133,7 +133,7 @@ internal static string IL2074 { } /// - /// Looks up a localized string similar to 'this' argument does not satisfy {2} in call to '{0}'. The return value of method '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to 'this' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to '{0}'. The return value of method '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2075 { get { @@ -142,7 +142,7 @@ internal static string IL2075 { } /// - /// Looks up a localized string similar to '{0}' generic argument does not satisfy {3} in '{1}'. The return value of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' generic argument does not satisfy 'DynamicallyAccessedMembersAttribute' in '{1}'. The return value of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2076 { get { @@ -151,7 +151,7 @@ internal static string IL2076 { } /// - /// Looks up a localized string similar to '{0}' argument does not satisfy {3} in call to '{1}'. The field '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to '{1}'. The field '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2077 { get { @@ -160,7 +160,7 @@ internal static string IL2077 { } /// - /// Looks up a localized string similar to '{0}' method return value does not satisfy {2} requirements. The field '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' method return value does not satisfy 'DynamicallyAccessedMembersAttribute' requirements. The field '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2078 { get { @@ -169,7 +169,7 @@ internal static string IL2078 { } /// - /// Looks up a localized string similar to value stored in field '{0}' does not satisfy {2} requirements. The field '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' field does not satisfy 'DynamicallyAccessedMembersAttribute' requirements. The field '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2079 { get { @@ -178,7 +178,7 @@ internal static string IL2079 { } /// - /// Looks up a localized string similar to 'this' argument does not satisfy {2} in call to '{0}'. The field '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to 'this' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to '{0}'. The field '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2080 { get { @@ -187,7 +187,7 @@ internal static string IL2080 { } /// - /// Looks up a localized string similar to '{0}' generic argument does not satisfy {3} in '{1}'. The field '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' generic argument does not satisfy 'DynamicallyAccessedMembersAttribute' in '{1}'. The field '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2081 { get { @@ -196,7 +196,7 @@ internal static string IL2081 { } /// - /// Looks up a localized string similar to '{0}' argument does not satisfy {3} in call to '{1}'. The implicit 'this' argument of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to '{1}'. The implicit 'this' argument of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2082 { get { @@ -205,7 +205,7 @@ internal static string IL2082 { } /// - /// Looks up a localized string similar to '{0}' method return value does not satisfy {2} requirements. The implicit 'this' argument of method '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' method return value does not satisfy 'DynamicallyAccessedMembersAttribute' requirements. The implicit 'this' argument of method '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2083 { get { @@ -214,7 +214,7 @@ internal static string IL2083 { } /// - /// Looks up a localized string similar to value stored in field '{0}' does not satisfy {2} requirements. The implicit 'this' argument of method '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' field does not satisfy 'DynamicallyAccessedMembersAttribute' requirements. The implicit 'this' argument of method '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2084 { get { @@ -223,7 +223,7 @@ internal static string IL2084 { } /// - /// Looks up a localized string similar to 'this' argument does not satisfy {2} in call to '{0}'. The implicit 'this' argument of method '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to 'this' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to '{0}'. The implicit 'this' argument of method '{1}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2085 { get { @@ -232,7 +232,7 @@ internal static string IL2085 { } /// - /// Looks up a localized string similar to '{0}' generic argument does not satisfy {3} in '{1}'. The implicit 'this' argument of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' generic argument does not satisfy 'DynamicallyAccessedMembersAttribute' in '{1}'. The implicit 'this' argument of method '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2086 { get { @@ -241,7 +241,7 @@ internal static string IL2086 { } /// - /// Looks up a localized string similar to '{0}' argument does not satisfy {4} in call to '{1}'. The generic parameter '{2}' of '{3}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to '{1}'. The generic parameter '{2}' of '{3}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2087 { get { @@ -250,7 +250,7 @@ internal static string IL2087 { } /// - /// Looks up a localized string similar to '{0}' method return value does not satisfy {3} requirements. The generic parameter '{1}' of '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' method return value does not satisfy 'DynamicallyAccessedMembersAttribute' requirements. The generic parameter '{1}' of '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2088 { get { @@ -259,7 +259,7 @@ internal static string IL2088 { } /// - /// Looks up a localized string similar to value stored in field '{0}' does not satisfy {3} requirements. The generic parameter '{1}' of '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' field does not satisfy 'DynamicallyAccessedMembersAttribute' requirements. The generic parameter '{1}' of '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2089 { get { @@ -268,7 +268,7 @@ internal static string IL2089 { } /// - /// Looks up a localized string similar to 'this' argument does not satisfy {3} in call to '{0}'. The generic parameter '{1}' of '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to 'this' argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to '{0}'. The generic parameter '{1}' of '{2}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2090 { get { @@ -277,7 +277,7 @@ internal static string IL2090 { } /// - /// Looks up a localized string similar to '{0}' generic argument does not satisfy {4} in '{1}'. The generic parameter '{2}' of '{3}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. + /// Looks up a localized string similar to '{0}' generic argument does not satisfy 'DynamicallyAccessedMembersAttribute' in '{1}'. The generic parameter '{2}' of '{3}' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.. /// internal static string IL2091 { get { From fb779db43bf23ed55c1fe2a3a229e8b32fc08ba5 Mon Sep 17 00:00:00 2001 From: Mateo Torres Ruiz Date: Wed, 9 Dec 2020 11:43:29 -0800 Subject: [PATCH 3/5] Update example --- docs/design/single-file-unsupported.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/docs/design/single-file-unsupported.md b/docs/design/single-file-unsupported.md index 198ac135d392..df9efca5c536 100644 --- a/docs/design/single-file-unsupported.md +++ b/docs/design/single-file-unsupported.md @@ -9,7 +9,13 @@ While the existing single-file analyzer can help us emit warnings whenever incom For library authors, the new attribute will allow them to easily annotate APIs known to be problematic when used in the context of a single-file application. ```C# - +[SingleFileUnsupported("'Bar' method is not compatible with single-file", "https://help")] +void Bar() +{ + Assembly executingAssembly = Assembly.GetExecutingAssembly(); + var codeBase = executingAssembly.CodeBase; + ... +} ``` Consumers of this library who want to publish their application as a single-file binary will commonly do it through setting the corresponding property in the application’s .csproj: @@ -22,13 +28,12 @@ Consumers of this library who want to publish their application as a single-file If the user now makes use of the problematic API, a warning will be produced: -> ILLink: Single-file warning ILXXXX: Foo.Bar(): This method is not compatible with single-file. Url. +> File.cs(4,4): Single-file warning ILXXXX: Foo.CallBar(): 'Bar' method is not compatible with single-file. Url. ```C# -[SingleFileUnsupported("This method is not compatible with single-file", "https://help")] -Bar() +void CallBar() { - Assembly executingAssembly = Assembly.GetExecutingAssembly(); - var codeBase = executingAssembly.CodeBase; + ... + Bar(); ... } ``` From fbbf40db2a92fe68581baaeb30ece09b6c29eaeb Mon Sep 17 00:00:00 2001 From: Mateo Torres Ruiz Date: Wed, 16 Dec 2020 16:19:04 -0800 Subject: [PATCH 4/5] Update doc --- docs/design/single-file-unsupported.md | 118 +++++++++++++++++++++---- 1 file changed, 99 insertions(+), 19 deletions(-) diff --git a/docs/design/single-file-unsupported.md b/docs/design/single-file-unsupported.md index df9efca5c536..d7b86f4b9b3c 100644 --- a/docs/design/single-file-unsupported.md +++ b/docs/design/single-file-unsupported.md @@ -6,44 +6,111 @@ While the existing single-file analyzer can help us emit warnings whenever incom ## User Experience -For library authors, the new attribute will allow them to easily annotate APIs known to be problematic when used in the context of a single-file application. +The new attribute will allow developers to easily annotate their APIs known to be problematic when used in the context of a single-file application. This new attribute will first be used by the runtime team to annotate low level [APIs known to be incompatible with single-file](https://docs.microsoft.com/en-us/dotnet/core/deploying/single-file#api-incompatibility). Library authors will then rely on analyzers to know which parts of their code would either need to be fixed or marked with `SingleFileUnsupportedAttribute`. + +### Example: API writer + +Ben is writing a library which he reckons some of its consumers might want to use in the context of a single-file app, so he decides to see if any warning is produced when `PublishSingleFile` is set in the .csproj file. After doing this, the following code gets a diagnostic produced by the single-file analyzer: + +> FileTable.cs(3,12): Single-file warning ILXXXX: 'System.Reflection.Assembly.GetFiles()' will throw for assemblies embedded in a single-file app. + +```C# +public int FileTableLength(Assembly assembly) +{ + return assembly.GetFiles().Length; +} +``` + +He then decides to annotate the method so that users who intent to deploy their app as a single-file binary know that `FileTableLength` will not be safe to use, and can either wrap the method call in a try-catch block or work around using it. + +```C# +[SingleFileUnsupported("This method will throw for single-file apps.", "https://helpurl")] +public int FileTableLength(Assembly assembly) +``` + +### Example: API consumer + +Maria is writing an application which keeps track of all times it has ran in a log next to the running assembly. For this purpose she uses `Assembly.Location`, which is marked with the new `SingleFileUnsupportedAttribute`. ```C# -[SingleFileUnsupported("'Bar' method is not compatible with single-file", "https://help")] -void Bar() +void RecordApplicationRun() { - Assembly executingAssembly = Assembly.GetExecutingAssembly(); - var codeBase = executingAssembly.CodeBase; - ... + var path = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase), "runs.log"); + using (StreamWriter sw = File.AppendText(path)) + sw.WriteLine(DateTime.Now.ToString()); } ``` -Consumers of this library who want to publish their application as a single-file binary will commonly do it through setting the corresponding property in the application’s .csproj: +After some time she decides to make her application a single-file app, so that she can easily share it with friends. She then adds the `PublishSingleFile` property to the .csproj. ```XML - + + + + Exe + net5.0 true - + + + +``` + +Now that she has expressed the intent to deploy the app as single-file binary, the following diagnostic is shown in her code: + +> Logger.cs(x,y): Single-file warning ILXXXX: Logger.RecordApplicationRun(): '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'. + +She now has the option to either suppress the warning or work around the highlighted method. + +### Detecting incompatible APIs + +Annotated public APIs that are directly called by the user's code will trigger the single-file analyzer to produce a diagnostic visible both in console and IDE. However, Roslyn analyzers have limited visibility of external code and cannot see methods which are indirectly called. For these cases a global analyzer is needed in order to report the diagnostics, this analyzer must be capable of seeing the entirety of the code used by the application. + +### Suppressing the warning + +For a method causing a diagnostic to be produced, the user can choose between following the recommended guidance given by the diagnostic message to fix the problem or suppress the warning via the known warning suppression mechanisms. + +To give an example, if a hypothetical consumer of Ben's library (shown [above](#example-api-writer)) decides to use the `FileTableLength` method even when publishing as single-file, the method call could be put inside a try-catch and the warning suppressed: + +```C# +[UnconditionalSuppressMessage("ILXXXX")] +void Method(Assembly assembly) +{ + try + { + Ben.FileTableLength(assembly); + } + catch + { + GetTheFileTableLengthSomeOtherWay(assembly); + } +} ``` -If the user now makes use of the problematic API, a warning will be produced: +The single-file analyzer as well as the global analyzer will exercise implicit suppression of all single-file related warnings which are produced by annotated methods called within an incompatible method. -> File.cs(4,4): Single-file warning ILXXXX: Foo.CallBar(): 'Bar' method is not compatible with single-file. Url. ```C# -void CallBar() +// Program.cs(3,4): Single-file warning ILXXXX: This method cannot be used in the context of a single-file application. +void A() { - ... - Bar(); - ... + B(); } + +// No diagnostic will be produced here, since the caller already produced a single-file diagnostic. +[SingleFileUnsupported("This method cannot be used in the context of a single-file application.")] +void B() +{ + C(); +} + +[SingleFileUnsupported("This method cannot be used in the context of a single-file application.")] +void C() { } ``` -The linker will exercise implicit suppression of all other single-file related warnings produced by code within the incompatible method. -If a user is confident that the used API does not pose a problem, the known warning suppression mechanisms can be used. + ## Goal -By adding this attribute, we expect to improve users experience by giving library authors a way to annotate their single-file incompatible APIs such that consumers get useful diagnostics when using them. Developers who build applications meant to be published as single-file binaries should not have to wait until running their program to find out that they are using incompatible APIs. +By adding this attribute, we expect to improve users experience by giving developers a way to annotate their single-file incompatible APIs such that consumers get useful diagnostics when using them. Developers who build applications meant to be published as single-file binaries should not have to wait until running their program to find out that they are using APIs that might cause their apps to break. ## Design @@ -65,4 +132,17 @@ namespace System.Diagnostics.CodeAnalysis } ``` -## Q & A \ No newline at end of file +## Q & A +--- + +### How are library authors going to know where to add the attribute? + +Initially, the runtime team will annotate existing APIs known to be incompatible with single-file. Library developers will have to use analyzers to get diagnostics in their code pinpointing all method calls that are problematic when used with single-file. + +### Which tool is responsible for emiting the warnings related with the attribute? + +The single-file analyzer as well as a global analyzer. + +### Is suppression the only way to mitigate the warning? + +Currently, yes. In the future, a new API can be introduced to guard the calls to methods marked as incompatible with single-file. \ No newline at end of file From 5ae99c0750424fd1bb93b09849738ccc940193a2 Mon Sep 17 00:00:00 2001 From: Mateo Torres Ruiz Date: Wed, 16 Dec 2020 17:03:35 -0800 Subject: [PATCH 5/5] Fix warning message, remove line --- docs/design/single-file-unsupported.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/design/single-file-unsupported.md b/docs/design/single-file-unsupported.md index d7b86f4b9b3c..25664b634d75 100644 --- a/docs/design/single-file-unsupported.md +++ b/docs/design/single-file-unsupported.md @@ -57,7 +57,7 @@ After some time she decides to make her application a single-file app, so that s Now that she has expressed the intent to deploy the app as single-file binary, the following diagnostic is shown in her code: -> Logger.cs(x,y): Single-file warning ILXXXX: Logger.RecordApplicationRun(): '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'. +> Logger.cs(x,y): Single-file warning ILXXXX: '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'. She now has the option to either suppress the warning or work around the highlighted method. @@ -133,7 +133,6 @@ namespace System.Diagnostics.CodeAnalysis ``` ## Q & A ---- ### How are library authors going to know where to add the attribute?