Skip to content

[ApiDiff] Allow passing diagnostic options to all Compat places that use CSharpCompilationOptions #46424

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 31, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public sealed class CSharpAssemblyDocumentGenerator
private readonly IEnumerable<MetadataReference>? _metadataReferences;
private readonly bool _addPartialModifier;
private readonly bool _hideImplicitDefaultConstructors;
private readonly CSharpCompilationOptions _compilationOptions;

/// <summary>
/// Initializes a new instance of the <see cref="CSharpAssemblyDocumentGenerator"/> class.
Expand All @@ -46,6 +47,7 @@ public sealed class CSharpAssemblyDocumentGenerator
/// <param name="exceptionMessage">The optional exception message to use.</param>
/// <param name="includeAssemblyAttributes">Whether to include assembly attributes or not.</param>
/// <param name="metadataReferences">The metadata references to use. The default value is <see langword="null"/>.</param>
/// <param name="diagnosticOptions">The optional diagnostic options to use. The default value is <see langword="null"/>.</param>
/// <param name="addPartialModifier">Whether to add the partial modifier or not. The default value is <see langword="true"/>.</param>
/// <param name="hideImplicitDefaultConstructors">Whether to hide implicit default constructors or not. The default value is <see langword="true"/>.</param>
public CSharpAssemblyDocumentGenerator(ILog log,
Expand All @@ -55,6 +57,7 @@ public CSharpAssemblyDocumentGenerator(ILog log,
string? exceptionMessage,
bool includeAssemblyAttributes,
IEnumerable<MetadataReference>? metadataReferences = null,
IEnumerable<KeyValuePair<string, ReportDiagnostic>>? diagnosticOptions = null,
bool addPartialModifier = true,
bool hideImplicitDefaultConstructors = true)
{
Expand All @@ -69,6 +72,11 @@ public CSharpAssemblyDocumentGenerator(ILog log,
_metadataReferences = metadataReferences;
_addPartialModifier = addPartialModifier;
_hideImplicitDefaultConstructors = hideImplicitDefaultConstructors;

_compilationOptions = new CSharpCompilationOptions(
OutputKind.DynamicallyLinkedLibrary,
nullableContextOptions: NullableContextOptions.Enable,
specificDiagnosticOptions: diagnosticOptions);
}

/// <summary>
Expand All @@ -78,11 +86,9 @@ public CSharpAssemblyDocumentGenerator(ILog log,
/// <returns>The source code document instance of the specified assembly symbol.</returns>
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<INamespaceSymbol> namespaceSymbols = EnumerateNamespaces(assemblySymbol).Where(_symbolFilter.Include);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/// <inheritdoc />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public static void Run(ILog log,
log,
assembliesPaths,
assemblyReferencesPaths,
respectInternals);
respectInternals: respectInternals);

Run(log,
loader,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,17 +57,19 @@ public class AssemblySymbolLoader : IAssemblySymbolLoader
/// <param name="log">The logger instance to use for message logging.</param>
/// <param name="assembliesPaths">A collection of paths where the assembly DLLs should be searched.</param>
/// <param name="assemblyReferencesPaths">An optional collection of paths where the assembly references should be searched.</param>
/// <param name="diagnosticOptions">An optional list of diagnostic options to use when compiling the loaded assemblies.</param>
/// <param name="respectInternals">Whether to include internal symbols or not.</param>
/// <returns>A tuple containing an assembly symbol loader and its corresponding dictionary of assembly symbols.</returns>
public static (AssemblySymbolLoader, Dictionary<string, IAssemblySymbol>) CreateFromFiles(ILog log, string[] assembliesPaths, string[]? assemblyReferencesPaths, bool respectInternals = false)
public static (AssemblySymbolLoader, Dictionary<string, IAssemblySymbol>) CreateFromFiles(ILog log, string[] assembliesPaths, string[]? assemblyReferencesPaths, IEnumerable<KeyValuePair<string, ReportDiagnostic>>? diagnosticOptions = null, bool respectInternals = false)
{
if (assembliesPaths.Length == 0)
{
return (new AssemblySymbolLoader(log, resolveAssemblyReferences: true, includeInternalSymbols: respectInternals), new Dictionary<string, IAssemblySymbol>());
return (new AssemblySymbolLoader(log, diagnosticOptions, resolveAssemblyReferences: true, includeInternalSymbols: respectInternals),
new Dictionary<string, IAssemblySymbol>());
}

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!);
Expand Down Expand Up @@ -97,14 +99,18 @@ public static (AssemblySymbolLoader, Dictionary<string, IAssemblySymbol>) Create
/// Creates a new instance of the <see cref="AssemblySymbolLoader"/> class.
/// </summary>
/// <param name="log">A logger instance for logging message.</param>
/// <param name="diagnosticOptions">An optional list of diagnostic options to use when compiling the loaded assemblies.</param>
/// <param name="resolveAssemblyReferences">True to attempt to load references for loaded assemblies from the locations specified with <see cref="AddReferenceSearchPaths(string[])"/>. Default is false.</param>
/// <param name="includeInternalSymbols">True to include all internal metadata for assemblies loaded. Default is false which only includes public and some internal metadata. <seealso cref="MetadataImportOptions"/></param>
public AssemblySymbolLoader(ILog log, bool resolveAssemblyReferences = false, bool includeInternalSymbols = false)
public AssemblySymbolLoader(ILog log, IEnumerable<KeyValuePair<string, ReportDiagnostic>>? 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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ public sealed class AssemblySymbolLoaderFactory(ILog log, bool includeInternalSy
{
/// <inheritdoc />
public IAssemblySymbolLoader Create(bool shouldResolveReferences) =>
new AssemblySymbolLoader(log, shouldResolveReferences, includeInternalSymbols);
new AssemblySymbolLoader(log, resolveAssemblyReferences: shouldResolveReferences, includeInternalSymbols: includeInternalSymbols);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,13 @@ public static string EmitAssemblyFromSyntax(string syntax,
}

public static Stream EmitAssemblyStreamFromSyntax(string syntax,
IEnumerable<KeyValuePair<string, ReportDiagnostic>> 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());

Expand Down Expand Up @@ -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<KeyValuePair<string, ReportDiagnostic>> diagnosticOptions = null)
{
CSharpCompilation compilation = CreateCSharpCompilation(name, enableNullable, publicKey, allowUnsafe);
CSharpCompilation compilation = CreateCSharpCompilation(name, enableNullable, publicKey, allowUnsafe, diagnosticOptions);
return compilation.AddSyntaxTrees(GetSyntaxTree(syntax));
}

Expand All @@ -94,15 +95,15 @@ 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<KeyValuePair<string, ReportDiagnostic>> diagnosticOptions = null)
{
bool publicSign = publicKey != null ? true : false;
var compilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary,
publicSign: publicSign,
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);
}
Expand Down
3 changes: 1 addition & 2 deletions test/Microsoft.DotNet.GenAPI.Tests/CSharpFileBuilderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -40,7 +39,7 @@ private void RunTest(string original,
Mock<ILog> log = new();

(IAssemblySymbolLoader loader, Dictionary<string, IAssemblySymbol> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,22 @@ namespace Microsoft.DotNet.GenAPI.Tests;

public class TestAssemblyLoaderFactory
{
public static (IAssemblySymbolLoader, Dictionary<string, IAssemblySymbol>) CreateFromTexts(ILog log, (string, string)[] assemblyTexts, bool respectInternals = false, bool allowUnsafe = false)
public static (IAssemblySymbolLoader, Dictionary<string, IAssemblySymbol>) CreateFromTexts(ILog log, (string, string)[] assemblyTexts, IEnumerable<KeyValuePair<string, ReportDiagnostic>>? diagnosticOptions = null, bool respectInternals = false, bool allowUnsafe = false)
{
if (assemblyTexts.Length == 0)
{
return (new AssemblySymbolLoader(log, resolveAssemblyReferences: true, includeInternalSymbols: respectInternals), new Dictionary<string, IAssemblySymbol>());
return (new AssemblySymbolLoader(log, diagnosticOptions, resolveAssemblyReferences: true, includeInternalSymbols: respectInternals),
new Dictionary<string, IAssemblySymbol>());
}

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<string, IAssemblySymbol> 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);
Expand Down
Loading