diff options
24 files changed, 650 insertions, 28 deletions
diff --git a/src/ILLink.RoslynAnalyzer/AnalyzerOptionsExtensions.cs b/src/ILLink.RoslynAnalyzer/AnalyzerOptionsExtensions.cs new file mode 100644 index 000000000..f5781802f --- /dev/null +++ b/src/ILLink.RoslynAnalyzer/AnalyzerOptionsExtensions.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace ILLink.RoslynAnalyzer +{ + internal static class AnalyzerOptionsExtensions + { + public static string? GetMSBuildPropertyValue ( + this AnalyzerOptions options, + string optionName, + Compilation compilation) + { + // MSBuild property values should be set at compilation level, and cannot have different values per-tree. + // So, we default to first syntax tree. + var tree = compilation.SyntaxTrees.FirstOrDefault (); + if (tree is null) { + return null; + } + + return options.AnalyzerConfigOptionsProvider.GlobalOptions.TryGetValue ( + $"build_property.{optionName}", out var value) + ? value + : null; + } + } +} diff --git a/src/ILLink.RoslynAnalyzer/DiagnosticCategory.cs b/src/ILLink.RoslynAnalyzer/DiagnosticCategory.cs new file mode 100644 index 000000000..28cf0f031 --- /dev/null +++ b/src/ILLink.RoslynAnalyzer/DiagnosticCategory.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace ILLink.RoslynAnalyzer +{ + internal static class DiagnosticCategory + { + public const string SingleFile = nameof (SingleFile); + public const string Trimming = nameof (Trimming); + } +} diff --git a/src/ILLink.RoslynAnalyzer/MSBuildPropertyOptionNames.cs b/src/ILLink.RoslynAnalyzer/MSBuildPropertyOptionNames.cs new file mode 100644 index 000000000..1f0b40459 --- /dev/null +++ b/src/ILLink.RoslynAnalyzer/MSBuildPropertyOptionNames.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace ILLink.RoslynAnalyzer +{ + public static class MSBuildPropertyOptionNames + { + public const string PublishSingleFile = nameof (PublishSingleFile); + public const string IncludeAllContentForSelfExtract = nameof (IncludeAllContentForSelfExtract); + public const string PublishTrimmed = nameof (PublishTrimmed); + } +} diff --git a/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs b/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs index 1dd287114..8b9c1cd7b 100644 --- a/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs +++ b/src/ILLink.RoslynAnalyzer/RequiresUnreferencedCodeAnalyzer.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Immutable; -using System.Diagnostics; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Operations; @@ -16,21 +15,13 @@ namespace ILLink.RoslynAnalyzer { public const string DiagnosticId = "IL2026"; - private static readonly LocalizableString s_title = new LocalizableResourceString ( - nameof (RequiresUnreferencedCodeAnalyzer) + "Title", - Resources.ResourceManager, - typeof (Resources)); - private static readonly LocalizableString s_messageFormat = new LocalizableResourceString ( - nameof (RequiresUnreferencedCodeAnalyzer) + "Message", - Resources.ResourceManager, - typeof (Resources)); - private const string s_category = "Trimming"; - private static readonly DiagnosticDescriptor s_rule = new DiagnosticDescriptor ( DiagnosticId, - s_title, - s_messageFormat, - s_category, + new LocalizableResourceString (nameof (Resources.RequiresUnreferencedCodeAnalyzerTitle), + Resources.ResourceManager, typeof (Resources)), + new LocalizableResourceString (nameof (Resources.RequiresUnreferencedCodeAnalyzerMessage), + Resources.ResourceManager, typeof (Resources)), + DiagnosticCategory.Trimming, DiagnosticSeverity.Warning, isEnabledByDefault: true); @@ -44,8 +35,16 @@ namespace ILLink.RoslynAnalyzer context.RegisterCompilationStartAction (context => { var compilation = context.Compilation; + var isPublishTrimmed = context.Options.GetMSBuildPropertyValue (MSBuildPropertyOptionNames.PublishTrimmed, compilation); + if (!string.Equals (isPublishTrimmed?.Trim (), "true", StringComparison.OrdinalIgnoreCase)) { + return; + } + context.RegisterOperationAction (operationContext => { var call = (IInvocationOperation) operationContext.Operation; + if (call.IsVirtual && call.TargetMethod.OverriddenMethod != null) + return; + CheckMethodOrCtorCall (operationContext, call.TargetMethod, call.Syntax.GetLocation ()); }, OperationKind.Invocation); diff --git a/src/ILLink.RoslynAnalyzer/Resources.resx b/src/ILLink.RoslynAnalyzer/Resources.resx index 0c0780e4d..202514e6d 100644 --- a/src/ILLink.RoslynAnalyzer/Resources.resx +++ b/src/ILLink.RoslynAnalyzer/Resources.resx @@ -123,4 +123,16 @@ <data name="RequiresUnreferencedCodeAnalyzerMessage" xml:space="preserve"> <value>Calling '{0}' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. {1}. {2}</value> </data> + <data name="AvoidAssemblyLocationInSingleFileTitle" xml:space="preserve"> + <value>Avoid accessing Assembly file path when publishing as a single file</value> + </data> + <data name="AvoidAssemblyLocationInSingleFileMessage" xml:space="preserve"> + <value>'{0}' 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'.</value> + </data> + <data name="AvoidAssemblyGetFilesInSingleFileTitle" xml:space="preserve"> + <value>Avoid accessing Assembly file path when publishing as a single file</value> + </data> + <data name="AvoidAssemblyGetFilesInSingleFileMessage" xml:space="preserve"> + <value>'{0}' will throw for assemblies embedded in a single-file app</value> + </data> </root>
\ No newline at end of file diff --git a/src/ILLink.RoslynAnalyzer/SingleFileAnalyzer.cs b/src/ILLink.RoslynAnalyzer/SingleFileAnalyzer.cs new file mode 100644 index 000000000..536cd82f2 --- /dev/null +++ b/src/ILLink.RoslynAnalyzer/SingleFileAnalyzer.cs @@ -0,0 +1,141 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Operations; + +namespace ILLink.RoslynAnalyzer +{ + /// <summary> + /// IL3000, IL3001: Do not use Assembly file path in single-file publish + /// </summary> + [DiagnosticAnalyzer (LanguageNames.CSharp, LanguageNames.VisualBasic)] + public sealed class AvoidAssemblyLocationInSingleFile : DiagnosticAnalyzer + { + public const string IL3000 = nameof (IL3000); + public const string IL3001 = nameof (IL3001); + + private static readonly DiagnosticDescriptor LocationRule = new DiagnosticDescriptor ( + IL3000, + new LocalizableResourceString (nameof (Resources.AvoidAssemblyLocationInSingleFileTitle), + Resources.ResourceManager, typeof (Resources)), + new LocalizableResourceString (nameof (Resources.AvoidAssemblyLocationInSingleFileMessage), + Resources.ResourceManager, typeof (Resources)), + DiagnosticCategory.SingleFile, + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + private static readonly DiagnosticDescriptor GetFilesRule = new DiagnosticDescriptor ( + IL3001, + new LocalizableResourceString (nameof (Resources.AvoidAssemblyGetFilesInSingleFileTitle), + Resources.ResourceManager, typeof (Resources)), + new LocalizableResourceString (nameof (Resources.AvoidAssemblyGetFilesInSingleFileMessage), + Resources.ResourceManager, typeof (Resources)), + DiagnosticCategory.SingleFile, + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create (LocationRule, GetFilesRule); + + public override void Initialize (AnalysisContext context) + { + context.EnableConcurrentExecution (); + context.ConfigureGeneratedCodeAnalysis (GeneratedCodeAnalysisFlags.ReportDiagnostics); + + context.RegisterCompilationStartAction (context => { + var compilation = context.Compilation; + + var isSingleFilePublish = context.Options.GetMSBuildPropertyValue (MSBuildPropertyOptionNames.PublishSingleFile, compilation); + if (!string.Equals (isSingleFilePublish?.Trim (), "true", StringComparison.OrdinalIgnoreCase)) { + return; + } + var includesAllContent = context.Options.GetMSBuildPropertyValue (MSBuildPropertyOptionNames.IncludeAllContentForSelfExtract, compilation); + if (string.Equals (includesAllContent?.Trim (), "true", StringComparison.OrdinalIgnoreCase)) { + return; + } + + var propertiesBuilder = ImmutableArray.CreateBuilder<IPropertySymbol> (); + var methodsBuilder = ImmutableArray.CreateBuilder<IMethodSymbol> (); + + var assemblyType = compilation.GetTypeByMetadataName ("System.Reflection.Assembly"); + if (assemblyType != null) { + // properties + AddIfNotNull (propertiesBuilder, TryGetSingleSymbol<IPropertySymbol> (assemblyType.GetMembers ("Location"))); + + // methods + methodsBuilder.AddRange (assemblyType.GetMembers ("GetFile").OfType<IMethodSymbol> ()); + methodsBuilder.AddRange (assemblyType.GetMembers ("GetFiles").OfType<IMethodSymbol> ()); + } + + var assemblyNameType = compilation.GetTypeByMetadataName ("System.Reflection.AssemblyName"); + if (assemblyNameType != null) { + AddIfNotNull (propertiesBuilder, TryGetSingleSymbol<IPropertySymbol> (assemblyNameType.GetMembers ("CodeBase"))); + AddIfNotNull (propertiesBuilder, TryGetSingleSymbol<IPropertySymbol> (assemblyNameType.GetMembers ("EscapedCodeBase"))); + } + + var properties = propertiesBuilder.ToImmutable (); + var methods = methodsBuilder.ToImmutable (); + + context.RegisterOperationAction (operationContext => { + var access = (IPropertyReferenceOperation) operationContext.Operation; + var property = access.Property; + if (!Contains (properties, property, SymbolEqualityComparer.Default)) { + return; + } + + operationContext.ReportDiagnostic (Diagnostic.Create (LocationRule, access.Syntax.GetLocation (), property)); + }, OperationKind.PropertyReference); + + context.RegisterOperationAction (operationContext => { + var invocation = (IInvocationOperation) operationContext.Operation; + var targetMethod = invocation.TargetMethod; + if (!Contains (methods, targetMethod, SymbolEqualityComparer.Default)) { + return; + } + + operationContext.ReportDiagnostic (Diagnostic.Create (GetFilesRule, invocation.Syntax.GetLocation (), targetMethod)); + }, OperationKind.Invocation); + + return; + + static bool Contains<T, TComp> (ImmutableArray<T> list, T elem, TComp comparer) + where TComp : IEqualityComparer<T> + { + foreach (var e in list) { + if (comparer.Equals (e, elem)) { + return true; + } + } + return false; + } + + static TSymbol? TryGetSingleSymbol<TSymbol> (ImmutableArray<ISymbol> members) where TSymbol : class, ISymbol + { + TSymbol? candidate = null; + foreach (var m in members) { + if (m is TSymbol tsym) { + if (candidate is null) { + candidate = tsym; + } else { + return null; + } + } + } + return candidate; + } + + static void AddIfNotNull<TSymbol> (ImmutableArray<TSymbol>.Builder properties, TSymbol? p) where TSymbol : class, ISymbol + { + if (p != null) { + properties.Add (p); + } + } + }); + } + } +} diff --git a/src/ILLink.RoslynAnalyzer/xlf/Resources.cs.xlf b/src/ILLink.RoslynAnalyzer/xlf/Resources.cs.xlf index 5c5e4af14..1c0aab648 100644 --- a/src/ILLink.RoslynAnalyzer/xlf/Resources.cs.xlf +++ b/src/ILLink.RoslynAnalyzer/xlf/Resources.cs.xlf @@ -2,6 +2,26 @@ <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd"> <file datatype="xml" source-language="en" target-language="cs" original="../Resources.resx"> <body> + <trans-unit id="AvoidAssemblyGetFilesInSingleFileMessage"> + <source>'{0}' will throw for assemblies embedded in a single-file app</source> + <target state="new">'{0}' will throw for assemblies embedded in a single-file app</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyGetFilesInSingleFileTitle"> + <source>Avoid accessing Assembly file path when publishing as a single file</source> + <target state="new">Avoid accessing Assembly file path when publishing as a single file</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyLocationInSingleFileMessage"> + <source>'{0}' 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'.</source> + <target state="new">'{0}' 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'.</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyLocationInSingleFileTitle"> + <source>Avoid accessing Assembly file path when publishing as a single file</source> + <target state="new">Avoid accessing Assembly file path when publishing as a single file</target> + <note /> + </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerMessage"> <source>Calling '{0}' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. {1}. {2}</source> <target state="new">Calling '{0}' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. {1}. {2}</target> diff --git a/src/ILLink.RoslynAnalyzer/xlf/Resources.de.xlf b/src/ILLink.RoslynAnalyzer/xlf/Resources.de.xlf index a80b9d4fa..30f09f0f3 100644 --- a/src/ILLink.RoslynAnalyzer/xlf/Resources.de.xlf +++ b/src/ILLink.RoslynAnalyzer/xlf/Resources.de.xlf @@ -2,6 +2,26 @@ <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd"> <file datatype="xml" source-language="en" target-language="de" original="../Resources.resx"> <body> + <trans-unit id="AvoidAssemblyGetFilesInSingleFileMessage"> + <source>'{0}' will throw for assemblies embedded in a single-file app</source> + <target state="new">'{0}' will throw for assemblies embedded in a single-file app</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyGetFilesInSingleFileTitle"> + <source>Avoid accessing Assembly file path when publishing as a single file</source> + <target state="new">Avoid accessing Assembly file path when publishing as a single file</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyLocationInSingleFileMessage"> + <source>'{0}' 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'.</source> + <target state="new">'{0}' 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'.</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyLocationInSingleFileTitle"> + <source>Avoid accessing Assembly file path when publishing as a single file</source> + <target state="new">Avoid accessing Assembly file path when publishing as a single file</target> + <note /> + </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerMessage"> <source>Calling '{0}' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. {1}. {2}</source> <target state="new">Calling '{0}' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. {1}. {2}</target> diff --git a/src/ILLink.RoslynAnalyzer/xlf/Resources.es.xlf b/src/ILLink.RoslynAnalyzer/xlf/Resources.es.xlf index 46cf19175..10648e7f4 100644 --- a/src/ILLink.RoslynAnalyzer/xlf/Resources.es.xlf +++ b/src/ILLink.RoslynAnalyzer/xlf/Resources.es.xlf @@ -2,6 +2,26 @@ <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd"> <file datatype="xml" source-language="en" target-language="es" original="../Resources.resx"> <body> + <trans-unit id="AvoidAssemblyGetFilesInSingleFileMessage"> + <source>'{0}' will throw for assemblies embedded in a single-file app</source> + <target state="new">'{0}' will throw for assemblies embedded in a single-file app</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyGetFilesInSingleFileTitle"> + <source>Avoid accessing Assembly file path when publishing as a single file</source> + <target state="new">Avoid accessing Assembly file path when publishing as a single file</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyLocationInSingleFileMessage"> + <source>'{0}' 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'.</source> + <target state="new">'{0}' 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'.</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyLocationInSingleFileTitle"> + <source>Avoid accessing Assembly file path when publishing as a single file</source> + <target state="new">Avoid accessing Assembly file path when publishing as a single file</target> + <note /> + </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerMessage"> <source>Calling '{0}' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. {1}. {2}</source> <target state="new">Calling '{0}' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. {1}. {2}</target> diff --git a/src/ILLink.RoslynAnalyzer/xlf/Resources.fr.xlf b/src/ILLink.RoslynAnalyzer/xlf/Resources.fr.xlf index c147c8c1d..1d4195817 100644 --- a/src/ILLink.RoslynAnalyzer/xlf/Resources.fr.xlf +++ b/src/ILLink.RoslynAnalyzer/xlf/Resources.fr.xlf @@ -2,6 +2,26 @@ <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd"> <file datatype="xml" source-language="en" target-language="fr" original="../Resources.resx"> <body> + <trans-unit id="AvoidAssemblyGetFilesInSingleFileMessage"> + <source>'{0}' will throw for assemblies embedded in a single-file app</source> + <target state="new">'{0}' will throw for assemblies embedded in a single-file app</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyGetFilesInSingleFileTitle"> + <source>Avoid accessing Assembly file path when publishing as a single file</source> + <target state="new">Avoid accessing Assembly file path when publishing as a single file</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyLocationInSingleFileMessage"> + <source>'{0}' 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'.</source> + <target state="new">'{0}' 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'.</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyLocationInSingleFileTitle"> + <source>Avoid accessing Assembly file path when publishing as a single file</source> + <target state="new">Avoid accessing Assembly file path when publishing as a single file</target> + <note /> + </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerMessage"> <source>Calling '{0}' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. {1}. {2}</source> <target state="new">Calling '{0}' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. {1}. {2}</target> diff --git a/src/ILLink.RoslynAnalyzer/xlf/Resources.it.xlf b/src/ILLink.RoslynAnalyzer/xlf/Resources.it.xlf index 6c3fbcd90..5090e344e 100644 --- a/src/ILLink.RoslynAnalyzer/xlf/Resources.it.xlf +++ b/src/ILLink.RoslynAnalyzer/xlf/Resources.it.xlf @@ -2,6 +2,26 @@ <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd"> <file datatype="xml" source-language="en" target-language="it" original="../Resources.resx"> <body> + <trans-unit id="AvoidAssemblyGetFilesInSingleFileMessage"> + <source>'{0}' will throw for assemblies embedded in a single-file app</source> + <target state="new">'{0}' will throw for assemblies embedded in a single-file app</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyGetFilesInSingleFileTitle"> + <source>Avoid accessing Assembly file path when publishing as a single file</source> + <target state="new">Avoid accessing Assembly file path when publishing as a single file</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyLocationInSingleFileMessage"> + <source>'{0}' 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'.</source> + <target state="new">'{0}' 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'.</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyLocationInSingleFileTitle"> + <source>Avoid accessing Assembly file path when publishing as a single file</source> + <target state="new">Avoid accessing Assembly file path when publishing as a single file</target> + <note /> + </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerMessage"> <source>Calling '{0}' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. {1}. {2}</source> <target state="new">Calling '{0}' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. {1}. {2}</target> diff --git a/src/ILLink.RoslynAnalyzer/xlf/Resources.ja.xlf b/src/ILLink.RoslynAnalyzer/xlf/Resources.ja.xlf index 38814260c..8c07ebe5d 100644 --- a/src/ILLink.RoslynAnalyzer/xlf/Resources.ja.xlf +++ b/src/ILLink.RoslynAnalyzer/xlf/Resources.ja.xlf @@ -2,6 +2,26 @@ <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd"> <file datatype="xml" source-language="en" target-language="ja" original="../Resources.resx"> <body> + <trans-unit id="AvoidAssemblyGetFilesInSingleFileMessage"> + <source>'{0}' will throw for assemblies embedded in a single-file app</source> + <target state="new">'{0}' will throw for assemblies embedded in a single-file app</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyGetFilesInSingleFileTitle"> + <source>Avoid accessing Assembly file path when publishing as a single file</source> + <target state="new">Avoid accessing Assembly file path when publishing as a single file</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyLocationInSingleFileMessage"> + <source>'{0}' 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'.</source> + <target state="new">'{0}' 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'.</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyLocationInSingleFileTitle"> + <source>Avoid accessing Assembly file path when publishing as a single file</source> + <target state="new">Avoid accessing Assembly file path when publishing as a single file</target> + <note /> + </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerMessage"> <source>Calling '{0}' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. {1}. {2}</source> <target state="new">Calling '{0}' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. {1}. {2}</target> diff --git a/src/ILLink.RoslynAnalyzer/xlf/Resources.ko.xlf b/src/ILLink.RoslynAnalyzer/xlf/Resources.ko.xlf index db861065b..363b9c427 100644 --- a/src/ILLink.RoslynAnalyzer/xlf/Resources.ko.xlf +++ b/src/ILLink.RoslynAnalyzer/xlf/Resources.ko.xlf @@ -2,6 +2,26 @@ <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd"> <file datatype="xml" source-language="en" target-language="ko" original="../Resources.resx"> <body> + <trans-unit id="AvoidAssemblyGetFilesInSingleFileMessage"> + <source>'{0}' will throw for assemblies embedded in a single-file app</source> + <target state="new">'{0}' will throw for assemblies embedded in a single-file app</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyGetFilesInSingleFileTitle"> + <source>Avoid accessing Assembly file path when publishing as a single file</source> + <target state="new">Avoid accessing Assembly file path when publishing as a single file</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyLocationInSingleFileMessage"> + <source>'{0}' 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'.</source> + <target state="new">'{0}' 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'.</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyLocationInSingleFileTitle"> + <source>Avoid accessing Assembly file path when publishing as a single file</source> + <target state="new">Avoid accessing Assembly file path when publishing as a single file</target> + <note /> + </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerMessage"> <source>Calling '{0}' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. {1}. {2}</source> <target state="new">Calling '{0}' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. {1}. {2}</target> diff --git a/src/ILLink.RoslynAnalyzer/xlf/Resources.pl.xlf b/src/ILLink.RoslynAnalyzer/xlf/Resources.pl.xlf index 7ba7c0e27..927764ec5 100644 --- a/src/ILLink.RoslynAnalyzer/xlf/Resources.pl.xlf +++ b/src/ILLink.RoslynAnalyzer/xlf/Resources.pl.xlf @@ -2,6 +2,26 @@ <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd"> <file datatype="xml" source-language="en" target-language="pl" original="../Resources.resx"> <body> + <trans-unit id="AvoidAssemblyGetFilesInSingleFileMessage"> + <source>'{0}' will throw for assemblies embedded in a single-file app</source> + <target state="new">'{0}' will throw for assemblies embedded in a single-file app</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyGetFilesInSingleFileTitle"> + <source>Avoid accessing Assembly file path when publishing as a single file</source> + <target state="new">Avoid accessing Assembly file path when publishing as a single file</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyLocationInSingleFileMessage"> + <source>'{0}' 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'.</source> + <target state="new">'{0}' 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'.</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyLocationInSingleFileTitle"> + <source>Avoid accessing Assembly file path when publishing as a single file</source> + <target state="new">Avoid accessing Assembly file path when publishing as a single file</target> + <note /> + </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerMessage"> <source>Calling '{0}' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. {1}. {2}</source> <target state="new">Calling '{0}' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. {1}. {2}</target> diff --git a/src/ILLink.RoslynAnalyzer/xlf/Resources.pt-BR.xlf b/src/ILLink.RoslynAnalyzer/xlf/Resources.pt-BR.xlf index 3c748ed32..f0c021bcc 100644 --- a/src/ILLink.RoslynAnalyzer/xlf/Resources.pt-BR.xlf +++ b/src/ILLink.RoslynAnalyzer/xlf/Resources.pt-BR.xlf @@ -2,6 +2,26 @@ <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd"> <file datatype="xml" source-language="en" target-language="pt-BR" original="../Resources.resx"> <body> + <trans-unit id="AvoidAssemblyGetFilesInSingleFileMessage"> + <source>'{0}' will throw for assemblies embedded in a single-file app</source> + <target state="new">'{0}' will throw for assemblies embedded in a single-file app</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyGetFilesInSingleFileTitle"> + <source>Avoid accessing Assembly file path when publishing as a single file</source> + <target state="new">Avoid accessing Assembly file path when publishing as a single file</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyLocationInSingleFileMessage"> + <source>'{0}' 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'.</source> + <target state="new">'{0}' 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'.</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyLocationInSingleFileTitle"> + <source>Avoid accessing Assembly file path when publishing as a single file</source> + <target state="new">Avoid accessing Assembly file path when publishing as a single file</target> + <note /> + </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerMessage"> <source>Calling '{0}' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. {1}. {2}</source> <target state="new">Calling '{0}' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. {1}. {2}</target> diff --git a/src/ILLink.RoslynAnalyzer/xlf/Resources.ru.xlf b/src/ILLink.RoslynAnalyzer/xlf/Resources.ru.xlf index 4820e55e8..afbc27f9b 100644 --- a/src/ILLink.RoslynAnalyzer/xlf/Resources.ru.xlf +++ b/src/ILLink.RoslynAnalyzer/xlf/Resources.ru.xlf @@ -2,6 +2,26 @@ <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd"> <file datatype="xml" source-language="en" target-language="ru" original="../Resources.resx"> <body> + <trans-unit id="AvoidAssemblyGetFilesInSingleFileMessage"> + <source>'{0}' will throw for assemblies embedded in a single-file app</source> + <target state="new">'{0}' will throw for assemblies embedded in a single-file app</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyGetFilesInSingleFileTitle"> + <source>Avoid accessing Assembly file path when publishing as a single file</source> + <target state="new">Avoid accessing Assembly file path when publishing as a single file</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyLocationInSingleFileMessage"> + <source>'{0}' 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'.</source> + <target state="new">'{0}' 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'.</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyLocationInSingleFileTitle"> + <source>Avoid accessing Assembly file path when publishing as a single file</source> + <target state="new">Avoid accessing Assembly file path when publishing as a single file</target> + <note /> + </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerMessage"> <source>Calling '{0}' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. {1}. {2}</source> <target state="new">Calling '{0}' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. {1}. {2}</target> diff --git a/src/ILLink.RoslynAnalyzer/xlf/Resources.tr.xlf b/src/ILLink.RoslynAnalyzer/xlf/Resources.tr.xlf index 9b85a9a3b..c1cf60671 100644 --- a/src/ILLink.RoslynAnalyzer/xlf/Resources.tr.xlf +++ b/src/ILLink.RoslynAnalyzer/xlf/Resources.tr.xlf @@ -2,6 +2,26 @@ <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd"> <file datatype="xml" source-language="en" target-language="tr" original="../Resources.resx"> <body> + <trans-unit id="AvoidAssemblyGetFilesInSingleFileMessage"> + <source>'{0}' will throw for assemblies embedded in a single-file app</source> + <target state="new">'{0}' will throw for assemblies embedded in a single-file app</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyGetFilesInSingleFileTitle"> + <source>Avoid accessing Assembly file path when publishing as a single file</source> + <target state="new">Avoid accessing Assembly file path when publishing as a single file</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyLocationInSingleFileMessage"> + <source>'{0}' 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'.</source> + <target state="new">'{0}' 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'.</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyLocationInSingleFileTitle"> + <source>Avoid accessing Assembly file path when publishing as a single file</source> + <target state="new">Avoid accessing Assembly file path when publishing as a single file</target> + <note /> + </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerMessage"> <source>Calling '{0}' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. {1}. {2}</source> <target state="new">Calling '{0}' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. {1}. {2}</target> diff --git a/src/ILLink.RoslynAnalyzer/xlf/Resources.zh-Hans.xlf b/src/ILLink.RoslynAnalyzer/xlf/Resources.zh-Hans.xlf index 80c654eb5..7ca61b715 100644 --- a/src/ILLink.RoslynAnalyzer/xlf/Resources.zh-Hans.xlf +++ b/src/ILLink.RoslynAnalyzer/xlf/Resources.zh-Hans.xlf @@ -2,6 +2,26 @@ <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd"> <file datatype="xml" source-language="en" target-language="zh-Hans" original="../Resources.resx"> <body> + <trans-unit id="AvoidAssemblyGetFilesInSingleFileMessage"> + <source>'{0}' will throw for assemblies embedded in a single-file app</source> + <target state="new">'{0}' will throw for assemblies embedded in a single-file app</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyGetFilesInSingleFileTitle"> + <source>Avoid accessing Assembly file path when publishing as a single file</source> + <target state="new">Avoid accessing Assembly file path when publishing as a single file</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyLocationInSingleFileMessage"> + <source>'{0}' 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'.</source> + <target state="new">'{0}' 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'.</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyLocationInSingleFileTitle"> + <source>Avoid accessing Assembly file path when publishing as a single file</source> + <target state="new">Avoid accessing Assembly file path when publishing as a single file</target> + <note /> + </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerMessage"> <source>Calling '{0}' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. {1}. {2}</source> <target state="new">Calling '{0}' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. {1}. {2}</target> diff --git a/src/ILLink.RoslynAnalyzer/xlf/Resources.zh-Hant.xlf b/src/ILLink.RoslynAnalyzer/xlf/Resources.zh-Hant.xlf index 80d60776b..64d933978 100644 --- a/src/ILLink.RoslynAnalyzer/xlf/Resources.zh-Hant.xlf +++ b/src/ILLink.RoslynAnalyzer/xlf/Resources.zh-Hant.xlf @@ -2,6 +2,26 @@ <xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 xliff-core-1.2-transitional.xsd"> <file datatype="xml" source-language="en" target-language="zh-Hant" original="../Resources.resx"> <body> + <trans-unit id="AvoidAssemblyGetFilesInSingleFileMessage"> + <source>'{0}' will throw for assemblies embedded in a single-file app</source> + <target state="new">'{0}' will throw for assemblies embedded in a single-file app</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyGetFilesInSingleFileTitle"> + <source>Avoid accessing Assembly file path when publishing as a single file</source> + <target state="new">Avoid accessing Assembly file path when publishing as a single file</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyLocationInSingleFileMessage"> + <source>'{0}' 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'.</source> + <target state="new">'{0}' 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'.</target> + <note /> + </trans-unit> + <trans-unit id="AvoidAssemblyLocationInSingleFileTitle"> + <source>Avoid accessing Assembly file path when publishing as a single file</source> + <target state="new">Avoid accessing Assembly file path when publishing as a single file</target> + <note /> + </trans-unit> <trans-unit id="RequiresUnreferencedCodeAnalyzerMessage"> <source>Calling '{0}' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. {1}. {2}</source> <target state="new">Calling '{0}' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. {1}. {2}</target> diff --git a/test/ILLink.RoslynAnalyzer.Tests/AvoidAssemblyLocationInSingleFileTests.cs b/test/ILLink.RoslynAnalyzer.Tests/AvoidAssemblyLocationInSingleFileTests.cs new file mode 100644 index 000000000..0ea712f97 --- /dev/null +++ b/test/ILLink.RoslynAnalyzer.Tests/AvoidAssemblyLocationInSingleFileTests.cs @@ -0,0 +1,144 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Testing; +using Xunit; +using VerifyCS = ILLink.RoslynAnalyzer.Tests.CSharpAnalyzerVerifier< + ILLink.RoslynAnalyzer.AvoidAssemblyLocationInSingleFile>; + +namespace ILLink.RoslynAnalyzer.Tests +{ + public class AvoidAssemblyLocationInSingleFileTests + { + static Task VerifySingleFileAnalyzer (string source, params DiagnosticResult[] expected) + { + return VerifyCS.VerifyAnalyzerAsync (source, + TestCaseUtils.UseMSBuildProperties (MSBuildPropertyOptionNames.PublishSingleFile), + expected); + } + + [Fact] + public Task GetExecutingAssemblyLocation () + { + const string src = @" +using System.Reflection; +class C +{ + public string M() => Assembly.GetExecutingAssembly().Location; +}"; + + return VerifySingleFileAnalyzer (src, + // (5,26): warning IL3000: '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'. + VerifyCS.Diagnostic (AvoidAssemblyLocationInSingleFile.IL3000).WithSpan (5, 26, 5, 66).WithArguments ("System.Reflection.Assembly.Location")); + } + + [Fact] + public Task AssemblyProperties () + { + var src = @" +using System.Reflection; +class C +{ + public void M() + { + var a = Assembly.GetExecutingAssembly(); + _ = a.Location; + // below methods are marked as obsolete in 5.0 + // _ = a.CodeBase; + // _ = a.EscapedCodeBase; + } +}"; + return VerifySingleFileAnalyzer (src, + // (8,13): warning IL3000: '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'. + VerifyCS.Diagnostic (AvoidAssemblyLocationInSingleFile.IL3000).WithSpan (8, 13, 8, 23).WithArguments ("System.Reflection.Assembly.Location") + ); + } + + [Fact] + public Task AssemblyMethods () + { + var src = @" +using System.Reflection; +class C +{ + public void M() + { + var a = Assembly.GetExecutingAssembly(); + _ = a.GetFile(""/some/file/path""); + _ = a.GetFiles(); + } +}"; + return VerifySingleFileAnalyzer (src, + // (8,13): warning IL3001: Assemblies embedded in a single-file app cannot have additional files in the manifest. + VerifyCS.Diagnostic (AvoidAssemblyLocationInSingleFile.IL3001).WithSpan (8, 13, 8, 41).WithArguments ("System.Reflection.Assembly.GetFile(string)"), + // (9,13): warning IL3001: Assemblies embedded in a single-file app cannot have additional files in the manifest. + VerifyCS.Diagnostic (AvoidAssemblyLocationInSingleFile.IL3001).WithSpan (9, 13, 9, 25).WithArguments ("System.Reflection.Assembly.GetFiles()") + ); + } + + [Fact] + public Task AssemblyNameAttributes () + { + var src = @" +using System.Reflection; +class C +{ + public void M() + { + var a = Assembly.GetExecutingAssembly().GetName(); + _ = a.CodeBase; + _ = a.EscapedCodeBase; + } +}"; + return VerifySingleFileAnalyzer (src, + // (8,13): warning IL3000: 'System.Reflection.AssemblyName.CodeBase' always returns an empty string for assemblies embedded in a single-file app. If the path to the app directory is needed, consider calling 'System.AppContext.BaseDirectory'. + VerifyCS.Diagnostic (AvoidAssemblyLocationInSingleFile.IL3000).WithSpan (8, 13, 8, 23).WithArguments ("System.Reflection.AssemblyName.CodeBase"), + // (9,13): warning IL3000: 'System.Reflection.AssemblyName.EscapedCodeBase' always returns an empty string for assemblies embedded in a single-file app. If the path to the app directory is needed, consider calling 'System.AppContext.BaseDirectory'. + VerifyCS.Diagnostic (AvoidAssemblyLocationInSingleFile.IL3000).WithSpan (9, 13, 9, 30).WithArguments ("System.Reflection.AssemblyName.EscapedCodeBase") + ); + } + + [Fact] + public Task FalsePositive () + { + // This is an OK use of Location and GetFile since these assemblies were loaded from + // a file, but the analyzer is conservative + var src = @" +using System.Reflection; +class C +{ + public void M() + { + var a = Assembly.LoadFrom(""/some/path/not/in/bundle""); + _ = a.Location; + _ = a.GetFiles(); + } +}"; + return VerifySingleFileAnalyzer (src, + // (8,13): warning IL3000: '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'. + VerifyCS.Diagnostic (AvoidAssemblyLocationInSingleFile.IL3000).WithSpan (8, 13, 8, 23).WithArguments ("System.Reflection.Assembly.Location"), + // (9,13): warning IL3001: Assemblies embedded in a single-file app cannot have additional files in the manifest. + VerifyCS.Diagnostic (AvoidAssemblyLocationInSingleFile.IL3001).WithSpan (9, 13, 9, 25).WithArguments ("System.Reflection.Assembly.GetFiles()") + ); + } + + [Fact] + public Task PublishSingleFileIsNotSet () + { + var src = @" +using System.Reflection; +class C +{ + public void M() + { + var a = Assembly.GetExecutingAssembly().Location; + } +}"; + // If 'PublishSingleFile' is not set to true, no diagnostics should be produced by the analyzer. This will + // effectively verify that the number of produced diagnostics matches the number of expected ones (zero). + return VerifyCS.VerifyAnalyzerAsync (src); + } + } +} diff --git a/test/ILLink.RoslynAnalyzer.Tests/LinkerTestCases.cs b/test/ILLink.RoslynAnalyzer.Tests/LinkerTestCases.cs index e63d2c02f..f205fb25e 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/LinkerTestCases.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/LinkerTestCases.cs @@ -3,8 +3,6 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; -using System.Collections.Immutable; -using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Xunit; @@ -24,7 +22,7 @@ namespace ILLink.RoslynAnalyzer.Tests case "MethodWithDuplicateRequiresAttribute": return; } - RunTest (m, attrs); + RunTest (m, attrs, UseMSBuildProperties (MSBuildPropertyOptionNames.PublishTrimmed)); } } } diff --git a/test/ILLink.RoslynAnalyzer.Tests/AnalyzerTests.cs b/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs index fc1ee182d..81899e890 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/AnalyzerTests.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/RequiresUnreferencedCodeAnalyzerTests.cs @@ -3,14 +3,22 @@ // See the LICENSE file in the project root for more information. using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Testing; using Xunit; using VerifyCS = ILLink.RoslynAnalyzer.Tests.CSharpAnalyzerVerifier< ILLink.RoslynAnalyzer.RequiresUnreferencedCodeAnalyzer>; namespace ILLink.RoslynAnalyzer.Tests { - public class AnalyzerTests + public class RequiresUnreferencedCodeAnalyzerTests { + static Task VerifyRequiresUnreferencedCodeAnalyzer (string source, params DiagnosticResult[] expected) + { + return VerifyCS.VerifyAnalyzerAsync (source, + TestCaseUtils.UseMSBuildProperties (MSBuildPropertyOptionNames.PublishTrimmed), + expected); + } + [Fact] public Task SimpleDiagnostic () { @@ -23,10 +31,9 @@ class C int M1() => 0; int M2() => M1(); }"; - return VerifyCS.VerifyAnalyzerAsync (TestRequiresWithMessageOnlyOnMethod, + return VerifyRequiresUnreferencedCodeAnalyzer (TestRequiresWithMessageOnlyOnMethod, // (8,17): warning IL2026: Calling 'System.Int32 C::M1()' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. message. - VerifyCS.Diagnostic ().WithSpan (8, 17, 8, 21).WithArguments ("C.M1()", "message", "") - ); + VerifyCS.Diagnostic ().WithSpan (8, 17, 8, 21).WithArguments ("C.M1()", "message", "")); } [Fact] @@ -46,7 +53,7 @@ class C { } }"; - return VerifyCS.VerifyAnalyzerAsync (MessageAndUrlOnMethod, + return VerifyRequiresUnreferencedCodeAnalyzer (MessageAndUrlOnMethod, // (8,3): warning IL2026: Calling 'C.RequiresWithMessageAndUrl()' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. Message for --RequiresWithMessageAndUrl--. VerifyCS.Diagnostic ().WithSpan (8, 3, 8, 31).WithArguments ("C.RequiresWithMessageAndUrl()", "Message for --RequiresWithMessageAndUrl--", "https://helpurl") ); @@ -70,7 +77,7 @@ class C get { return 42; } } }"; - return VerifyCS.VerifyAnalyzerAsync (PropertyRequires, + return VerifyRequiresUnreferencedCodeAnalyzer (PropertyRequires, // (8,7): warning IL2026: Calling 'C.PropertyRequires.get' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. Message for --getter PropertyRequires--. VerifyCS.Diagnostic ().WithSpan (8, 7, 8, 23).WithArguments ("C.PropertyRequires.get", "Message for --getter PropertyRequires--", "") ); @@ -94,7 +101,7 @@ class C set { } } }"; - return VerifyCS.VerifyAnalyzerAsync (PropertyRequires, + return VerifyRequiresUnreferencedCodeAnalyzer (PropertyRequires, // (8,3): warning IL2026: Calling 'C.PropertyRequires.set' which has `RequiresUnreferencedCodeAttribute` can break functionality when trimming application code. Message for --setter PropertyRequires--. VerifyCS.Diagnostic ().WithSpan (8, 3, 8, 19).WithArguments ("C.PropertyRequires.set", "Message for --setter PropertyRequires--", "") ); diff --git a/test/ILLink.RoslynAnalyzer.Tests/TestCaseUtils.cs b/test/ILLink.RoslynAnalyzer.Tests/TestCaseUtils.cs index 003823c02..c62c2d762 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/TestCaseUtils.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/TestCaseUtils.cs @@ -53,9 +53,9 @@ namespace ILLink.RoslynAnalyzer.Tests } } - internal static void RunTest (MethodDeclarationSyntax m, List<AttributeSyntax> attrs) + internal static void RunTest (MethodDeclarationSyntax m, List<AttributeSyntax> attrs, params (string, string)[] MSBuildProperties) { - var comp = CSharpAnalyzerVerifier<RequiresUnreferencedCodeAnalyzer>.CreateCompilation (m.SyntaxTree).Result; + var comp = CSharpAnalyzerVerifier<RequiresUnreferencedCodeAnalyzer>.CreateCompilation (m.SyntaxTree, MSBuildProperties).Result; var diags = comp.GetAnalyzerDiagnosticsAsync ().Result; var filtered = diags.Where (d => d.Location.SourceSpan.IntersectsWith (m.Span)) @@ -181,6 +181,11 @@ In diagnostics: } } + internal static (string, string)[] UseMSBuildProperties (params string[] MSBuildProperties) + { + return MSBuildProperties.Select (msbp => ($"build_property.{msbp}", "true")).ToArray (); + } + internal static void GetDirectoryPaths (out string rootSourceDirectory, out string testAssemblyPath, [CallerFilePath] string thisFile = null) { diff --git a/test/ILLink.RoslynAnalyzer.Tests/Verifiers/CSharpAnalyzerVerifier`1.cs b/test/ILLink.RoslynAnalyzer.Tests/Verifiers/CSharpAnalyzerVerifier`1.cs index 91b28b1aa..4d96f37d8 100644 --- a/test/ILLink.RoslynAnalyzer.Tests/Verifiers/CSharpAnalyzerVerifier`1.cs +++ b/test/ILLink.RoslynAnalyzer.Tests/Verifiers/CSharpAnalyzerVerifier`1.cs @@ -71,9 +71,9 @@ namespace ILLink.RoslynAnalyzer.Tests } /// <inheritdoc cref="AnalyzerVerifier{TAnalyzer, TTest, TVerifier}.VerifyAnalyzerAsync(string, DiagnosticResult[])"/> - public static async Task VerifyAnalyzerAsync (string src, params DiagnosticResult[] expected) + public static async Task VerifyAnalyzerAsync (string src, (string, string)[]? analyzerOptions = null, params DiagnosticResult[] expected) { - var diags = await (await CreateCompilation (src)).GetAllDiagnosticsAsync (); + var diags = await (await CreateCompilation (src, analyzerOptions)).GetAllDiagnosticsAsync (); var analyzers = ImmutableArray.Create<DiagnosticAnalyzer> (new TAnalyzer ()); VerifyDiagnosticResults (diags, analyzers, expected, DefaultVerifier); |