/// <summary>
        /// Attempts to find all the locations to rename.  Will not cross any process boundaries to do this.
        /// </summary>
        public static async Task <HeavyweightRenameLocations> FindLocationsInCurrentProcessAsync(
            ISymbol symbol, Solution solution, SymbolRenameOptions options, CodeCleanupOptionsProvider cleanupOptions, CancellationToken cancellationToken)
        {
            Contract.ThrowIfNull(symbol);
            using (Logger.LogBlock(FunctionId.Rename_AllRenameLocations, cancellationToken))
            {
                symbol = await RenameUtilities.FindDefinitionSymbolAsync(symbol, solution, cancellationToken).ConfigureAwait(false);

                // First, find the direct references just to the symbol being renamed.
                var originalSymbolResult = await AddLocationsReferenceSymbolsAsync(symbol, solution, cancellationToken).ConfigureAwait(false);

                // Next, find references to overloads, if the user has asked to rename those as well.
                var overloadsResult = options.RenameOverloads ? await GetOverloadsAsync(symbol, solution, cancellationToken).ConfigureAwait(false) :
                                      ImmutableArray <SearchResult> .Empty;

                // Finally, include strings/comments if that's what the user wants.
                var(strings, comments) = await ReferenceProcessing.GetRenamableLocationsInStringsAndCommentsAsync(
                    symbol,
                    solution,
                    originalSymbolResult.Locations,
                    options.RenameInStrings,
                    options.RenameInComments,
                    cancellationToken).ConfigureAwait(false);

                var mergedLocations = ImmutableHashSet.CreateBuilder <RenameLocation>();

                using var _1 = ArrayBuilder <ISymbol> .GetInstance(out var mergedReferencedSymbols);

                using var _2 = ArrayBuilder <ReferenceLocation> .GetInstance(out var mergedImplicitLocations);

                var renameMethodGroupReferences = options.RenameOverloads || !RenameUtilities.GetOverloadedSymbols(symbol).Any();
                foreach (var result in overloadsResult.Concat(originalSymbolResult))
                {
                    mergedLocations.AddRange(renameMethodGroupReferences
                        ? result.Locations
                        : result.Locations.Where(x => x.CandidateReason != CandidateReason.MemberGroup));

                    mergedImplicitLocations.AddRange(result.ImplicitLocations);
                    mergedReferencedSymbols.AddRange(result.ReferencedSymbols);
                }

                // Add string and comment locations to the merged hashset
                // after adding in reference symbols. This allows any references
                // in comments to be resolved as proper references rather than
                // comment resolutions. See https://github.com/dotnet/roslyn/issues/54294
                mergedLocations.AddRange(strings.NullToEmpty());
                mergedLocations.AddRange(comments.NullToEmpty());

                return(new HeavyweightRenameLocations(
                           symbol, solution, options, cleanupOptions,
                           mergedLocations.ToImmutable(),
                           mergedImplicitLocations.ToImmutable(),
                           mergedReferencedSymbols.ToImmutable()));
            }
        }
        private static async Task <ImmutableArray <SearchResult> > GetOverloadsAsync(
            ISymbol symbol, Solution solution, CancellationToken cancellationToken)
        {
            using var _ = ArrayBuilder <SearchResult> .GetInstance(out var overloadsResult);

            foreach (var overloadedSymbol in RenameUtilities.GetOverloadedSymbols(symbol))
            {
                overloadsResult.Add(await AddLocationsReferenceSymbolsAsync(overloadedSymbol, solution, cancellationToken).ConfigureAwait(false));
            }

            return(overloadsResult.ToImmutable());
        }
            internal static async Task<(ImmutableArray<RenameLocation>, ImmutableArray<RenameLocation>)> GetRenamableLocationsInStringsAndCommentsAsync(
                ISymbol originalSymbol,
                Solution solution,
                ISet<RenameLocation> renameLocations,
                bool renameInStrings,
                bool renameInComments,
                CancellationToken cancellationToken)
            {
                if (!renameInStrings && !renameInComments)
                    return default;

                var renameText = originalSymbol.Name;

                using var _1 = ArrayBuilder<RenameLocation>.GetInstance(out var stringLocations);
                using var _2 = ArrayBuilder<RenameLocation>.GetInstance(out var commentLocations);

                foreach (var documentsGroupedByLanguage in RenameUtilities.GetDocumentsAffectedByRename(originalSymbol, solution, renameLocations).GroupBy(d => d.Project.Language))
                {
                    var syntaxFactsLanguageService = solution.Workspace.Services.GetLanguageServices(documentsGroupedByLanguage.Key).GetService<ISyntaxFactsService>();

                    if (syntaxFactsLanguageService != null)
                    {
                        foreach (var document in documentsGroupedByLanguage)
                        {
                            if (renameInStrings)
                            {
                                await AddLocationsToRenameInStringsAsync(
                                    document, renameText, syntaxFactsLanguageService,
                                    stringLocations, cancellationToken).ConfigureAwait(false);
                            }

                            if (renameInComments)
                            {
                                await AddLocationsToRenameInCommentsAsync(document, renameText, commentLocations, cancellationToken).ConfigureAwait(false);
                            }
                        }
                    }
                }

                return (renameInStrings ? stringLocations.ToImmutable() : default,
            internal static async Task <Tuple <IEnumerable <RenameLocation>, IEnumerable <RenameLocation> > > GetRenamableLocationsInStringsAndCommentsAsync(
                ISymbol originalSymbol,
                Solution solution,
                ISet <RenameLocation> renameLocations,
                bool renameInStrings,
                bool renameInComments,
                CancellationToken cancellationToken)
            {
                if (!renameInStrings && !renameInComments)
                {
                    return(new Tuple <IEnumerable <RenameLocation>, IEnumerable <RenameLocation> >(null, null));
                }

                var renameText = originalSymbol.Name;
                List <RenameLocation> stringLocations  = renameInStrings ? new List <RenameLocation>() : null;
                List <RenameLocation> commentLocations = renameInComments ? new List <RenameLocation>() : null;

                foreach (var documentsGroupedByLanguage in RenameUtilities.GetDocumentsAffectedByRename(originalSymbol, solution, renameLocations).GroupBy(d => d.Project.Language))
                {
                    var syntaxFactsLanguageService = solution.Workspace.Services.GetLanguageServices(documentsGroupedByLanguage.Key).GetService <ISyntaxFactsService>();
                    foreach (var document in documentsGroupedByLanguage)
                    {
                        if (renameInStrings)
                        {
                            await AddLocationsToRenameInStringsAsync(document, renameText, syntaxFactsLanguageService,
                                                                     stringLocations, cancellationToken).ConfigureAwait(false);
                        }

                        if (renameInComments)
                        {
                            await AddLocationsToRenameInCommentsAsync(document, renameText, commentLocations, cancellationToken).ConfigureAwait(false);
                        }
                    }
                }

                return(new Tuple <IEnumerable <RenameLocation>, IEnumerable <RenameLocation> >(stringLocations, commentLocations));
            }
Example #5
0
        /// <summary>
        /// Given a symbol, finds the symbol that actually defines the name that we're using.
        /// </summary>
        public static async Task <ISymbol> FindDefinitionSymbolAsync(
            ISymbol symbol, Solution solution, CancellationToken cancellationToken)
        {
            Contract.ThrowIfNull(symbol);
            Contract.ThrowIfNull(solution);

            // Make sure we're on the original source definition if we can be
            var foundSymbol = await SymbolFinder.FindSourceDefinitionAsync(
                symbol, solution, cancellationToken).ConfigureAwait(false);

            var bestSymbol = foundSymbol ?? symbol;

            symbol = bestSymbol;

            // If we're renaming a property, it might be a synthesized property for a method
            // backing field.
            if (symbol.Kind == SymbolKind.Parameter)
            {
                if (symbol.ContainingSymbol.Kind == SymbolKind.Method)
                {
                    var containingMethod = (IMethodSymbol)symbol.ContainingSymbol;
                    if (containingMethod.AssociatedSymbol is IPropertySymbol)
                    {
                        var associatedPropertyOrEvent = (IPropertySymbol)containingMethod.AssociatedSymbol;
                        var ordinal = containingMethod.Parameters.IndexOf((IParameterSymbol)symbol);
                        if (ordinal < associatedPropertyOrEvent.Parameters.Length)
                        {
                            return(associatedPropertyOrEvent.Parameters[ordinal]);
                        }
                    }
                }
            }

            // if we are renaming a compiler generated delegate for an event, cascade to the event
            if (symbol.Kind == SymbolKind.NamedType)
            {
                var typeSymbol = (INamedTypeSymbol)symbol;
                if (typeSymbol.IsImplicitlyDeclared && typeSymbol.IsDelegateType() && typeSymbol.AssociatedSymbol != null)
                {
                    return(typeSymbol.AssociatedSymbol);
                }
            }

            // If we are renaming a constructor or destructor, we wish to rename the whole type
            if (symbol.Kind == SymbolKind.Method)
            {
                var methodSymbol = (IMethodSymbol)symbol;
                if (methodSymbol.MethodKind is MethodKind.Constructor or
                    MethodKind.StaticConstructor or
                    MethodKind.Destructor)
                {
                    return(methodSymbol.ContainingType);
                }
            }

            // If we are renaming a backing field for a property, cascade to the property
            if (symbol.Kind == SymbolKind.Field)
            {
                var fieldSymbol = (IFieldSymbol)symbol;
                if (fieldSymbol.IsImplicitlyDeclared &&
                    fieldSymbol.AssociatedSymbol.IsKind(SymbolKind.Property))
                {
                    return(fieldSymbol.AssociatedSymbol);
                }
            }

            // in case this is e.g. an overridden property accessor, we'll treat the property itself as the definition symbol
            var property = await RenameUtilities.TryGetPropertyFromAccessorOrAnOverrideAsync(bestSymbol, solution, cancellationToken).ConfigureAwait(false);

            return(property ?? bestSymbol);
        }