Skip to content

Commit 07308ff

Browse files
committed
Allow passing the diagnostic options to all Compatibility places using CSharpCompilationOptions.
1 parent 467d5db commit 07308ff

File tree

9 files changed

+187
-17
lines changed

9 files changed

+187
-17
lines changed

src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI.Task/GenAPITask.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ protected override void ExecuteCore()
7272
log,
7373
assembliesPaths: Assemblies,
7474
assemblyReferencesPaths: AssemblyReferences,
75-
RespectInternals);
75+
respectInternals: RespectInternals);
7676

7777
GenAPIApp.Run(log,
7878
loader,

src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI.Tool/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ static int Main(string[] args)
110110
log,
111111
assembliesPaths: assemblies,
112112
assemblyReferencesPaths: parseResult.GetValue(assemblyReferencesOption),
113-
respectInternals);
113+
respectInternals: respectInternals);
114114

115115
GenAPIApp.Run(log,
116116
loader,

src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/CSharpAssemblyDocumentGenerator.cs

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ public sealed class CSharpAssemblyDocumentGenerator
3535
private readonly IEnumerable<MetadataReference>? _metadataReferences;
3636
private readonly bool _addPartialModifier;
3737
private readonly bool _hideImplicitDefaultConstructors;
38+
private readonly CSharpCompilationOptions _compilationOptions;
3839

3940
/// <summary>
4041
/// Initializes a new instance of the <see cref="CSharpAssemblyDocumentGenerator"/> class.
@@ -46,6 +47,7 @@ public sealed class CSharpAssemblyDocumentGenerator
4647
/// <param name="exceptionMessage">The optional exception message to use.</param>
4748
/// <param name="includeAssemblyAttributes">Whether to include assembly attributes or not.</param>
4849
/// <param name="metadataReferences">The metadata references to use. The default value is <see langword="null"/>.</param>
50+
/// <param name="diagnosticOptions">The optional diagnostic options to use. The default value is <see langword="null"/>.</param>
4951
/// <param name="addPartialModifier">Whether to add the partial modifier or not. The default value is <see langword="true"/>.</param>
5052
/// <param name="hideImplicitDefaultConstructors">Whether to hide implicit default constructors or not. The default value is <see langword="true"/>.</param>
5153
public CSharpAssemblyDocumentGenerator(ILog log,
@@ -55,6 +57,7 @@ public CSharpAssemblyDocumentGenerator(ILog log,
5557
string? exceptionMessage,
5658
bool includeAssemblyAttributes,
5759
IEnumerable<MetadataReference>? metadataReferences = null,
60+
Dictionary<string, ReportDiagnostic>? diagnosticOptions = null,
5861
bool addPartialModifier = true,
5962
bool hideImplicitDefaultConstructors = true)
6063
{
@@ -69,6 +72,11 @@ public CSharpAssemblyDocumentGenerator(ILog log,
6972
_metadataReferences = metadataReferences;
7073
_addPartialModifier = addPartialModifier;
7174
_hideImplicitDefaultConstructors = hideImplicitDefaultConstructors;
75+
76+
_compilationOptions = new CSharpCompilationOptions(
77+
OutputKind.DynamicallyLinkedLibrary,
78+
nullableContextOptions: NullableContextOptions.Enable,
79+
specificDiagnosticOptions: diagnosticOptions);
7280
}
7381

7482
/// <summary>
@@ -78,11 +86,16 @@ public CSharpAssemblyDocumentGenerator(ILog log,
7886
/// <returns>The source code document instance of the specified assembly symbol.</returns>
7987
public Document GetDocumentForAssembly(IAssemblySymbol assemblySymbol)
8088
{
81-
CSharpCompilationOptions compilationOptions = new(OutputKind.DynamicallyLinkedLibrary,
82-
nullableContextOptions: NullableContextOptions.Enable);
83-
Project project = _adhocWorkspace.AddProject(ProjectInfo.Create(
84-
ProjectId.CreateNewId(), VersionStamp.Create(), assemblySymbol.Name, assemblySymbol.Name, LanguageNames.CSharp,
85-
compilationOptions: compilationOptions));
89+
ProjectInfo projectInfo = ProjectInfo.Create(
90+
ProjectId.CreateNewId(),
91+
VersionStamp.Create(),
92+
assemblySymbol.Name,
93+
assemblySymbol.Name,
94+
LanguageNames.CSharp,
95+
compilationOptions: _compilationOptions);
96+
97+
Project project = _adhocWorkspace.AddProject(projectInfo);
98+
8699
project = project.AddMetadataReferences(_metadataReferences ?? _loader.MetadataReferences);
87100

88101
IEnumerable<INamespaceSymbol> namespaceSymbols = EnumerateNamespaces(assemblySymbol).Where(_symbolFilter.Include);

src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/CSharpFileBuilder.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,14 @@ public CSharpFileBuilder(ILog log,
3333
{
3434
_textWriter = textWriter;
3535
_header = header;
36-
_docGenerator = new CSharpAssemblyDocumentGenerator(log, loader, symbolFilter, attributeDataSymbolFilter, exceptionMessage, includeAssemblyAttributes, metadataReferences, addPartialModifier);
36+
_docGenerator = new CSharpAssemblyDocumentGenerator(log,
37+
loader,
38+
symbolFilter,
39+
attributeDataSymbolFilter,
40+
exceptionMessage,
41+
includeAssemblyAttributes,
42+
metadataReferences,
43+
addPartialModifier: addPartialModifier);
3744
}
3845

3946
/// <inheritdoc />

src/Compatibility/Microsoft.DotNet.ApiSymbolExtensions/AssemblySymbolLoader.cs

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,17 +57,28 @@ public class AssemblySymbolLoader : IAssemblySymbolLoader
5757
/// <param name="log">The logger instance to use for message logging.</param>
5858
/// <param name="assembliesPaths">A collection of paths where the assembly DLLs should be searched.</param>
5959
/// <param name="assemblyReferencesPaths">An optional collection of paths where the assembly references should be searched.</param>
60+
/// <param name="diagnosticOptions">An optional dictionary of specific diagnostic options to use when compiling the loaded assemblies.</param>
6061
/// <param name="respectInternals">Whether to include internal symbols or not.</param>
6162
/// <returns>A tuple containing an assembly symbol loader and its corresponding dictionary of assembly symbols.</returns>
62-
public static (AssemblySymbolLoader, Dictionary<string, IAssemblySymbol>) CreateFromFiles(ILog log, string[] assembliesPaths, string[]? assemblyReferencesPaths, bool respectInternals = false)
63+
public static (AssemblySymbolLoader, Dictionary<string, IAssemblySymbol>) CreateFromFiles(ILog log, string[] assembliesPaths, string[]? assemblyReferencesPaths, Dictionary<string, ReportDiagnostic>? diagnosticOptions = null, bool respectInternals = false)
6364
{
65+
AssemblySymbolLoader loader;
6466
if (assembliesPaths.Length == 0)
6567
{
66-
return (new AssemblySymbolLoader(log, resolveAssemblyReferences: true, includeInternalSymbols: respectInternals), new Dictionary<string, IAssemblySymbol>());
68+
loader = new AssemblySymbolLoader(log,
69+
diagnosticOptions,
70+
resolveAssemblyReferences: true,
71+
includeInternalSymbols: respectInternals);
72+
73+
return (loader, new Dictionary<string, IAssemblySymbol>());
6774
}
6875

6976
bool atLeastOneReferencePath = assemblyReferencesPaths?.Count() > 0;
70-
AssemblySymbolLoader loader = new(log, resolveAssemblyReferences: atLeastOneReferencePath, respectInternals);
77+
loader = new AssemblySymbolLoader(log,
78+
diagnosticOptions,
79+
resolveAssemblyReferences: atLeastOneReferencePath,
80+
includeInternalSymbols: respectInternals);
81+
7182
if (atLeastOneReferencePath)
7283
{
7384
loader.AddReferenceSearchPaths(assemblyReferencesPaths!);
@@ -97,14 +108,19 @@ public static (AssemblySymbolLoader, Dictionary<string, IAssemblySymbol>) Create
97108
/// Creates a new instance of the <see cref="AssemblySymbolLoader"/> class.
98109
/// </summary>
99110
/// <param name="log">A logger instance for logging message.</param>
111+
/// <param name="diagnosticOptions">An optional dictionary of specific diagnostic options to use when compiling the loaded assemblies.</param>
100112
/// <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>
101113
/// <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>
102-
public AssemblySymbolLoader(ILog log, bool resolveAssemblyReferences = false, bool includeInternalSymbols = false)
114+
public AssemblySymbolLoader(ILog log, Dictionary<string, ReportDiagnostic>? diagnosticOptions = null, bool resolveAssemblyReferences = false, bool includeInternalSymbols = false)
103115
{
104116
_log = log;
105117
_loadedAssemblies = [];
106-
CSharpCompilationOptions compilationOptions = new(OutputKind.DynamicallyLinkedLibrary, nullableContextOptions: NullableContextOptions.Enable,
107-
metadataImportOptions: includeInternalSymbols ? MetadataImportOptions.Internal : MetadataImportOptions.Public);
118+
CSharpCompilationOptions compilationOptions = new(
119+
OutputKind.DynamicallyLinkedLibrary,
120+
nullableContextOptions: NullableContextOptions.Enable,
121+
metadataImportOptions: includeInternalSymbols ? MetadataImportOptions.Internal : MetadataImportOptions.Public,
122+
specificDiagnosticOptions: diagnosticOptions);
123+
108124
_cSharpCompilation = CSharpCompilation.Create($"AssemblyLoader_{DateTime.Now:MM_dd_yy_HH_mm_ss_FFF}", options: compilationOptions);
109125
_resolveReferences = resolveAssemblyReferences;
110126
}
@@ -160,6 +176,29 @@ public void AddReferenceSearchPaths(params string[] paths)
160176
return assemblySymbols;
161177
}
162178

179+
/// <inheritdoc />
180+
private IDictionary<string, IAssemblySymbol> LoadAssembliesAsDictionary(params string[] paths)
181+
{
182+
// First resolve all assemblies that are passed in and create metadata references out of them.
183+
// Reference assemblies of the passed in assemblies that themselves are passed in, will be skipped to be resolved,
184+
// as they are resolved as part of the loop below.
185+
ImmutableHashSet<string> fileNames = paths.Select(path => Path.GetFileName(path)).ToImmutableHashSet();
186+
List<MetadataReference> assembliesToReturn = LoadFromPaths(paths, fileNames);
187+
188+
// Create IAssemblySymbols out of the MetadataReferences.
189+
// Doing this after resolving references to make sure that references are available.
190+
Dictionary<string, IAssemblySymbol> assemblySymbols = [];
191+
foreach (MetadataReference metadataReference in assembliesToReturn)
192+
{
193+
if (_cSharpCompilation.GetAssemblyOrModuleSymbol(metadataReference) is IAssemblySymbol assemblySymbol)
194+
{
195+
assemblySymbols.Add(assemblySymbol.Name, assemblySymbol);
196+
}
197+
}
198+
199+
return assemblySymbols;
200+
}
201+
163202
/// <inheritdoc />
164203
public IReadOnlyList<IAssemblySymbol?> LoadAssembliesFromArchive(string archivePath, IReadOnlyList<string> relativePaths)
165204
{

src/Compatibility/Microsoft.DotNet.ApiSymbolExtensions/AssemblySymbolLoaderFactory.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using Microsoft.CodeAnalysis;
45
using Microsoft.DotNet.ApiSymbolExtensions.Logging;
56

67
namespace Microsoft.DotNet.ApiSymbolExtensions
@@ -14,6 +15,6 @@ public sealed class AssemblySymbolLoaderFactory(ILog log, bool includeInternalSy
1415
{
1516
/// <inheritdoc />
1617
public IAssemblySymbolLoader Create(bool shouldResolveReferences) =>
17-
new AssemblySymbolLoader(log, shouldResolveReferences, includeInternalSymbols);
18+
new AssemblySymbolLoader(log, resolveAssemblyReferences: shouldResolveReferences, includeInternalSymbols: includeInternalSymbols);
1819
}
1920
}

src/Compatibility/Microsoft.DotNet.ApiSymbolExtensions/Filtering/CompositeSymbolFilter.cs

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,114 @@ public sealed class CompositeSymbolFilter(params IEnumerable<ISymbolFilter> filt
1414
/// <summary>
1515
/// List on inner filters.
1616
/// </summary>
17-
public List<ISymbolFilter> Filters { get; } = new(filters);
17+
public List<ISymbolFilter> Filters { get; } = [.. filters];
18+
19+
/// <summary>
20+
/// Creates a composite filter to exclude APIs using the DocIDs provided in the specifed file paths.
21+
/// </summary>
22+
/// <param name="apiExclusionFilePaths">A collection of paths where the exclusion files should be searched.</param>
23+
/// <param name="respectInternals">Whether to include internal symbols or not.</param>
24+
/// <param name="includeEffectivelyPrivateSymbols">Whether to include effectively private symbols or not.</param>
25+
/// <param name="includeExplicitInterfaceImplementationSymbols">Whether to include explicit interface implementation symbols or not.</param>
26+
/// <returns>An instance of the symbol filter.</returns>
27+
public static ISymbolFilter GetSymbolFilterFromFiles(string[]? apiExclusionFilePaths,
28+
bool respectInternals = false,
29+
bool includeEffectivelyPrivateSymbols = true,
30+
bool includeExplicitInterfaceImplementationSymbols = true)
31+
{
32+
DocIdSymbolFilter? docIdSymbolFilter =
33+
apiExclusionFilePaths?.Count() > 0 ?
34+
DocIdSymbolFilter.CreateFromFiles(apiExclusionFilePaths) : null;
35+
36+
return GetCompositeSymbolFilter(docIdSymbolFilter, respectInternals, includeEffectivelyPrivateSymbols, includeExplicitInterfaceImplementationSymbols, withImplicitSymbolFilter: true);
37+
}
38+
39+
/// <summary>
40+
/// Creates a composite filter to exclude APIs using the DocIDs provided in the specifed list.
41+
/// </summary>
42+
/// <param name="apiExclusionList">A collection of exclusion list.</param>
43+
/// <param name="respectInternals">Whether to include internal symbols or not.</param>
44+
/// <param name="includeEffectivelyPrivateSymbols">Whether to include effectively private symbols or not.</param>
45+
/// <param name="includeExplicitInterfaceImplementationSymbols">Whether to include explicit interface implementation symbols or not.</param>
46+
/// <returns>An instance of the symbol filter.</returns>
47+
public static ISymbolFilter GetSymbolFilterFromList(string[]? apiExclusionList,
48+
bool respectInternals = false,
49+
bool includeEffectivelyPrivateSymbols = true,
50+
bool includeExplicitInterfaceImplementationSymbols = true)
51+
{
52+
DocIdSymbolFilter? docIdSymbolFilter =
53+
apiExclusionList?.Count() > 0 ?
54+
DocIdSymbolFilter.CreateFromDocIDs(apiExclusionList) : null;
55+
56+
return GetCompositeSymbolFilter(docIdSymbolFilter, respectInternals, includeEffectivelyPrivateSymbols, includeExplicitInterfaceImplementationSymbols, withImplicitSymbolFilter: true);
57+
}
58+
59+
/// <summary>
60+
/// Creates an composite filter to exclude attributes using the DocID provided in the specified file paths.
61+
/// </summary>
62+
/// <param name="attributeExclusionFilePaths">A collection of paths where the exclusion files should be searched.</param>
63+
/// <param name="respectInternals">Whether to include internal symbols or not.</param>
64+
/// <param name="includeEffectivelyPrivateSymbols">Whether to include effectively private symbols or not.</param>
65+
/// <param name="includeExplicitInterfaceImplementationSymbols">Whether to include explicit interface implementation symbols or not.</param>
66+
/// <returns>An instance of the attribute filter.</returns>
67+
public static ISymbolFilter GetAttributeFilterFromPaths(string[]? attributeExclusionFilePaths,
68+
bool respectInternals = false,
69+
bool includeEffectivelyPrivateSymbols = true,
70+
bool includeExplicitInterfaceImplementationSymbols = true)
71+
{
72+
DocIdSymbolFilter? docIdSymbolFilter =
73+
attributeExclusionFilePaths?.Count() > 0 ?
74+
DocIdSymbolFilter.CreateFromFiles(attributeExclusionFilePaths) : null;
75+
76+
return GetCompositeSymbolFilter(docIdSymbolFilter, respectInternals, includeEffectivelyPrivateSymbols, includeExplicitInterfaceImplementationSymbols, withImplicitSymbolFilter: false);
77+
}
78+
79+
/// <summary>
80+
/// Creates an composite filter to exclude attributes using the DocID provided in the specified list.
81+
/// </summary>
82+
/// <param name="attributeExclusionList">A collection of exclusion list.</param>
83+
/// <param name="respectInternals">Whether to include internal symbols or not.</param>
84+
/// <param name="includeEffectivelyPrivateSymbols">Whether to include effectively private symbols or not.</param>
85+
/// <param name="includeExplicitInterfaceImplementationSymbols">Whether to include explicit interface implementation symbols or not.</param>
86+
/// <returns>An instance of the attribute filter.</returns>
87+
public static ISymbolFilter GetAttributeFilterFromList(string[]? attributeExclusionList,
88+
bool respectInternals = false,
89+
bool includeEffectivelyPrivateSymbols = true,
90+
bool includeExplicitInterfaceImplementationSymbols = true)
91+
{
92+
DocIdSymbolFilter? docIdSymbolFilter =
93+
attributeExclusionList?.Count() > 0 ?
94+
DocIdSymbolFilter.CreateFromDocIDs(attributeExclusionList) : null;
95+
96+
return GetCompositeSymbolFilter(docIdSymbolFilter, respectInternals, includeEffectivelyPrivateSymbols, includeExplicitInterfaceImplementationSymbols, withImplicitSymbolFilter: false);
97+
}
98+
99+
private static ISymbolFilter GetCompositeSymbolFilter(DocIdSymbolFilter? customFilter,
100+
bool respectInternals,
101+
bool includeEffectivelyPrivateSymbols,
102+
bool includeExplicitInterfaceImplementationSymbols,
103+
bool withImplicitSymbolFilter)
104+
{
105+
AccessibilitySymbolFilter accessibilitySymbolFilter = new(
106+
respectInternals,
107+
includeEffectivelyPrivateSymbols,
108+
includeExplicitInterfaceImplementationSymbols);
109+
110+
CompositeSymbolFilter filter = new();
111+
112+
if (customFilter != null)
113+
{
114+
filter.Add(customFilter);
115+
}
116+
if (withImplicitSymbolFilter)
117+
{
118+
filter.Add(new ImplicitSymbolFilter());
119+
}
120+
121+
filter.Add(accessibilitySymbolFilter);
122+
123+
return filter;
124+
}
18125

19126
/// <summary>
20127
/// Determines whether the <see cref="ISymbol"/> should be included.

0 commit comments

Comments
 (0)