File: FindSymbols\FindReferences\Finders\ExplicitConversionSymbolReferenceFinder.cs
Web Access
Project: ..\..\..\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.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
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<ImmutableArray<Document>> DetermineDocumentsToSearchAsync(
            IMethodSymbol symbol,
            HashSet<string>? globalAliases,
            Project project,
            IImmutableSet<Document>? documents,
            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);
            var documentsWithName = await FindDocumentsAsync(project, documents, cancellationToken, underlyingNamedType.Name).ConfigureAwait(false);
            var documentsWithType = await FindDocumentsAsync(project, documents, underlyingNamedType.SpecialType.ToPredefinedType(), cancellationToken).ConfigureAwait(false);
 
            using var _ = ArrayBuilder<Document>.GetInstance(out var result);
 
            // Ignore any documents that don't also have an explicit cast in them.
            foreach (var document in documentsWithName.Concat(documentsWithType).Distinct())
            {
                var index = await SyntaxTreeIndex.GetRequiredIndexAsync(document, cancellationToken).ConfigureAwait(false);
                if (index.ContainsConversion)
                    result.Add(document);
            }
 
            return result.ToImmutable();
        }
 
        protected sealed override ValueTask<ImmutableArray<FinderLocation>> FindReferencesInDocumentAsync(
            IMethodSymbol symbol,
            FindReferencesDocumentState state,
            FindReferencesSearchOptions options,
            CancellationToken cancellationToken)
        {
            var tokens = state.Root
                .DescendantTokens(descendIntoTrivia: true)
                .WhereAsArray(
                    static (token, state) => IsPotentialReference(state.SyntaxFacts, token),
                    state);
 
            return FindReferencesInTokensAsync(symbol, state, tokens, cancellationToken);
        }
 
        private static bool IsPotentialReference(
            ISyntaxFactsService syntaxFacts, SyntaxToken token)
        {
            var node = token.GetRequiredParent();
            return node.GetFirstToken() == token && syntaxFacts.IsConversionExpression(node);
        }
    }
}