RenameLocations.cs 10.8 KB
Newer Older
J
Jonathon Marolf 已提交
1 2 3
// 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.
P
Pilchie 已提交
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.Rename
{
    /// <summary>
19
    /// Holds the Locations of a symbol that should be renamed, along with the symbol and Solution
P
Pilchie 已提交
20 21
    /// for the set.
    /// </summary>
22
    internal sealed partial class RenameLocations
P
Pilchie 已提交
23 24 25 26 27
    {
        private class SearchResult
        {
            public readonly IEnumerable<ReferenceLocation> ImplicitLocations;
            public readonly ISet<RenameLocation> Locations;
28
            public readonly IEnumerable<SymbolAndProjectId> ReferencedSymbols;
P
Pilchie 已提交
29

30 31 32 33
            public SearchResult(
                ISet<RenameLocation> locations,
                IEnumerable<ReferenceLocation> implicitLocations,
                IEnumerable<SymbolAndProjectId> referencedSymbols)
P
Pilchie 已提交
34 35 36 37 38 39 40 41
            {
                this.Locations = locations;
                this.ImplicitLocations = implicitLocations;
                this.ReferencedSymbols = referencedSymbols;
            }
        }

        // never null fields
42
        private readonly SymbolAndProjectId _symbolAndProjectId;
43 44
        private readonly Solution _solution;
        private readonly SearchResult _mergedResult;
45
        internal OptionSet Options { get; }
P
Pilchie 已提交
46 47

        // possibly null fields
48 49 50 51
        private readonly SearchResult _originalSymbolResult;
        private readonly List<SearchResult> _overloadsResult;
        private readonly IEnumerable<RenameLocation> _stringsResult;
        private readonly IEnumerable<RenameLocation> _commentsResult;
P
Pilchie 已提交
52

53
        internal RenameLocations(
D
dotnet-bot 已提交
54 55 56 57 58
            ISet<RenameLocation> locations,
            SymbolAndProjectId symbolAndProjectId,
            Solution solution,
            IEnumerable<SymbolAndProjectId> referencedSymbols,
            IEnumerable<ReferenceLocation> implicitLocations,
59
            OptionSet options)
P
Pilchie 已提交
60
        {
61
            _symbolAndProjectId = symbolAndProjectId;
62 63
            _solution = solution;
            _mergedResult = new SearchResult(locations, implicitLocations, referencedSymbols);
64
            Options = options;
P
Pilchie 已提交
65 66
        }

67 68 69 70 71 72 73 74
        private RenameLocations(
            SymbolAndProjectId symbolAndProjectId,
            Solution solution,
            OptionSet options,
            SearchResult originalSymbolResult,
            List<SearchResult> overloadsResult,
            IEnumerable<RenameLocation> stringsResult,
            IEnumerable<RenameLocation> commentsResult)
P
Pilchie 已提交
75
        {
76
            _symbolAndProjectId = symbolAndProjectId;
77
            _solution = solution;
78
            Options = options;
79 80 81 82
            _originalSymbolResult = originalSymbolResult;
            _overloadsResult = overloadsResult;
            _stringsResult = stringsResult;
            _commentsResult = commentsResult;
P
Pilchie 已提交
83 84

            var mergedLocations = new HashSet<RenameLocation>();
85
            var mergedReferencedSymbols = new List<SymbolAndProjectId>();
P
Pilchie 已提交
86 87
            var mergedImplicitLocations = new List<ReferenceLocation>();

88
            if (options.GetOption(RenameOptions.RenameInStrings))
P
Pilchie 已提交
89 90 91 92
            {
                mergedLocations.AddRange(stringsResult);
            }

93
            if (options.GetOption(RenameOptions.RenameInComments))
P
Pilchie 已提交
94 95 96 97
            {
                mergedLocations.AddRange(commentsResult);
            }

D
dotnet-bot 已提交
98
            var renameMethodGroupReferences =
99
                options.GetOption(RenameOptions.RenameOverloads) || !GetOverloadedSymbols(symbolAndProjectId).Any();
100
            var overloadsToMerge = (options.GetOption(RenameOptions.RenameOverloads) ? overloadsResult : null) ?? SpecializedCollections.EmptyEnumerable<SearchResult>();
P
Pilchie 已提交
101 102
            foreach (var result in overloadsToMerge.Concat(originalSymbolResult))
            {
J
Jared Parsons 已提交
103
                mergedLocations.AddRange(renameMethodGroupReferences
104
                    ? result.Locations
105
                    : result.Locations.Where(x => x.CandidateReason != CandidateReason.MemberGroup));
106

P
Pilchie 已提交
107 108 109 110
                mergedImplicitLocations.AddRange(result.ImplicitLocations);
                mergedReferencedSymbols.AddRange(result.ReferencedSymbols);
            }

111
            _mergedResult = new SearchResult(mergedLocations, mergedImplicitLocations, mergedReferencedSymbols);
P
Pilchie 已提交
112 113
        }

114
        public ISet<RenameLocation> Locations => _mergedResult.Locations;
115 116
        public SymbolAndProjectId SymbolAndProjectId => _symbolAndProjectId;
        public ISymbol Symbol => _symbolAndProjectId.Symbol;
C
CyrusNajmabadi 已提交
117
        public Solution Solution => _solution;
118
        public IEnumerable<SymbolAndProjectId> ReferencedSymbols => _mergedResult.ReferencedSymbols;
C
CyrusNajmabadi 已提交
119
        public IEnumerable<ReferenceLocation> ImplicitLocations => _mergedResult.ImplicitLocations;
P
Pilchie 已提交
120 121 122 123

        /// <summary>
        /// Find the locations that need to be renamed.
        /// </summary>
124 125
        internal static async Task<RenameLocations> FindAsync(
            SymbolAndProjectId symbolAndProjectId, Solution solution, OptionSet optionSet, CancellationToken cancellationToken)
P
Pilchie 已提交
126
        {
127
            Contract.ThrowIfNull(symbolAndProjectId.Symbol);
H
heejaechang 已提交
128
            using (Logger.LogBlock(FunctionId.Rename_AllRenameLocations, cancellationToken))
P
Pilchie 已提交
129
            {
130 131 132
                symbolAndProjectId = await ReferenceProcessing.FindDefinitionSymbolAsync(symbolAndProjectId, solution, cancellationToken).ConfigureAwait(false);
                var originalSymbolResult = await AddLocationsReferenceSymbolsAsync(symbolAndProjectId, solution, cancellationToken).ConfigureAwait(false);
                var intermediateResult = new RenameLocations(symbolAndProjectId, solution, optionSet, originalSymbolResult, overloadsResult: null, stringsResult: null, commentsResult: null);
P
Pilchie 已提交
133

134
                return await intermediateResult.FindWithUpdatedOptionsAsync(optionSet, cancellationToken).ConfigureAwait(false);
P
Pilchie 已提交
135 136 137
            }
        }

138
        internal async Task<RenameLocations> FindWithUpdatedOptionsAsync(OptionSet optionSet, CancellationToken cancellationToken)
P
Pilchie 已提交
139
        {
140
            Contract.ThrowIfNull(Options, "FindWithUpdatedOptionsAsync can only be called on a result of FindAsync");
H
heejaechang 已提交
141
            using (Logger.LogBlock(FunctionId.Rename_AllRenameLocations, cancellationToken))
P
Pilchie 已提交
142
            {
143
                var overloadsResult = _overloadsResult ?? (optionSet.GetOption(RenameOptions.RenameOverloads)
144
                    ? await GetOverloadsAsync(_symbolAndProjectId, _solution, cancellationToken).ConfigureAwait(false)
145
                    : null);
P
Pilchie 已提交
146 147

                var stringsAndComments = await ReferenceProcessing.GetRenamableLocationsInStringsAndCommentsAsync(
148
                    _symbolAndProjectId.Symbol,
149 150 151 152
                    _solution,
                    _originalSymbolResult.Locations,
                    optionSet.GetOption(RenameOptions.RenameInStrings) && _stringsResult == null,
                    optionSet.GetOption(RenameOptions.RenameInComments) && _commentsResult == null,
P
Pilchie 已提交
153 154
                    cancellationToken).ConfigureAwait(false);

155 156
                return new RenameLocations(
                    _symbolAndProjectId, _solution, optionSet, _originalSymbolResult,
157 158 159
                    _overloadsResult ?? overloadsResult,
                    _stringsResult ?? stringsAndComments.Item1,
                    _commentsResult ?? stringsAndComments.Item2);
P
Pilchie 已提交
160 161 162
            }
        }

163 164
        private static async Task<List<SearchResult>> GetOverloadsAsync(
            SymbolAndProjectId symbolAndProjectId, Solution solution, CancellationToken cancellationToken)
P
Pilchie 已提交
165 166
        {
            var overloadsResult = new List<SearchResult>();
167
            foreach (var overloadedSymbol in GetOverloadedSymbols(symbolAndProjectId))
P
Pilchie 已提交
168 169 170 171 172 173 174
            {
                overloadsResult.Add(await AddLocationsReferenceSymbolsAsync(overloadedSymbol, solution, cancellationToken).ConfigureAwait(false));
            }

            return overloadsResult;
        }

175 176
        internal static IEnumerable<SymbolAndProjectId> GetOverloadedSymbols(
            SymbolAndProjectId symbolAndProjectId)
P
Pilchie 已提交
177
        {
178
            var symbol = symbolAndProjectId.Symbol;
P
Pilchie 已提交
179 180 181 182 183 184 185 186 187
            if (symbol is IMethodSymbol)
            {
                var containingType = symbol.ContainingType;
                if (containingType.Kind == SymbolKind.NamedType)
                {
                    foreach (var member in containingType.GetMembers())
                    {
                        if (string.Equals(member.MetadataName, symbol.MetadataName, StringComparison.Ordinal) && member is IMethodSymbol && !member.Equals(symbol))
                        {
188
                            yield return symbolAndProjectId.WithSymbol(member);
P
Pilchie 已提交
189 190 191 192 193 194 195
                        }
                    }
                }
            }
        }

        private static async Task<SearchResult> AddLocationsReferenceSymbolsAsync(
196
            SymbolAndProjectId symbolAndProjectId,
P
Pilchie 已提交
197 198 199
            Solution solution,
            CancellationToken cancellationToken)
        {
200
            var symbol = symbolAndProjectId.Symbol;
P
Pilchie 已提交
201
            var locations = new HashSet<RenameLocation>();
202 203
            var referenceSymbols = await SymbolFinder.FindRenamableReferencesAsync(
                symbolAndProjectId, solution, cancellationToken).ConfigureAwait(false);
P
Pilchie 已提交
204 205 206 207 208 209 210 211 212 213 214 215

            foreach (var referencedSymbol in referenceSymbols)
            {
                locations.AddAll(
                    await ReferenceProcessing.GetRenamableDefinitionLocationsAsync(referencedSymbol.Definition, symbol, solution, cancellationToken).ConfigureAwait(false));

                locations.AddAll(
                    await referencedSymbol.Locations.SelectManyAsync<ReferenceLocation, RenameLocation>(
                        (l, c) => ReferenceProcessing.GetRenamableReferenceLocationsAsync(referencedSymbol.Definition, symbol, l, solution, c),
                        cancellationToken).ConfigureAwait(false));
            }

216 217
            var implicitLocations = referenceSymbols.SelectMany(refSym => refSym.Locations).Where(loc => loc.IsImplicit).ToList();
            var referencedSymbols = referenceSymbols.Select(r => r.DefinitionAndProjectId).Where(r => !r.Symbol.Equals(symbol)).ToList();
P
Pilchie 已提交
218 219 220

            return new SearchResult(locations, implicitLocations, referencedSymbols);
        }
C
Cyrus Najmabadi 已提交
221 222 223 224 225 226 227

        public RenameLocations Filter(Func<Location, bool> filter)
            => new RenameLocations(
                this.Locations.Where(loc => filter(loc.Location)).ToSet(),
                this.SymbolAndProjectId, this.Solution,
                this.ReferencedSymbols, this.ImplicitLocations.Where(loc => filter(loc.Location)),
                this.Options);
P
Pilchie 已提交
228
    }
S
Sam Harwell 已提交
229
}