File: FindSymbols\Declarations\DeclarationFinder.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;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.LanguageService;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.FindSymbols
{
    internal static partial class DeclarationFinder
    {
        private static async Task AddCompilationSourceDeclarationsWithNormalQueryAsync(
            Project project,
            SearchQuery query,
            SymbolFilter filter,
            ArrayBuilder<ISymbol> list,
            CancellationToken cancellationToken)
        {
            if (!project.SupportsCompilation)
                return;
 
            Contract.ThrowIfTrue(query.Kind == SearchKind.Custom, "Custom queries are not supported in this API");
 
            using (Logger.LogBlock(FunctionId.SymbolFinder_Project_AddDeclarationsAsync, cancellationToken))
            {
                var syntaxFacts = project.GetRequiredLanguageService<ISyntaxFactsService>();
 
                // If this is an exact query, we can speed things up by just calling into the
                // compilation entrypoints that take a string directly.
                //
                // the search is 'exact' if it's either an exact-case-sensitive search,
                // or it's an exact-case-insensitive search and we're in a case-insensitive
                // language.
                var isExactNameSearch = query.Kind == SearchKind.Exact ||
                    (query.Kind == SearchKind.ExactIgnoreCase && !syntaxFacts.IsCaseSensitive);
 
                // Do a quick syntactic check first using our cheaply built indices.  That will help us avoid creating
                // a compilation here if it's not necessary.  In the case of an exact name search we can call a special 
                // overload that quickly uses the direct bloom-filter identifier maps in the index.  If it's nto an 
                // exact name search, then we will run the query's predicate over every DeclaredSymbolInfo stored in
                // the doc.
                var containsSymbol = isExactNameSearch
                    ? await project.ContainsSymbolsWithNameAsync(query.Name!, cancellationToken).ConfigureAwait(false)
                    : await project.ContainsSymbolsWithNameAsync(query.GetPredicate(), filter, cancellationToken).ConfigureAwait(false);
 
                if (!containsSymbol)
                    return;
 
                var compilation = await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false);
 
                var symbols = isExactNameSearch
                    ? compilation.GetSymbolsWithName(query.Name!, filter, cancellationToken)
                    : compilation.GetSymbolsWithName(query.GetPredicate(), filter, cancellationToken);
 
                var symbolsWithName = symbols.ToImmutableArray();
 
                list.AddRange(FilterByCriteria(symbolsWithName, filter));
            }
        }
 
        private static async Task AddMetadataDeclarationsWithNormalQueryAsync(
            Project project,
            AsyncLazy<IAssemblySymbol?> lazyAssembly,
            PortableExecutableReference reference,
            SearchQuery query,
            SymbolFilter filter,
            ArrayBuilder<ISymbol> list,
            CancellationToken cancellationToken)
        {
            // All entrypoints to this function are Find functions that are only searching
            // for specific strings (i.e. they never do a custom search).
            Contract.ThrowIfTrue(query.Kind == SearchKind.Custom, "Custom queries are not supported in this API");
 
            using (Logger.LogBlock(FunctionId.SymbolFinder_Assembly_AddDeclarationsAsync, cancellationToken))
            {
                var info = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync(
                    project.Solution, reference, checksum: null, cancellationToken).ConfigureAwait(false);
 
                Contract.ThrowIfNull(info);
 
                var symbols = await info.FindAsync(query, lazyAssembly, filter, cancellationToken).ConfigureAwait(false);
                list.AddRange(symbols);
            }
        }
 
        internal static ImmutableArray<ISymbol> FilterByCriteria(ImmutableArray<ISymbol> symbols, SymbolFilter criteria)
            => symbols.WhereAsArray(s => MeetCriteria(s, criteria));
 
        private static bool MeetCriteria(ISymbol symbol, SymbolFilter filter)
        {
            if (!symbol.IsImplicitlyDeclared && !symbol.IsAccessor())
            {
                if (IsOn(filter, SymbolFilter.Namespace) && symbol.Kind == SymbolKind.Namespace)
                {
                    return true;
                }
 
                if (IsOn(filter, SymbolFilter.Type) && symbol is ITypeSymbol)
                {
                    return true;
                }
 
                if (IsOn(filter, SymbolFilter.Member) && IsNonTypeMember(symbol))
                {
                    return true;
                }
            }
 
            return false;
        }
 
        private static bool IsNonTypeMember(ISymbol symbol)
        {
            return symbol.Kind is SymbolKind.Method or
                   SymbolKind.Property or
                   SymbolKind.Event or
                   SymbolKind.Field;
        }
 
        private static bool IsOn(SymbolFilter filter, SymbolFilter flag)
            => (filter & flag) == flag;
    }
}