Skip to content

Commit

Permalink
[ApiDiff] Update symbol filtering code to be able to load a list of s…
Browse files Browse the repository at this point in the history
…trings or a list of files (#46255)
  • Loading branch information
carlossanlop authored Jan 29, 2025
1 parent e266107 commit 13355a4
Showing 7 changed files with 312 additions and 108 deletions.
Original file line number Diff line number Diff line change
@@ -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,
20 changes: 2 additions & 18 deletions src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIApp.cs
Original file line number Diff line number Diff line change
@@ -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)
@@ -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,
Original file line number Diff line number Diff line change
@@ -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.
@@ -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();
}
}
}
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

0 comments on commit 13355a4

Please sign in to comment.