Skip to content

Commit 13355a4

Browse files
authored
[ApiDiff] Update symbol filtering code to be able to load a list of strings or a list of files (#46255)
1 parent e266107 commit 13355a4

File tree

7 files changed

+312
-108
lines changed

7 files changed

+312
-108
lines changed

Diff for: src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ApiCompatServiceProvider.cs

+12-13
Original file line numberDiff line numberDiff line change
@@ -30,21 +30,20 @@ public ApiCompatServiceProvider(Func<ISuppressionEngine, ISuppressibleLog> logFa
3030
AccessibilitySymbolFilter accessibilitySymbolFilter = new(respectInternals);
3131
SymbolEqualityComparer symbolEqualityComparer = new();
3232

33-
// The attribute data symbol filter is a composite that contains both the accessibility
34-
// symbol filter and the doc id symbol filter.
35-
CompositeSymbolFilter attributeDataSymbolFilter = new(accessibilitySymbolFilter);
36-
if (excludeAttributesFiles is not null)
37-
{
38-
attributeDataSymbolFilter.Add(new DocIdSymbolFilter(excludeAttributesFiles));
39-
}
33+
ISymbolFilter attributeDataSymbolFilter = SymbolFilterFactory.GetFilterFromFiles(
34+
apiExclusionFilePaths: excludeAttributesFiles,
35+
accessibilitySymbolFilter: accessibilitySymbolFilter,
36+
respectInternals: respectInternals);
37+
38+
AttributeDataEqualityComparer attributeDataEqualityComparer = new(symbolEqualityComparer,
39+
new TypedConstantEqualityComparer(symbolEqualityComparer));
4040

4141
ApiComparerSettings apiComparerSettings = new(
42-
accessibilitySymbolFilter,
43-
symbolEqualityComparer,
44-
attributeDataSymbolFilter,
45-
new AttributeDataEqualityComparer(symbolEqualityComparer,
46-
new TypedConstantEqualityComparer(symbolEqualityComparer)),
47-
respectInternals);
42+
symbolFilter: accessibilitySymbolFilter,
43+
symbolEqualityComparer: symbolEqualityComparer,
44+
attributeDataSymbolFilter: attributeDataSymbolFilter,
45+
attributeDataEqualityComparer: attributeDataEqualityComparer,
46+
includeInternalSymbols: respectInternals);
4847

4948
return new ApiCompatRunner(SuppressibleLog,
5049
SuppressionEngine,

Diff for: src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIApp.cs

+2-18
Original file line numberDiff line numberDiff line change
@@ -48,22 +48,6 @@ public static void Run(ILog log,
4848
includeEffectivelyPrivateSymbols: true,
4949
includeExplicitInterfaceImplementationSymbols: true);
5050

51-
// Configure the symbol filter
52-
CompositeSymbolFilter symbolFilter = new();
53-
if (excludeApiFiles is not null)
54-
{
55-
symbolFilter.Add(new DocIdSymbolFilter(excludeApiFiles));
56-
}
57-
symbolFilter.Add(new ImplicitSymbolFilter());
58-
symbolFilter.Add(accessibilitySymbolFilter);
59-
60-
// Configure the attribute data symbol filter
61-
CompositeSymbolFilter attributeDataSymbolFilter = new();
62-
if (excludeAttributesFiles is not null)
63-
{
64-
attributeDataSymbolFilter.Add(new DocIdSymbolFilter(excludeAttributesFiles));
65-
}
66-
attributeDataSymbolFilter.Add(accessibilitySymbolFilter);
6751

6852
// Invoke the CSharpFileBuilder for each directly loaded assembly.
6953
foreach (IAssemblySymbol? assemblySymbol in assemblySymbols)
@@ -75,8 +59,8 @@ public static void Run(ILog log,
7559
textWriter.Write(headerFileText);
7660

7761
using CSharpFileBuilder fileBuilder = new(log,
78-
symbolFilter,
79-
attributeDataSymbolFilter,
62+
SymbolFilterFactory.GetFilterFromFiles(excludeApiFiles, respectInternals: respectInternals),
63+
SymbolFilterFactory.GetFilterFromFiles(excludeAttributesFiles, respectInternals: respectInternals),
8064
textWriter,
8165
exceptionMessage,
8266
includeAssemblyAttributes,

Diff for: src/Compatibility/Microsoft.DotNet.ApiSymbolExtensions/Filtering/DocIdSymbolFilter.cs

+45-11
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,46 @@ namespace Microsoft.DotNet.ApiSymbolExtensions.Filtering
99
/// Implements the logic of filtering out api.
1010
/// Reads the file with the list of attributes, types, members in DocId format.
1111
/// </summary>
12-
public class DocIdSymbolFilter(string[] docIdsToExcludeFiles) : ISymbolFilter
12+
public class DocIdSymbolFilter : ISymbolFilter
1313
{
14-
private readonly HashSet<string> _docIdsToExclude = new(ReadDocIdsAttributes(docIdsToExcludeFiles));
14+
private readonly HashSet<string> _docIdsToExclude;
15+
16+
/// <summary>
17+
/// Creates a filter to exclude APIs using the DocIDs provided in the specified files.
18+
/// </summary>
19+
/// <param name="filesWithDocIdsToExclude">A collection of files each containing multiple DocIDs to exclude.</param>
20+
/// <returns>An instance of the symbol filter.</returns>
21+
public static DocIdSymbolFilter CreateFromFiles(params string[] filesWithDocIdsToExclude)
22+
{
23+
List<string> docIds = new();
24+
25+
foreach (string docIdsToExcludeFile in filesWithDocIdsToExclude)
26+
{
27+
if (string.IsNullOrWhiteSpace(docIdsToExcludeFile))
28+
{
29+
continue;
30+
}
31+
32+
foreach (string docId in ReadDocIdsFromList(File.ReadAllLines(docIdsToExcludeFile)))
33+
{
34+
docIds.Add(docId);
35+
}
36+
}
37+
38+
return new DocIdSymbolFilter(docIds);
39+
}
40+
41+
/// <summary>
42+
/// Creates a filter to exclude APIs using the DocIDs provided in the specified list.
43+
/// </summary>
44+
/// <param name="docIdsToExclude">A collection of DocIDs to exclude.</param>
45+
/// <returns>An instance of the symbol filter.</returns>
46+
public static DocIdSymbolFilter CreateFromLists(params string[] docIdsToExclude)
47+
=> new DocIdSymbolFilter(ReadDocIdsFromList(docIdsToExclude));
48+
49+
// Private constructor to avoid creating an instance with an empty list.
50+
private DocIdSymbolFilter(IEnumerable<string> docIdsToExclude)
51+
=> _docIdsToExclude = [.. docIdsToExclude];
1552

1653
/// <summary>
1754
/// Determines whether the <see cref="ISymbol"/> should be included.
@@ -29,20 +66,17 @@ public bool Include(ISymbol symbol)
2966
return true;
3067
}
3168

32-
private static IEnumerable<string> ReadDocIdsAttributes(IEnumerable<string> docIdsToExcludeFiles)
69+
private static IEnumerable<string> ReadDocIdsFromList(params string[] ids)
3370
{
34-
foreach (string docIdsToExcludeFile in docIdsToExcludeFiles)
71+
foreach (string id in ids)
3572
{
36-
foreach (string id in File.ReadAllLines(docIdsToExcludeFile))
37-
{
3873
#if NET
39-
if (!string.IsNullOrWhiteSpace(id) && !id.StartsWith('#') && !id.StartsWith("//"))
74+
if (!string.IsNullOrWhiteSpace(id) && !id.StartsWith('#') && !id.StartsWith("//"))
4075
#else
41-
if (!string.IsNullOrWhiteSpace(id) && !id.StartsWith("#") && !id.StartsWith("//"))
76+
if (!string.IsNullOrWhiteSpace(id) && !id.StartsWith("#") && !id.StartsWith("//"))
4277
#endif
43-
{
44-
yield return id.Trim();
45-
}
78+
{
79+
yield return id.Trim();
4680
}
4781
}
4882
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using Microsoft.CodeAnalysis;
5+
6+
namespace Microsoft.DotNet.ApiSymbolExtensions.Filtering;
7+
8+
/// <summary>
9+
/// A factory class to create symbol filters.
10+
/// </summary>
11+
public static class SymbolFilterFactory
12+
{
13+
/// <summary>
14+
/// Creates a composite filter to exclude APIs using the DocIDs provided in the specifed file paths.
15+
/// </summary>
16+
/// <param name="apiExclusionFilePaths">A collection of paths where the exclusion files should be searched.</param>
17+
/// <param name="accessibilitySymbolFilter">An optional custom accessibility symbol filter to use.</param>
18+
/// <param name="respectInternals">Whether to include internal symbols or not.</param>
19+
/// <param name="includeEffectivelyPrivateSymbols">Whether to include effectively private symbols or not.</param>
20+
/// <param name="includeExplicitInterfaceImplementationSymbols">Whether to include explicit interface implementation symbols or not.</param>
21+
/// <param name="includeImplicitSymbolFilter">Whether to include implicit symbols or not.</param>
22+
/// <returns>An instance of the symbol filter.</returns>
23+
public static ISymbolFilter GetFilterFromFiles(string[]? apiExclusionFilePaths,
24+
AccessibilitySymbolFilter? accessibilitySymbolFilter = null,
25+
bool respectInternals = false,
26+
bool includeEffectivelyPrivateSymbols = true,
27+
bool includeExplicitInterfaceImplementationSymbols = true,
28+
bool includeImplicitSymbolFilter = true)
29+
{
30+
DocIdSymbolFilter? docIdSymbolFilter =
31+
apiExclusionFilePaths?.Length > 0 ?
32+
DocIdSymbolFilter.CreateFromFiles(apiExclusionFilePaths) : null;
33+
34+
return GetCompositeSymbolFilter(docIdSymbolFilter, accessibilitySymbolFilter, respectInternals, includeEffectivelyPrivateSymbols, includeExplicitInterfaceImplementationSymbols, includeImplicitSymbolFilter);
35+
}
36+
37+
/// <summary>
38+
/// Creates a composite filter to exclude APIs using the DocIDs provided in the specifed list.
39+
/// </summary>
40+
/// <param name="apiExclusionList">A collection of exclusion list.</param>
41+
/// <param name="accessibilitySymbolFilter">An optional custom accessibility symbol filter to use.</param>
42+
/// <param name="respectInternals">Whether to include internal symbols or not.</param>
43+
/// <param name="includeEffectivelyPrivateSymbols">Whether to include effectively private symbols or not.</param>
44+
/// <param name="includeExplicitInterfaceImplementationSymbols">Whether to include explicit interface implementation symbols or not.</param>
45+
/// <param name="includeImplicitSymbolFilter">Whether to include implicit symbols or not.</param>
46+
/// <returns>An instance of the symbol filter.</returns>
47+
public static ISymbolFilter GetFilterFromList(string[]? apiExclusionList,
48+
AccessibilitySymbolFilter? accessibilitySymbolFilter = null,
49+
bool respectInternals = false,
50+
bool includeEffectivelyPrivateSymbols = true,
51+
bool includeExplicitInterfaceImplementationSymbols = true,
52+
bool includeImplicitSymbolFilter = true)
53+
{
54+
DocIdSymbolFilter? docIdSymbolFilter =
55+
apiExclusionList?.Count() > 0 ?
56+
DocIdSymbolFilter.CreateFromLists(apiExclusionList) : null;
57+
58+
return GetCompositeSymbolFilter(docIdSymbolFilter, accessibilitySymbolFilter, respectInternals, includeEffectivelyPrivateSymbols, includeExplicitInterfaceImplementationSymbols, includeImplicitSymbolFilter);
59+
}
60+
61+
private static ISymbolFilter GetCompositeSymbolFilter(DocIdSymbolFilter? customFilter,
62+
AccessibilitySymbolFilter? accessibilitySymbolFilter,
63+
bool respectInternals,
64+
bool includeEffectivelyPrivateSymbols,
65+
bool includeExplicitInterfaceImplementationSymbols,
66+
bool includeImplicitSymbolFilter)
67+
{
68+
accessibilitySymbolFilter ??= new(
69+
respectInternals,
70+
includeEffectivelyPrivateSymbols,
71+
includeExplicitInterfaceImplementationSymbols);
72+
73+
CompositeSymbolFilter filter = new();
74+
75+
if (customFilter != null)
76+
{
77+
filter.Add(customFilter);
78+
}
79+
if (includeImplicitSymbolFilter)
80+
{
81+
filter.Add(new ImplicitSymbolFilter());
82+
}
83+
84+
filter.Add(accessibilitySymbolFilter);
85+
86+
return filter;
87+
}
88+
}

0 commit comments

Comments
 (0)