From 2363be70da2b4a519eaf2f90905f32bb9d4226dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20S=C3=A1nchez=20L=C3=B3pez?= <1175054+carlossanlop@users.noreply.github.com> Date: Thu, 30 Jan 2025 17:51:41 -0800 Subject: [PATCH 1/3] Allow passing an optional list of diagnostic options in all places that use CSharpCompilatioOptions in Compat. --- .../CSharpAssemblyDocumentGenerator.cs | 12 +++++++++--- .../CSharpFileBuilder.cs | 2 +- .../AssemblySymbolLoader.cs | 18 ++++++++++++------ .../AssemblySymbolLoaderFactory.cs | 2 +- .../CSharpFileBuilderTests.cs | 3 +-- 5 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/CSharpAssemblyDocumentGenerator.cs b/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/CSharpAssemblyDocumentGenerator.cs index 7355e9b1fa17..7df552851bd3 100644 --- a/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/CSharpAssemblyDocumentGenerator.cs +++ b/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/CSharpAssemblyDocumentGenerator.cs @@ -35,6 +35,7 @@ public sealed class CSharpAssemblyDocumentGenerator private readonly IEnumerable? _metadataReferences; private readonly bool _addPartialModifier; private readonly bool _hideImplicitDefaultConstructors; + private readonly CSharpCompilationOptions _compilationOptions; /// /// Initializes a new instance of the class. @@ -46,6 +47,7 @@ public sealed class CSharpAssemblyDocumentGenerator /// The optional exception message to use. /// Whether to include assembly attributes or not. /// The metadata references to use. The default value is . + /// The optional diagnostic options to use. The default value is . /// Whether to add the partial modifier or not. The default value is . /// Whether to hide implicit default constructors or not. The default value is . public CSharpAssemblyDocumentGenerator(ILog log, @@ -55,6 +57,7 @@ public CSharpAssemblyDocumentGenerator(ILog log, string? exceptionMessage, bool includeAssemblyAttributes, IEnumerable? metadataReferences = null, + IEnumerable>? diagnosticOptions = null, bool addPartialModifier = true, bool hideImplicitDefaultConstructors = true) { @@ -69,6 +72,11 @@ public CSharpAssemblyDocumentGenerator(ILog log, _metadataReferences = metadataReferences; _addPartialModifier = addPartialModifier; _hideImplicitDefaultConstructors = hideImplicitDefaultConstructors; + + _compilationOptions = new CSharpCompilationOptions( + OutputKind.DynamicallyLinkedLibrary, + nullableContextOptions: NullableContextOptions.Enable, + specificDiagnosticOptions: diagnosticOptions); } /// @@ -78,11 +86,9 @@ public CSharpAssemblyDocumentGenerator(ILog log, /// The source code document instance of the specified assembly symbol. public Document GetDocumentForAssembly(IAssemblySymbol assemblySymbol) { - CSharpCompilationOptions compilationOptions = new(OutputKind.DynamicallyLinkedLibrary, - nullableContextOptions: NullableContextOptions.Enable); Project project = _adhocWorkspace.AddProject(ProjectInfo.Create( ProjectId.CreateNewId(), VersionStamp.Create(), assemblySymbol.Name, assemblySymbol.Name, LanguageNames.CSharp, - compilationOptions: compilationOptions)); + compilationOptions: _compilationOptions)); project = project.AddMetadataReferences(_metadataReferences ?? _loader.MetadataReferences); IEnumerable namespaceSymbols = EnumerateNamespaces(assemblySymbol).Where(_symbolFilter.Include); diff --git a/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/CSharpFileBuilder.cs b/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/CSharpFileBuilder.cs index d52543f05a0f..a5cfa52b1af5 100644 --- a/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/CSharpFileBuilder.cs +++ b/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/CSharpFileBuilder.cs @@ -33,7 +33,7 @@ public CSharpFileBuilder(ILog log, { _textWriter = textWriter; _header = header; - _docGenerator = new CSharpAssemblyDocumentGenerator(log, loader, symbolFilter, attributeDataSymbolFilter, exceptionMessage, includeAssemblyAttributes, metadataReferences, addPartialModifier); + _docGenerator = new CSharpAssemblyDocumentGenerator(log, loader, symbolFilter, attributeDataSymbolFilter, exceptionMessage, includeAssemblyAttributes, metadataReferences, addPartialModifier: addPartialModifier); } /// diff --git a/src/Compatibility/Microsoft.DotNet.ApiSymbolExtensions/AssemblySymbolLoader.cs b/src/Compatibility/Microsoft.DotNet.ApiSymbolExtensions/AssemblySymbolLoader.cs index 86b961d6388d..caf4300aac15 100644 --- a/src/Compatibility/Microsoft.DotNet.ApiSymbolExtensions/AssemblySymbolLoader.cs +++ b/src/Compatibility/Microsoft.DotNet.ApiSymbolExtensions/AssemblySymbolLoader.cs @@ -57,17 +57,19 @@ public class AssemblySymbolLoader : IAssemblySymbolLoader /// The logger instance to use for message logging. /// A collection of paths where the assembly DLLs should be searched. /// An optional collection of paths where the assembly references should be searched. + /// An optional list of diagnostic options to use when compiling the loaded assemblies. /// Whether to include internal symbols or not. /// A tuple containing an assembly symbol loader and its corresponding dictionary of assembly symbols. - public static (AssemblySymbolLoader, Dictionary) CreateFromFiles(ILog log, string[] assembliesPaths, string[]? assemblyReferencesPaths, bool respectInternals = false) + public static (AssemblySymbolLoader, Dictionary) CreateFromFiles(ILog log, string[] assembliesPaths, string[]? assemblyReferencesPaths, IEnumerable>? diagnosticOptions = null, bool respectInternals = false) { if (assembliesPaths.Length == 0) { - return (new AssemblySymbolLoader(log, resolveAssemblyReferences: true, includeInternalSymbols: respectInternals), new Dictionary()); + return (new AssemblySymbolLoader(log, diagnosticOptions, resolveAssemblyReferences: true, includeInternalSymbols: respectInternals), + new Dictionary()); } bool atLeastOneReferencePath = assemblyReferencesPaths?.Count() > 0; - AssemblySymbolLoader loader = new(log, resolveAssemblyReferences: atLeastOneReferencePath, respectInternals); + AssemblySymbolLoader loader = new(log, diagnosticOptions, resolveAssemblyReferences: atLeastOneReferencePath, includeInternalSymbols: respectInternals); if (atLeastOneReferencePath) { loader.AddReferenceSearchPaths(assemblyReferencesPaths!); @@ -97,14 +99,18 @@ public static (AssemblySymbolLoader, Dictionary) Create /// Creates a new instance of the class. /// /// A logger instance for logging message. + /// An optional list of diagnostic options to use when compiling the loaded assemblies. /// True to attempt to load references for loaded assemblies from the locations specified with . Default is false. /// True to include all internal metadata for assemblies loaded. Default is false which only includes public and some internal metadata. - public AssemblySymbolLoader(ILog log, bool resolveAssemblyReferences = false, bool includeInternalSymbols = false) + public AssemblySymbolLoader(ILog log, IEnumerable>? diagnosticOptions = null, bool resolveAssemblyReferences = false, bool includeInternalSymbols = false) { _log = log; _loadedAssemblies = []; - CSharpCompilationOptions compilationOptions = new(OutputKind.DynamicallyLinkedLibrary, nullableContextOptions: NullableContextOptions.Enable, - metadataImportOptions: includeInternalSymbols ? MetadataImportOptions.Internal : MetadataImportOptions.Public); + CSharpCompilationOptions compilationOptions = new( + OutputKind.DynamicallyLinkedLibrary, + nullableContextOptions: NullableContextOptions.Enable, + metadataImportOptions: includeInternalSymbols ? MetadataImportOptions.Internal : MetadataImportOptions.Public, + specificDiagnosticOptions: diagnosticOptions); _cSharpCompilation = CSharpCompilation.Create($"AssemblyLoader_{DateTime.Now:MM_dd_yy_HH_mm_ss_FFF}", options: compilationOptions); _resolveReferences = resolveAssemblyReferences; } diff --git a/src/Compatibility/Microsoft.DotNet.ApiSymbolExtensions/AssemblySymbolLoaderFactory.cs b/src/Compatibility/Microsoft.DotNet.ApiSymbolExtensions/AssemblySymbolLoaderFactory.cs index 5a2b74600cc0..e5448223b273 100644 --- a/src/Compatibility/Microsoft.DotNet.ApiSymbolExtensions/AssemblySymbolLoaderFactory.cs +++ b/src/Compatibility/Microsoft.DotNet.ApiSymbolExtensions/AssemblySymbolLoaderFactory.cs @@ -14,6 +14,6 @@ public sealed class AssemblySymbolLoaderFactory(ILog log, bool includeInternalSy { /// public IAssemblySymbolLoader Create(bool shouldResolveReferences) => - new AssemblySymbolLoader(log, shouldResolveReferences, includeInternalSymbols); + new AssemblySymbolLoader(log, resolveAssemblyReferences: shouldResolveReferences, includeInternalSymbols: includeInternalSymbols); } } diff --git a/test/Microsoft.DotNet.GenAPI.Tests/CSharpFileBuilderTests.cs b/test/Microsoft.DotNet.GenAPI.Tests/CSharpFileBuilderTests.cs index 6cadd92d112d..c53d1445cef1 100644 --- a/test/Microsoft.DotNet.GenAPI.Tests/CSharpFileBuilderTests.cs +++ b/test/Microsoft.DotNet.GenAPI.Tests/CSharpFileBuilderTests.cs @@ -9,7 +9,6 @@ using Microsoft.DotNet.ApiSymbolExtensions; using Microsoft.DotNet.ApiSymbolExtensions.Filtering; using Microsoft.DotNet.ApiSymbolExtensions.Logging; -using Microsoft.DotNet.ApiSymbolExtensions.Tests; using Moq; namespace Microsoft.DotNet.GenAPI.Tests @@ -40,7 +39,7 @@ private void RunTest(string original, Mock log = new(); (IAssemblySymbolLoader loader, Dictionary assemblySymbols) = TestAssemblyLoaderFactory - .CreateFromTexts(log.Object, assemblyTexts: [(assemblyName, original)], respectInternals: includeInternalSymbols, allowUnsafe); + .CreateFromTexts(log.Object, assemblyTexts: [(assemblyName, original)], respectInternals: includeInternalSymbols, allowUnsafe: allowUnsafe); ISymbolFilter symbolFilter = SymbolFilterFactory.GetFilterFromList([], null, includeInternalSymbols, includeEffectivelyPrivateSymbols, includeExplicitInterfaceImplementationSymbols); ISymbolFilter attributeDataSymbolFilter = SymbolFilterFactory.GetFilterFromList(excludedAttributeList, null, includeInternalSymbols, includeEffectivelyPrivateSymbols, includeExplicitInterfaceImplementationSymbols); From 7b739f536ea08eed2bf813bdd90a6117335d34f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20S=C3=A1nchez=20L=C3=B3pez?= <1175054+carlossanlop@users.noreply.github.com> Date: Thu, 30 Jan 2025 17:51:48 -0800 Subject: [PATCH 2/3] Adjust tests accordingly. --- .../SymbolFactory.cs | 11 ++++++----- .../TestAssemblyLoaderFactory.cs | 9 +++++---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/test/Microsoft.DotNet.ApiSymbolExtensions.Tests/SymbolFactory.cs b/test/Microsoft.DotNet.ApiSymbolExtensions.Tests/SymbolFactory.cs index dce5d5266a33..9f5abe5252bb 100644 --- a/test/Microsoft.DotNet.ApiSymbolExtensions.Tests/SymbolFactory.cs +++ b/test/Microsoft.DotNet.ApiSymbolExtensions.Tests/SymbolFactory.cs @@ -31,12 +31,13 @@ public static string EmitAssemblyFromSyntax(string syntax, } public static Stream EmitAssemblyStreamFromSyntax(string syntax, + IEnumerable> diagnosticOptions = null, bool enableNullable = false, byte[] publicKey = null, [CallerMemberName] string assemblyName = "", bool allowUnsafe = false) { - CSharpCompilation compilation = CreateCSharpCompilationFromSyntax(syntax, assemblyName, enableNullable, publicKey, allowUnsafe); + CSharpCompilation compilation = CreateCSharpCompilationFromSyntax(syntax, assemblyName, enableNullable, publicKey, allowUnsafe, diagnosticOptions); Assert.Empty(compilation.GetDiagnostics()); @@ -76,9 +77,9 @@ public static IAssemblySymbol GetAssemblyFromSyntaxWithReferences(string syntax, return compilation.Assembly; } - private static CSharpCompilation CreateCSharpCompilationFromSyntax(string syntax, string name, bool enableNullable, byte[] publicKey, bool allowUnsafe) + private static CSharpCompilation CreateCSharpCompilationFromSyntax(string syntax, string name, bool enableNullable, byte[] publicKey, bool allowUnsafe, IEnumerable> diagnosticOptions = null) { - CSharpCompilation compilation = CreateCSharpCompilation(name, enableNullable, publicKey, allowUnsafe); + CSharpCompilation compilation = CreateCSharpCompilation(name, enableNullable, publicKey, allowUnsafe, diagnosticOptions); return compilation.AddSyntaxTrees(GetSyntaxTree(syntax)); } @@ -94,7 +95,7 @@ private static SyntaxTree GetSyntaxTree(string syntax) return CSharpSyntaxTree.ParseText(syntax, ParseOptions); } - private static CSharpCompilation CreateCSharpCompilation(string name, bool enableNullable, byte[] publicKey, bool allowUnsafe) + private static CSharpCompilation CreateCSharpCompilation(string name, bool enableNullable, byte[] publicKey, bool allowUnsafe, IEnumerable> diagnosticOptions = null) { bool publicSign = publicKey != null ? true : false; var compilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, @@ -102,7 +103,7 @@ private static CSharpCompilation CreateCSharpCompilation(string name, bool enabl cryptoPublicKey: publicSign ? publicKey.ToImmutableArray() : default, nullableContextOptions: enableNullable ? NullableContextOptions.Enable : NullableContextOptions.Disable, allowUnsafe: allowUnsafe, - specificDiagnosticOptions: DiagnosticOptions); + specificDiagnosticOptions: diagnosticOptions ?? DiagnosticOptions); return CSharpCompilation.Create(name, options: compilationOptions, references: DefaultReferences); } diff --git a/test/Microsoft.DotNet.GenAPI.Tests/TestAssemblyLoaderFactory.cs b/test/Microsoft.DotNet.GenAPI.Tests/TestAssemblyLoaderFactory.cs index df759758eee3..9bd062acf33a 100644 --- a/test/Microsoft.DotNet.GenAPI.Tests/TestAssemblyLoaderFactory.cs +++ b/test/Microsoft.DotNet.GenAPI.Tests/TestAssemblyLoaderFactory.cs @@ -11,21 +11,22 @@ namespace Microsoft.DotNet.GenAPI.Tests; public class TestAssemblyLoaderFactory { - public static (IAssemblySymbolLoader, Dictionary) CreateFromTexts(ILog log, (string, string)[] assemblyTexts, bool respectInternals = false, bool allowUnsafe = false) + public static (IAssemblySymbolLoader, Dictionary) CreateFromTexts(ILog log, (string, string)[] assemblyTexts, IEnumerable>? diagnosticOptions = null, bool respectInternals = false, bool allowUnsafe = false) { if (assemblyTexts.Length == 0) { - return (new AssemblySymbolLoader(log, resolveAssemblyReferences: true, includeInternalSymbols: respectInternals), new Dictionary()); + return (new AssemblySymbolLoader(log, diagnosticOptions, resolveAssemblyReferences: true, includeInternalSymbols: respectInternals), + new Dictionary()); } - AssemblySymbolLoader loader = new(log, resolveAssemblyReferences: true, includeInternalSymbols: respectInternals); + AssemblySymbolLoader loader = new(log, diagnosticOptions, resolveAssemblyReferences: true, includeInternalSymbols: respectInternals); loader.AddReferenceSearchPaths(typeof(object).Assembly!.Location!); loader.AddReferenceSearchPaths(typeof(DynamicAttribute).Assembly!.Location!); Dictionary assemblySymbols = new(); foreach ((string assemblyName, string assemblyText) in assemblyTexts) { - using Stream assemblyStream = SymbolFactory.EmitAssemblyStreamFromSyntax(assemblyText, enableNullable: true, allowUnsafe: allowUnsafe, assemblyName: assemblyName); + using Stream assemblyStream = SymbolFactory.EmitAssemblyStreamFromSyntax(assemblyText, diagnosticOptions, enableNullable: true, allowUnsafe: allowUnsafe, assemblyName: assemblyName); if (loader.LoadAssembly(assemblyName, assemblyStream) is IAssemblySymbol assemblySymbol) { assemblySymbols.Add(assemblyName, assemblySymbol); From 389c9ce9a843804b1c399d129ce7e9082b76197f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20S=C3=A1nchez=20L=C3=B3pez?= <1175054+carlossanlop@users.noreply.github.com> Date: Thu, 30 Jan 2025 17:52:51 -0800 Subject: [PATCH 3/3] missed named argument --- src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIApp.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIApp.cs b/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIApp.cs index 948f7089667c..d34c1e6366bc 100644 --- a/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIApp.cs +++ b/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIApp.cs @@ -35,7 +35,7 @@ public static void Run(ILog log, log, assembliesPaths, assemblyReferencesPaths, - respectInternals); + respectInternals: respectInternals); Run(log, loader,