// 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.Concurrent; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using Analyzer.Utilities.Lightup; using Microsoft.CodeAnalysis; namespace Analyzer.Utilities.Extensions { internal static class INamedTypeSymbolExtensions { public static bool IsFileLocal(this INamedTypeSymbol symbol) => symbol.IsFileLocal; /// <summary> /// Returns a value indicating whether <paramref name="type"/> derives from, or implements /// any generic construction of, the type defined by <paramref name="parentType"/>. /// </summary> /// <remarks> /// This method only works when <paramref name="parentType"/> is a definition, /// not a constructed type. /// </remarks> /// <example> /// <para> /// If <paramref name="parentType"/> is the class <see cref="Stack{T}"/>, then this /// method will return <see langword="true"/> when called on <c>Stack>int></c> /// or any type derived it, because <c>Stack>int></c> is constructed from /// <see cref="Stack{T}"/>. /// </para> /// <para> /// Similarly, if <paramref name="parentType"/> is the interface <see cref="IList{T}"/>, /// then this method will return <see langword="true"/> for <c>List>int></c> /// or any other class that extends <see cref="IList{T}"/> or an class that implements it, /// because <c>IList>int></c> is constructed from <see cref="IList{T}"/>. /// </para> /// </example> public static bool DerivesFromOrImplementsAnyConstructionOf(this INamedTypeSymbol type, INamedTypeSymbol parentType) { if (!parentType.IsDefinition) { throw new ArgumentException($"The type {nameof(parentType)} is not a definition; it is a constructed type", nameof(parentType)); } for (INamedTypeSymbol? baseType = type.OriginalDefinition; baseType != null; baseType = baseType.BaseType?.OriginalDefinition) { if (baseType.Equals(parentType)) { return true; } } if (type.OriginalDefinition.AllInterfaces.Any(baseInterface => baseInterface.OriginalDefinition.Equals(parentType))) { return true; } return false; } public static bool OverridesEquals(this INamedTypeSymbol symbol) { // Does the symbol override Object.Equals? return symbol.GetMembers(WellKnownMemberNames.ObjectEquals).OfType<IMethodSymbol>().Any(m => m.IsObjectEqualsOverride()); } public static bool IsBenchmarkOrXUnitTestAttribute(this INamedTypeSymbol attributeClass, ConcurrentDictionary<INamedTypeSymbol, bool> knownTestAttributes, INamedTypeSymbol? benchmarkAttribute, INamedTypeSymbol? xunitFactAttribute) { if (knownTestAttributes.TryGetValue(attributeClass, out var isTest)) return isTest; var derivedFromKnown = (xunitFactAttribute is not null && attributeClass.DerivesFrom(xunitFactAttribute)) || (benchmarkAttribute is not null && attributeClass.DerivesFrom(benchmarkAttribute)); return knownTestAttributes.GetOrAdd(attributeClass, derivedFromKnown); } } } |