File: src\RoslynAnalyzers\Utilities\Compiler\Extensions\ISymbolExtensions.cs
Web Access
Project: src\src\RoslynAnalyzers\PerformanceSensitiveAnalyzers\Core\Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers.csproj (Microsoft.CodeAnalysis.PerformanceSensitiveAnalyzers)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#nullable disable warnings
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Utilities;
// using Microsoft.CodeAnalysis.Shared.Utilities;
 
namespace Analyzer.Utilities.Extensions
{
    internal static class ISymbolExtensions
    {
        public static bool IsDefaultConstructor([NotNullWhen(returnValue: true)] this ISymbol? symbol)
        {
            return symbol.IsConstructor() && symbol.GetParameters().IsEmpty;
        }
 
        public static bool IsPublic(this ISymbol symbol)
        {
            return symbol.DeclaredAccessibility == Accessibility.Public;
        }
 
        public static bool IsPrivate(this ISymbol symbol)
        {
            return symbol.DeclaredAccessibility == Accessibility.Private;
        }
 
        public static bool IsIndexer([NotNullWhen(returnValue: true)] this ISymbol? symbol)
        {
            return symbol is IPropertySymbol { IsIndexer: true };
        }
 
        public static bool IsPropertyWithBackingField([NotNullWhen(returnValue: true)] this ISymbol? symbol, [NotNullWhen(true)] out IFieldSymbol? backingField)
        {
            if (symbol is IPropertySymbol propertySymbol)
            {
                foreach (ISymbol member in propertySymbol.ContainingType.GetMembers())
                {
                    if (member is IFieldSymbol associated &&
                        associated.IsImplicitlyDeclared &&
                        Equals(associated.AssociatedSymbol, propertySymbol))
                    {
                        backingField = associated;
                        return true;
                    }
                }
            }
 
            backingField = null;
            return false;
        }
 
        /// <summary>
        /// True if the symbol is externally visible outside this assembly.
        /// </summary>
        public static bool IsExternallyVisible(this ISymbol symbol) =>
            symbol.GetResultantVisibility() == SymbolVisibility.Public;
 
        /// <summary>
        /// Check whether parameter count and parameter types of the given methods are same.
        /// </summary>
        public static bool ParametersAreSame(this IMethodSymbol method1, IMethodSymbol method2)
        {
            if (method1.Parameters.Length != method2.Parameters.Length)
            {
                return false;
            }
 
            for (int index = 0; index < method1.Parameters.Length; index++)
            {
                if (!ParameterTypesAreSame(method1.Parameters[index], method2.Parameters[index]))
                {
                    return false;
                }
            }
 
            return true;
        }
 
        /// <summary>
        /// Check whether parameter types of the given methods are same for given parameter indices.
        /// </summary>
        public static bool ParameterTypesAreSame(this IMethodSymbol method1, IMethodSymbol method2, IEnumerable<int> parameterIndices, CancellationToken cancellationToken)
        {
            foreach (int index in parameterIndices)
            {
                cancellationToken.ThrowIfCancellationRequested();
 
                if (!ParameterTypesAreSame(method1.Parameters[index], method2.Parameters[index]))
                {
                    return false;
                }
            }
 
            return true;
        }
 
        public static bool ParameterTypesAreSame(this IParameterSymbol parameter1, IParameterSymbol parameter2)
        {
            var type1 = parameter1.Type.OriginalDefinition;
            var type2 = parameter2.Type.OriginalDefinition;
 
            if (type1.TypeKind == TypeKind.TypeParameter &&
                type2.TypeKind == TypeKind.TypeParameter &&
                ((ITypeParameterSymbol)type1).Ordinal == ((ITypeParameterSymbol)type2).Ordinal)
            {
                return true;
            }
 
            // this doesn't account for type conversion but FxCop implementation seems doesn't either
            // so this should match FxCop implementation.
            return SymbolEqualityComparer.Default.Equals(type2, type1);
        }
 
        /// <summary>
        /// Check whether return type, parameters count and parameter types are same for the given methods.
        /// </summary>
        public static bool ReturnTypeAndParametersAreSame(this IMethodSymbol method, IMethodSymbol otherMethod)
            => SymbolEqualityComparer.Default.Equals(method.ReturnType, otherMethod.ReturnType) &&
               method.ParametersAreSame(otherMethod);
 
