File: FindSymbols\FindReferences\Finders\ExplicitConversionSymbolReferenceFinder.cs
Web Access
Project: src\src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj (Microsoft.CodeAnalysis.Workspaces)
// 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.
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.FindSymbols.Finders;
 
internal sealed partial class ExplicitConversionSymbolReferenceFinder : AbstractMethodOrPropertyOrEventSymbolReferenceFinder<IMethodSymbol>
{
    protected override bool CanFind(IMethodSymbol symbol)
        => symbol is { MethodKind: MethodKind.Conversion, Name: WellKnownMemberNames.ExplicitConversionName or WellKnownMemberNames.ImplicitConversionName } &&
           GetUnderlyingNamedType(symbol.ReturnType) is not null;
 
    private static INamedTypeSymbol? GetUnderlyingNamedType(ITypeSymbol symbol)
        => UnderlyingNamedTypeVisitor.Instance.Visit(symbol);
 
    protected sealed override async Task DetermineDocumentsToSearchAsync<TData>(
        IMethodSymbol symbol,
        HashSet<string>? globalAliases,
        Project project,
        IImmutableSet<Document>? documents,
        Action<Document, TData> processResult,
        TData processResultData,
        FindReferencesSearchOptions options,
        CancellationToken cancellationToken)
    {
        // Look for documents that both contain an explicit cast in them as well as a reference to the type in the
        // explicit conversion.  i.e. if we have `public static explicit operator Goo(Bar b);` we want to find files
        // both with `Goo` `and `(...)` in them as we're looking for cases of `(Goo)...`.
        //
        // Note that explicit conversions may be to complex types (like arrays).  For example:
        //
        //      public static explicit operator Goo[](Bar b);
        //
        // So we need to find the underlying named type `Goo` (if there is one) to find references.
 
        var underlyingNamedType = GetUnderlyingNamedType(symbol.ReturnType);
        Contract.ThrowIfNull(underlyingNamedType);
 
        using var _ = PooledHashSet<Document>.GetInstance(out var result);
        await FindDocumentsAsync(project, documents, StandardCallbacks<Document>.AddToHashSet, result, cancellationToken, underlyingNamedType.Name).ConfigureAwait(false);
        await FindDocumentsAsync(project, documents, underlyingNamedType.SpecialType.ToPredefinedType(), StandardCallbacks<Document>.AddToHashSet, result, cancellationToken).ConfigureAwait(false);
 
        // Ignore any documents that don't also have an explicit cast in them.
        foreach (var document in result)
        {
            var index = await SyntaxTreeIndex.GetRequiredIndexAsync(document, cancellationToken).ConfigureAwait(false);
            if (index.ContainsConversion)
                processResult(document, processResultData);
        }
    }
 
    protected sealed override void FindReferencesInDocument<TData>(
        IMethodSymbol symbol,
        FindReferencesDocumentState state,
        Action<FinderLocation, TData> processResult,
        TData processResultData,
        FindReferencesSearchOptions options,
        CancellationToken cancellationToken)
    {
        var tokens = state.Root
            .DescendantTokens(descendIntoTrivia: true)
            .WhereAsArray(
                static (token, state) => IsPotentialReference(state.SyntaxFacts, token),
                state);
 
        FindReferencesInTokens(symbol, state, tokens, processResult, processResultData, cancellationToken);
    }
 
    private static bool IsPotentialReference(
        ISyntaxFactsService syntaxFacts, SyntaxToken token)
    {
        var node = token.GetRequiredParent();
        return node.GetFirstToken() == token && syntaxFacts.IsConversionExpression(node);
    }
}