DeclarationFinder_SourceDeclarations.cs 12.2 KB
Newer Older
1 2 3 4 5 6
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

using System;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
7
using Microsoft.CodeAnalysis.PatternMatching;
8
using Microsoft.CodeAnalysis.Remote;
9
using Microsoft.CodeAnalysis.Shared.Extensions;
10 11 12 13 14 15 16 17

namespace Microsoft.CodeAnalysis.FindSymbols
{
    // All the logic for finding source declarations in a given solution/project with some name 
    // is in this file.  

    internal static partial class DeclarationFinder
    {
C
CyrusNajmabadi 已提交
18 19 20 21 22 23
        #region Dispatch Members

        // These are the public entrypoints to finding source declarations.  They will attempt to
        // remove the query to the OOP process, and will fallback to local processing if they can't.

        public static async Task<ImmutableArray<SymbolAndProjectId>> FindSourceDeclarationsWithNormalQueryAsync(
24
            Solution solution, string name, bool ignoreCase, SymbolFilter criteria, CancellationToken cancellationToken)
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
        {
            if (solution == null)
            {
                throw new ArgumentNullException(nameof(solution));
            }

            if (name == null)
            {
                throw new ArgumentNullException(nameof(name));
            }

            if (string.IsNullOrWhiteSpace(name))
            {
                return ImmutableArray<SymbolAndProjectId>.Empty;
            }

41 42 43 44 45 46 47 48 49 50 51 52
            var (succeded, results) = await TryFindSourceDeclarationsWithNormalQueryInRemoteProcessAsync(
                solution, name, ignoreCase, criteria, cancellationToken).ConfigureAwait(false);

            if (succeded)
            {
                return results;
            }

            return await FindSourceDeclarationsWithNormalQueryInCurrentProcessAsync(
                solution, name, ignoreCase, criteria, cancellationToken).ConfigureAwait(false);
        }

C
CyrusNajmabadi 已提交
53
        public static async Task<ImmutableArray<SymbolAndProjectId>> FindSourceDeclarationsWithNormalQueryAsync(
54
            Project project, string name, bool ignoreCase, SymbolFilter criteria, CancellationToken cancellationToken)
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
        {
            if (project == null)
            {
                throw new ArgumentNullException(nameof(project));
            }

            if (name == null)
            {
                throw new ArgumentNullException(nameof(name));
            }

            if (string.IsNullOrWhiteSpace(name))
            {
                return ImmutableArray<SymbolAndProjectId>.Empty;
            }

71 72 73 74 75 76 77 78 79 80 81 82
            var (succeded, results) = await TryFindSourceDeclarationsWithNormalQueryInRemoteProcessAsync(
                project, name, ignoreCase, criteria, cancellationToken).ConfigureAwait(false);

            if (succeded)
            {
                return results;
            }

            return await FindSourceDeclarationsWithNormalQueryInCurrentProcessAsync(
                project, name, ignoreCase, criteria, cancellationToken).ConfigureAwait(false);
        }

83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
        public static async Task<ImmutableArray<SymbolAndProjectId>> FindSourceDeclarationsWithPatternAsync(
            Project project, string pattern, SymbolFilter criteria, CancellationToken cancellationToken)
        {
            if (project == null)
            {
                throw new ArgumentNullException(nameof(project));
            }

            if (pattern == null)
            {
                throw new ArgumentNullException(nameof(pattern));
            }

            var (succeded, results) = await TryFindSourceDeclarationsWithPatternInRemoteProcessAsync(
                project, pattern, criteria, cancellationToken).ConfigureAwait(false);

            if (succeded)
            {
                return results;
            }

            return await FindSourceDeclarationsWithPatternInCurrentProcessAsync(
                project, pattern, criteria, cancellationToken).ConfigureAwait(false);
        }

C
CyrusNajmabadi 已提交
108 109 110 111 112 113 114 115 116
        #endregion

        #region Remote Dispatch

        // These are the members that actually try to send the request to the remote process.