        /// <summary>
        /// Checks if a given symbol implements an interface member implicitly or explicitly
        /// </summary>
        public static bool IsImplementationOfAnyInterfaceMember(this ISymbol symbol)
        {
            return symbol.IsImplementationOfAnyExplicitInterfaceMember() || symbol.IsImplementationOfAnyImplicitInterfaceMember();
        }
 
        public static bool IsImplementationOfAnyImplicitInterfaceMember(this ISymbol symbol)
        {
            return IsImplementationOfAnyImplicitInterfaceMember<ISymbol>(symbol);
        }
 
        /// <summary>
        /// Checks if a given symbol implements an interface member implicitly
        /// </summary>
        public static bool IsImplementationOfAnyImplicitInterfaceMember<TSymbol>(this ISymbol symbol)
            where TSymbol : ISymbol
        {
            if (symbol.ContainingType != null)
            {
                foreach (INamedTypeSymbol interfaceSymbol in symbol.ContainingType.AllInterfaces)
                {
                    foreach (var interfaceMember in interfaceSymbol.GetMembers().OfType<TSymbol>())
                    {
                        if (IsImplementationOfInterfaceMember(symbol, interfaceMember))
                        {
                            return true;
                        }
                    }
                }
            }
 
            return false;
        }
 
        public static bool IsImplementationOfInterfaceMember(this ISymbol symbol, [NotNullWhen(returnValue: true)] ISymbol? interfaceMember)
        {
            return interfaceMember != null &&
                SymbolEqualityComparer.Default.Equals(symbol, symbol.ContainingType.FindImplementationForInterfaceMember(interfaceMember));
        }
 
        /// <summary>
        /// Checks if a given symbol implements an interface member or overrides an implementation of an interface member.
        /// </summary>
        public static bool IsOverrideOrImplementationOfInterfaceMember(this ISymbol symbol, [NotNullWhen(returnValue: true)] ISymbol? interfaceMember)
        {
            if (interfaceMember == null)
            {
                return false;
            }
 
            if (symbol.IsImplementationOfInterfaceMember(interfaceMember))
            {
                return true;
            }
 
            return symbol.IsOverride &&
                symbol.GetOverriddenMember()?.IsOverrideOrImplementationOfInterfaceMember(interfaceMember) == true;
        }
 
        /// <summary>
        /// Checks if a given symbol implements an interface member explicitly
        /// </summary>
        public static bool IsImplementationOfAnyExplicitInterfaceMember([NotNullWhen(returnValue: true)] this ISymbol? symbol)
        {
            if (symbol is IMethodSymbol methodSymbol && !methodSymbol.ExplicitInterfaceImplementations.IsEmpty)
            {
                return true;
            }
 
            if (symbol is IPropertySymbol propertySymbol && !propertySymbol.ExplicitInterfaceImplementations.IsEmpty)
            {
                return true;
            }
 
            if (symbol is IEventSymbol eventSymbol && !eventSymbol.ExplicitInterfaceImplementations.IsEmpty)
            {
                return true;
            }
 
            return false;
        }
 
        public static ITypeSymbol? GetMemberOrLocalOrParameterType(this ISymbol symbol)
        {
            return symbol.Kind switch
            {
                SymbolKind.Local => ((ILocalSymbol)symbol).Type,
 
                SymbolKind.Parameter => ((IParameterSymbol)symbol).Type,
 
                _ => symbol.GetMemberType(),
            };
        }
 
        public static bool IsReadOnlyFieldOrProperty([NotNullWhen(returnValue: true)] this ISymbol? symbol)
        {
            return symbol switch
            {
                IFieldSymbol field => field.IsReadOnly,
 
                IPropertySymbol property => property.IsReadOnly,
 
                _ => false,
            };
        }
 
        public static AttributeData? GetAttribute(this ISymbol symbol, [NotNullWhen(true)] INamedTypeSymbol? attributeType)
        {
            return symbol.GetAttributes(attributeType).FirstOrDefault();
        }
 
