File: FindSymbols\FindReferences\Finders\AbstractMemberScopedReferenceFinder.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 abstract class AbstractMemberScopedReferenceFinder<TSymbol> : AbstractReferenceFinder<TSymbol>
        where TSymbol : ISymbol
    {
        protected abstract bool TokensMatch(
            FindReferencesDocumentState state, SyntaxToken token, string name);
 
        protected sealed override bool CanFind(TSymbol symbol)
            => true;
 
        protected sealed override Task<ImmutableArray<Document>> DetermineDocumentsToSearchAsync(
            TSymbol symbol,
            HashSet<string>? globalAliases,
            Project project,
            IImmutableSet<Document>? documents,
            FindReferencesSearchOptions options,
            CancellationToken cancellationToken)
        {
            var location = symbol.Locations.FirstOrDefault();
            if (location == null || !location.IsInSource)
                return SpecializedTasks.EmptyImmutableArray<Document>();
 
            var document = project.GetDocument(location.SourceTree);
            if (document == null)
                return SpecializedTasks.EmptyImmutableArray<Document>();
 
            if (documents != null && !documents.Contains(document))
                return SpecializedTasks.EmptyImmutableArray<Document>();
 
            return Task.FromResult(ImmutableArray.Create(document));
        }
 
        protected sealed override async ValueTask<ImmutableArray<FinderLocation>> FindReferencesInDocumentAsync(
            TSymbol symbol,
            FindReferencesDocumentState state,
            FindReferencesSearchOptions options,
            CancellationToken cancellationToken)
        {
            var container = GetContainer(symbol);
            if (container != null)
                return await FindReferencesInContainerAsync(symbol, container, state, cancellationToken).ConfigureAwait(false);
 
            if (symbol.ContainingType != null && symbol.ContainingType.IsScriptClass)
            {
                var tokens = await FindMatchingIdentifierTokensAsync(state, symbol.Name, cancellationToken).ConfigureAwait(false);
                return await FindReferencesInTokensAsync(symbol, state, tokens, cancellationToken).ConfigureAwait(false);
            }
 
            return ImmutableArray<FinderLocation>.Empty;
        }
 
        private static ISymbol? GetContainer(ISymbol symbol)
        {
            for (var current = symbol; current != null; current = current.ContainingSymbol)
            {
                if (current.DeclaringSyntaxReferences.Length == 0)
                    continue;
 
                if (current is IPropertySymbol)
                    return current;
 
                // If this is an initializer for a property's backing field, then we want to 
                // search for results within the property itself.
                if (current is IFieldSymbol field)
                {
                    return field is { IsImplicitlyDeclared: true, AssociatedSymbol.Kind: SymbolKind.Property }
                        ? field.AssociatedSymbol
                        : field;
                }
 
                // Note: this may hit a containing local-function/lambda.  That's fine as that's still the scope we want
                // to look for this local within.
                if (current is IMethodSymbol)
                    return current;
            }
 
            return null;
        }
 
        private ValueTask<ImmutableArray<FinderLocation>> FindReferencesInContainerAsync(
            TSymbol symbol,
            ISymbol container,
            FindReferencesDocumentState state,
            CancellationToken cancellationToken)
        {
            var service = state.Document.GetRequiredLanguageService<ISymbolDeclarationService>();
            using var _ = ArrayBuilder<SyntaxToken>.GetInstance(out var tokens);
 
            foreach (var declaration in service.GetDeclarations(container))
            {
                var syntax = declaration.GetSyntax(cancellationToken);
                if (syntax.SyntaxTree != state.SyntaxTree)
                    continue;
 
                foreach (var token in syntax.DescendantTokens())
                {
                    if (TokensMatch(state, token, symbol.Name))
                        tokens.Add(token);
                }
            }
 
            return FindReferencesInTokensAsync(symbol, state, tokens.ToImmutable(), cancellationToken);
        }
    }
}