        private static async Task<(bool, ImmutableArray<SymbolAndProjectId>)> TryFindSourceDeclarationsWithNormalQueryInRemoteProcessAsync(
            Solution solution, string name, bool ignoreCase, SymbolFilter criteria, CancellationToken cancellationToken)
        {
117
            using (var session = await SymbolFinder.TryGetRemoteSessionAsync(solution, cancellationToken).ConfigureAwait(false))
C
CyrusNajmabadi 已提交
118
            {
119 120
                if (session != null)
                {
121
                    var result = await session.InvokeAsync<ImmutableArray<SerializableSymbolAndProjectId>>(
122 123
                        nameof(IRemoteSymbolFinder.FindSolutionSourceDeclarationsWithNormalQueryAsync),
                        name, ignoreCase, criteria).ConfigureAwait(false);
C
CyrusNajmabadi 已提交
124

125 126
                    var rehydrated = await RehydrateAsync(
                        solution, result, cancellationToken).ConfigureAwait(false);
C
CyrusNajmabadi 已提交
127

128 129
                    return (true, rehydrated);
                }
C
CyrusNajmabadi 已提交
130 131 132 133 134
            }

            return (false, ImmutableArray<SymbolAndProjectId>.Empty);
        }

135 136 137
        private static async Task<(bool, ImmutableArray<SymbolAndProjectId>)> TryFindSourceDeclarationsWithNormalQueryInRemoteProcessAsync(
            Project project, string name, bool ignoreCase, SymbolFilter criteria, CancellationToken cancellationToken)
        {
138
            using (var session = await SymbolFinder.TryGetRemoteSessionAsync(project.Solution, cancellationToken).ConfigureAwait(false))
139
            {
140 141
                if (session != null)
                {
142
                    var result = await session.InvokeAsync<ImmutableArray<SerializableSymbolAndProjectId>>(
143 144
                        nameof(IRemoteSymbolFinder.FindProjectSourceDeclarationsWithNormalQueryAsync),
                        project.Id, name, ignoreCase, criteria).ConfigureAwait(false);
145

146 147
                    var rehydrated = await RehydrateAsync(
                        project.Solution, result, cancellationToken).ConfigureAwait(false);
148

149 150
                    return (true, rehydrated);
                }
151 152 153 154 155
            }

            return (false, ImmutableArray<SymbolAndProjectId>.Empty);
        }

156 157 158
        private static async Task<(bool, ImmutableArray<SymbolAndProjectId>)> TryFindSourceDeclarationsWithPatternInRemoteProcessAsync(
            Project project, string pattern, SymbolFilter criteria, CancellationToken cancellationToken)
        {
159
            using (var session = await SymbolFinder.TryGetRemoteSessionAsync(project.Solution, cancellationToken).ConfigureAwait(false))
160
            {
161 162
                if (session != null)
                {
163
                    var result = await session.InvokeAsync<ImmutableArray<SerializableSymbolAndProjectId>>(
164 165
                        nameof(IRemoteSymbolFinder.FindProjectSourceDeclarationsWithPatternAsync),
                        project.Id, pattern, criteria).ConfigureAwait(false);
166

167 168
                    var rehydrated = await RehydrateAsync(
                        project.Solution, result, cancellationToken).ConfigureAwait(false);
169

170 171
                    return (true, rehydrated);
                }
172 173 174 175 176
            }

            return (false, ImmutableArray<SymbolAndProjectId>.Empty);
        }

C
CyrusNajmabadi 已提交
177 178 179 180 181 182 183 184 185 186 187
        #endregion

        #region Local processing

        // These are the members that have the core logic that does the actual finding.  They will
        // be called 'in proc' in the remote process if we are able to remote the request.  Or they
        // will be called 'in proc' from within VS if we are not able to remote the request.

