Skip to content

[ApiDiff] Update symbol filtering code to be able to load a list of strings or a list of files #46255

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 12 commits into from
Jan 29, 2025
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -30,21 +30,20 @@ public ApiCompatServiceProvider(Func<ISuppressionEngine, ISuppressibleLog> logFa
AccessibilitySymbolFilter accessibilitySymbolFilter = new(respectInternals);
SymbolEqualityComparer symbolEqualityComparer = new();

// The attribute data symbol filter is a composite that contains both the accessibility
// symbol filter and the doc id symbol filter.
CompositeSymbolFilter attributeDataSymbolFilter = new(accessibilitySymbolFilter);
if (excludeAttributesFiles is not null)
{
attributeDataSymbolFilter.Add(new DocIdSymbolFilter(excludeAttributesFiles));
}
ISymbolFilter attributeDataSymbolFilter = SymbolFilterFactory.GetFilterFromFiles(
apiExclusionFilePaths: excludeAttributesFiles,
accessibilitySymbolFilter: accessibilitySymbolFilter,
respectInternals: respectInternals);

AttributeDataEqualityComparer attributeDataEqualityComparer = new(symbolEqualityComparer,
new TypedConstantEqualityComparer(symbolEqualityComparer));

ApiComparerSettings apiComparerSettings = new(
accessibilitySymbolFilter,
symbolEqualityComparer,
attributeDataSymbolFilter,
new AttributeDataEqualityComparer(symbolEqualityComparer,
new TypedConstantEqualityComparer(symbolEqualityComparer)),
respectInternals);
symbolFilter: accessibilitySymbolFilter,
symbolEqualityComparer: symbolEqualityComparer,
attributeDataSymbolFilter: attributeDataSymbolFilter,
attributeDataEqualityComparer: attributeDataEqualityComparer,
includeInternalSymbols: respectInternals);