        public static IEnumerable<AttributeData> GetAttributes(this ISymbol symbol, IEnumerable<INamedTypeSymbol?> attributesToMatch)
        {
            foreach (var attribute in symbol.GetAttributes())
            {
                if (attribute.AttributeClass == null)
                    continue;
 
                foreach (var attributeToMatch in attributesToMatch)
                {
                    if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, attributeToMatch))
                    {
                        yield return attribute;
                        break;
                    }
                }
            }
        }
 
        public static IEnumerable<AttributeData> GetAttributes(this ISymbol symbol, params INamedTypeSymbol?[] attributeTypesToMatch)
        {
            return symbol.GetAttributes(attributesToMatch: attributeTypesToMatch);
        }
 
        public static bool HasAnyAttribute(this ISymbol symbol, IEnumerable<INamedTypeSymbol> attributesToMatch)
        {
            return symbol.GetAttributes(attributesToMatch).Any();
        }
 
        public static bool HasAnyAttribute(this ISymbol symbol, params INamedTypeSymbol?[] attributeTypesToMatch)
        {
            return symbol.GetAttributes(attributeTypesToMatch).Any();
        }
 
        /// <summary>
        /// Returns a value indicating whether the specified or inherited symbol has the specified
        /// attribute.
        /// </summary>
        /// <param name="symbol">
        /// The symbol being examined.
        /// </param>
        /// <param name="attribute">
        /// The attribute in question.
        /// </param>
        /// <returns>
        /// <see langword="true"/> if <paramref name="symbol"/> has an attribute of type
        /// <paramref name="attribute"/>; otherwise <see langword="false"/>.
        /// </returns>
        public static bool HasDerivedTypeAttribute(this ITypeSymbol symbol, [NotNullWhen(returnValue: true)] INamedTypeSymbol? attribute)
        {
            if (attribute == null)
            {
                return false;
            }
 
            while (symbol != null)
            {
                if (symbol.HasAnyAttribute(attribute))
                {
                    return true;
                }
 
                if (symbol.BaseType == null)
                {
                    return false;
                }
 
                symbol = symbol.BaseType;
            }
 
            return false;
        }
 
        /// <summary>
        /// Returns a value indicating whether the specified or inherited method symbol has the specified
        /// attribute.
        /// </summary>
        /// <param name="symbol">
        /// The symbol being examined.
        /// </param>
        /// <param name="attribute">
        /// The attribute in question.
        /// </param>
        /// <returns>
        /// <see langword="true"/> if <paramref name="symbol"/> has an attribute of type
        /// <paramref name="attribute"/>; otherwise <see langword="false"/>.
        /// </returns>
        public static bool HasDerivedMethodAttribute(this IMethodSymbol symbol, [NotNullWhen(returnValue: true)] INamedTypeSymbol? attribute)
        {
            if (attribute == null)
            {
                return false;
            }
 
            while (symbol != null)
            {
                if (symbol.HasAnyAttribute(attribute))
                {
                    return true;
                }
 
                if (symbol.OverriddenMethod == null)
                {
                    return false;
                }
 
                symbol = symbol.OverriddenMethod;
            }
 
            return false;
        }
 
        public static bool IsLambdaOrLocalFunction([NotNullWhen(returnValue: true)] this ISymbol? symbol)
            => (symbol as IMethodSymbol)?.IsLambdaOrLocalFunction() == true;
 
        public static bool IsConst([NotNullWhen(returnValue: true)] this ISymbol? symbol)
        {
            return symbol switch
            {
                IFieldSymbol field => field.IsConst,
 
                ILocalSymbol local => local.IsConst,
 
                _ => false,
            };
        }
 
        public static bool IsReadOnly([NotNullWhen(returnValue: true)] this ISymbol? symbol)
            => symbol switch
            {
                IFieldSymbol field => field.IsReadOnly,
                IPropertySymbol property => property.IsReadOnly,
#if CODEANALYSIS_V3_OR_BETTER
                IMethodSymbol method => method.IsReadOnly,
                ITypeSymbol type => type.IsReadOnly,
#endif
                _ => false,
            };
    }
}