        internal static async Task<ImmutableArray<SymbolAndProjectId>> FindSourceDeclarationsWithNormalQueryInCurrentProcessAsync(
            Solution solution, string name, bool ignoreCase, SymbolFilter criteria, CancellationToken cancellationToken)
        {
188
            using (var query = SearchQuery.Create(name, ignoreCase))
C
CyrusNajmabadi 已提交
189
            {
190 191 192 193 194 195 196
                var result = ArrayBuilder<SymbolAndProjectId>.GetInstance();
                foreach (var projectId in solution.ProjectIds)
                {
                    var project = solution.GetProject(projectId);
                    await AddCompilationDeclarationsWithNormalQueryAsync(
                        project, query, criteria, result, cancellationToken).ConfigureAwait(false);
                }
C
CyrusNajmabadi 已提交
197

198 199
                return result.ToImmutableAndFree();
            }
C
CyrusNajmabadi 已提交
200 201
        }

202 203 204
        internal static async Task<ImmutableArray<SymbolAndProjectId>> FindSourceDeclarationsWithNormalQueryInCurrentProcessAsync(
            Project project, string name, bool ignoreCase, SymbolFilter filter, CancellationToken cancellationToken)
        {
205
            var list = ArrayBuilder<SymbolAndProjectId>.GetInstance();
206 207 208 209 210 211 212

            using (var query = SearchQuery.Create(name, ignoreCase))
            {
                await AddCompilationDeclarationsWithNormalQueryAsync(
                    project, query, filter, list, cancellationToken).ConfigureAwait(false);
                return list.ToImmutableAndFree();
            }
213
        }
C
CyrusNajmabadi 已提交
214

215 216 217 218 219 220 221 222 223
        internal static async Task<ImmutableArray<SymbolAndProjectId>> FindSourceDeclarationsWithPatternInCurrentProcessAsync(
            Project project, string pattern, SymbolFilter criteria, CancellationToken cancellationToken)
        {
            // The compiler API only supports a predicate which is given a symbol's name.  Because
            // we only have the name, and nothing else, we need to check it against the last segment
            // of the pattern.  i.e. if the pattern is 'Console.WL' and we are given 'WriteLine', then
            // we don't want to check the whole pattern against it (as it will clearly fail), instead
            // we only want to check the 'WL' portion.  Then, after we get all the candidate symbols
            // we'll check if the full name matches the full pattern.
C
CyrusNajmabadi 已提交
224 225
            var (namePart, containerPart) = PatternMatcher.GetNameAndContainer(pattern);

226
            var dotIndex = pattern.LastIndexOf('.');
227 228 229 230 231
            var isDottedPattern = dotIndex >= 0;

            // If we don't have a dot in the pattern, just make a pattern matcher for the entire
            // pattern they passed in.  Otherwise, make a pattern matcher just for the part after
            // the dot.
C
CyrusNajmabadi 已提交
232 233
            using (var nameMatcher = PatternMatcher.CreatePatternMatcher(namePart, includeMatchedSpans: false))
            using (var query = SearchQuery.CreateCustom(nameMatcher.Matches))
234
            {
235 236 237
                var symbolAndProjectIds = await SymbolFinder.FindSourceDeclarationsWithCustomQueryAsync(
                    project, query, criteria, cancellationToken).ConfigureAwait(false);

238 239
                if (symbolAndProjectIds.Length == 0 ||
                    !isDottedPattern)
240
                {
241 242
                    // If it wasn't a dotted pattern, or we didn't get anything back, then we're done.
                    // We can just return whatever set of results we got so far.
243 244 245 246 247
                    return symbolAndProjectIds;
                }

                // Ok, we had a dotted pattern.  Have to see if the symbol's container matches the 
                // pattern as well.
248
                using (var containerPatternMatcher = PatternMatcher.CreateDotSeparatedContainerMatcher(containerPart))
249 250 251 252
                {
                    return symbolAndProjectIds.WhereAsArray(t =>
                        containerPatternMatcher.Matches(GetContainer(t.Symbol)));
                }
253
            }
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
        }

        private static string GetContainer(ISymbol symbol)
        {
            var container = symbol.ContainingSymbol;
            if (container == null)
            {
                return null;
            }

            return container.ToDisplayString(DottedNameFormat);
        }

        private static readonly SymbolDisplayFormat DottedNameFormat =
            new SymbolDisplayFormat(
                globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
                typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
                delegateStyle: SymbolDisplayDelegateStyle.NameOnly,
                extensionMethodStyle: SymbolDisplayExtensionMethodStyle.StaticMethod,
                propertyStyle: SymbolDisplayPropertyStyle.NameOnly);

C
CyrusNajmabadi 已提交
275
        #endregion
276 277
    }
}