|
// 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;
using Microsoft.DotNet.ApiSymbolExtensions;
using Microsoft.DotNet.ApiSymbolExtensions.Filtering;
namespace Microsoft.DotNet.ApiSymbolExtensions.Filtering
{
/// <summary>
/// Filter out implicitly generated members for properties, events, etc.
/// </summary>
public class ImplicitSymbolFilter : ISymbolFilter
{
/// <summary>
/// Determines whether implicitly generated symbols <see cref="ISymbol"/> should be included.
/// </summary>
/// <param name="symbol"><see cref="ISymbol"/> to evaluate.</param>
/// <returns>True to include the <paramref name="symbol"/> or false to filter it out.</returns>
public bool Include(ISymbol symbol)
{
if (symbol is IMethodSymbol method)
{
if (method.IsImplicitlyDeclared ||
method.Kind == SymbolKind.NamedType ||
method.MethodKind == MethodKind.PropertyGet ||
method.MethodKind == MethodKind.PropertySet ||
method.MethodKind == MethodKind.EventAdd ||
method.MethodKind == MethodKind.EventRemove ||
method.MethodKind == MethodKind.EventRaise ||
method.MethodKind == MethodKind.DelegateInvoke)
{
return false;
}
// If the method is an explicitly implemented getter or setter, exclude it.
// https://github.com/dotnet/roslyn/issues/53911
if (method.MethodKind == MethodKind.ExplicitInterfaceImplementation &&
method.ExplicitInterfaceImplementations.Any(m => m is { MethodKind: MethodKind.PropertyGet or MethodKind.PropertySet }))
{
return false;
}
}
if (symbol is ITypeSymbol type)
{
if (type.DeclaredAccessibility == Accessibility.Internal && (
// exclude the compiler generated `<Module>` type
type.Name == "<Module>" ||
// exclude any types which the compiler embedded - marked with EmbeddedAttribute.
// these will be generated by the compiler when compiling C# syntax that requires them.
type.GetAttributes().Any(a => a.AttributeClass?.Name == "EmbeddedAttribute" && a.AttributeClass?.ContainingNamespace.ToDisplayString() == "Microsoft.CodeAnalysis")))
{
return false;
}
}
// exclude compiler-synthesized members on record
if (symbol.ContainingType is { IsRecord: true })
{
if (symbol.IsCompilerGenerated())
{
return false;
}
// see if we can identify the record parameter syntax by locating the compiler generated constructor
if (symbol.ContainingType.TryGetRecordConstructor(out IMethodSymbol? recordConstructor))
{
// exclude the compiler generated constructor
if (SymbolEqualityComparer.Default.Equals(symbol, recordConstructor))
{
return false;
}
// exclude the compiler generated properties
if (symbol is IPropertySymbol)
{
// Exclude members with the same name as the record constructor's parameters
return !recordConstructor.Parameters.Any(p => p.Name == symbol.Name);
}
}
}
return true;
}
}
}
|