return new ApiCompatRunner(SuppressibleLog,
SuppressionEngine,
Expand Down
20 changes: 2 additions & 18 deletions src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIApp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,22 +48,6 @@ public static void Run(ILog log,
includeEffectivelyPrivateSymbols: true,
includeExplicitInterfaceImplementationSymbols: true);

// Configure the symbol filter
CompositeSymbolFilter symbolFilter = new();
if (excludeApiFiles is not null)
{
symbolFilter.Add(new DocIdSymbolFilter(excludeApiFiles));
}
symbolFilter.Add(new ImplicitSymbolFilter());
symbolFilter.Add(accessibilitySymbolFilter);

// Configure the attribute data symbol filter
CompositeSymbolFilter attributeDataSymbolFilter = new();
if (excludeAttributesFiles is not null)
{
attributeDataSymbolFilter.Add(new DocIdSymbolFilter(excludeAttributesFiles));
}
attributeDataSymbolFilter.Add(accessibilitySymbolFilter);

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

using CSharpFileBuilder fileBuilder = new(log,
symbolFilter,
attributeDataSymbolFilter,
SymbolFilterFactory.GetFilterFromFiles(excludeApiFiles, respectInternals: respectInternals),
SymbolFilterFactory.GetFilterFromFiles(excludeAttributesFiles, respectInternals: respectInternals),
textWriter,
exceptionMessage,
includeAssemblyAttributes,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,46 @@ namespace Microsoft.DotNet.ApiSymbolExtensions.Filtering
/// Implements the logic of filtering out api.
/// Reads the file with the list of attributes, types, members in DocId format.
/// </summary>
public class DocIdSymbolFilter(string[] docIdsToExcludeFiles) : ISymbolFilter
public class DocIdSymbolFilter : ISymbolFilter
{
private readonly HashSet<string> _docIdsToExclude = new(ReadDocIdsAttributes(docIdsToExcludeFiles));
private readonly HashSet<string> _docIdsToExclude;

/// <summary>
/// Creates a filter to exclude APIs using the DocIDs provided in the specified files.
/// </summary>
/// <param name="filesWithDocIdsToExclude">A collection of files each containing multiple DocIDs to exclude.</param>
/// <returns>An instance of the symbol filter.</returns>
public static DocIdSymbolFilter CreateFromFiles(params string[] filesWithDocIdsToExclude)
{
List<string> docIds = new();

foreach (string docIdsToExcludeFile in filesWithDocIdsToExclude)
{
if (string.IsNullOrWhiteSpace(docIdsToExcludeFile))
{
continue;
}

foreach (string docId in ReadDocIdsFromList(File.ReadAllLines(docIdsToExcludeFile)))
{
docIds.Add(docId);
}
}

return new DocIdSymbolFilter(docIds);
}

/// <summary>
/// Creates a filter to exclude APIs using the DocIDs provided in the specified list.
/// </summary>
/// <param name="docIdsToExclude">A collection of DocIDs to exclude.</param>
/// <returns>An instance of the symbol filter.</returns>
public static DocIdSymbolFilter CreateFromLists(params string[] docIdsToExclude)
=> new DocIdSymbolFilter(ReadDocIdsFromList(docIdsToExclude));

// Private constructor to avoid creating an instance with an empty list.
private DocIdSymbolFilter(IEnumerable<string> docIdsToExclude)
=> _docIdsToExclude = [.. docIdsToExclude];

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

private static IEnumerable<string> ReadDocIdsAttributes(IEnumerable<string> docIdsToExcludeFiles)
private static IEnumerable<string> ReadDocIdsFromList(params string[] ids)
{
foreach (string docIdsToExcludeFile in docIdsToExcludeFiles)
foreach (string id in ids)
{
foreach (string id in File.ReadAllLines(docIdsToExcludeFile))
{
#if NET
if (!string.IsNullOrWhiteSpace(id) && !id.StartsWith('#') && !id.StartsWith("//"))
if (!string.IsNullOrWhiteSpace(id) && !id.StartsWith('#') && !id.StartsWith("//"))
#else
if (!string.IsNullOrWhiteSpace(id) && !id.StartsWith("#") && !id.StartsWith("//"))
if (!string.IsNullOrWhiteSpace(id) && !id.StartsWith("#") && !id.StartsWith("//"))
#endif
{
yield return id.Trim();
}
{
yield return id.Trim();
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.CodeAnalysis;

namespace Microsoft.DotNet.ApiSymbolExtensions.Filtering;

/// <summary>
/// A factory class to create symbol filters.
/// </summary>
public static class SymbolFilterFactory
{
/// <summary>
/// Creates a composite filter to exclude APIs using the DocIDs provided in the specifed file paths.
/// </summary>
/// <param name="apiExclusionFilePaths">A collection of paths where the exclusion files should be searched.</param>
/// <param name="accessibilitySymbolFilter">An optional custom accessibility symbol filter to use.</param>
/// <param name="respectInternals">Whether to include internal symbols or not.</param>
/// <param name="includeEffectivelyPrivateSymbols">Whether to include effectively private symbols or not.</param>
/// <param name="includeExplicitInterfaceImplementationSymbols">Whether to include explicit interface implementation symbols or not.</param>
/// <param name="includeImplicitSymbolFilter">Whether to include implicit symbols or not.</param>
/// <returns>An instance of the symbol filter.</returns>
public static ISymbolFilter GetFilterFromFiles(string[]? apiExclusionFilePaths,
AccessibilitySymbolFilter? accessibilitySymbolFilter = null,
bool respectInternals = false,
bool includeEffectivelyPrivateSymbols = true,
bool includeExplicitInterfaceImplementationSymbols = true,
bool includeImplicitSymbolFilter = true)
{
DocIdSymbolFilter? docIdSymbolFilter =
apiExclusionFilePaths?.Length > 0 ?
DocIdSymbolFilter.CreateFromFiles(apiExclusionFilePaths) : null;

return GetCompositeSymbolFilter(docIdSymbolFilter, accessibilitySymbolFilter, respectInternals, includeEffectivelyPrivateSymbols, includeExplicitInterfaceImplementationSymbols, includeImplicitSymbolFilter);
}

/// <summary>
/// Creates a composite filter to exclude APIs using the DocIDs provided in the specifed list.
/// </summary>
/// <param name="apiExclusionList">A collection of exclusion list.</param>
/// <param name="accessibilitySymbolFilter">An optional custom accessibility symbol filter to use.</param>
/// <param name="respectInternals">Whether to include internal symbols or not.</param>
/// <param name="includeEffectivelyPrivateSymbols">Whether to include effectively private symbols or not.</param>
/// <param name="includeExplicitInterfaceImplementationSymbols">Whether to include explicit interface implementation symbols or not.</param>
/// <param name="includeImplicitSymbolFilter">Whether to include implicit symbols or not.</param>
/// <returns>An instance of the symbol filter.</returns>
public static ISymbolFilter GetFilterFromList(string[]? apiExclusionList,
AccessibilitySymbolFilter? accessibilitySymbolFilter = null,
bool respectInternals = false,
bool includeEffectivelyPrivateSymbols = true,
bool includeExplicitInterfaceImplementationSymbols = true,
bool includeImplicitSymbolFilter = true)
{
DocIdSymbolFilter? docIdSymbolFilter =
apiExclusionList?.Count() > 0 ?
DocIdSymbolFilter.CreateFromLists(apiExclusionList) : null;

return GetCompositeSymbolFilter(docIdSymbolFilter, accessibilitySymbolFilter, respectInternals, includeEffectivelyPrivateSymbols, includeExplicitInterfaceImplementationSymbols, includeImplicitSymbolFilter);
}

private static ISymbolFilter GetCompositeSymbolFilter(DocIdSymbolFilter? customFilter,
AccessibilitySymbolFilter? accessibilitySymbolFilter,
bool respectInternals,
bool includeEffectivelyPrivateSymbols,
bool includeExplicitInterfaceImplementationSymbols,
bool includeImplicitSymbolFilter)
{
accessibilitySymbolFilter ??= new(
respectInternals,
includeEffectivelyPrivateSymbols,
includeExplicitInterfaceImplementationSymbols);

CompositeSymbolFilter filter = new();

if (customFilter != null)
{
filter.Add(customFilter);
}
if (includeImplicitSymbolFilter)
{
filter.Add(new ImplicitSymbolFilter());
}

filter.Add(accessibilitySymbolFilter);

return filter;
}
}
Loading
Loading