Пример #1
0
        private static async Task AddImplicitConflictsAsync(
            ISymbol renamedSymbol,
            ISymbol originalSymbol,
            IEnumerable <ReferenceLocation> implicitReferenceLocations,
            SemanticModel semanticModel,
            Location originalDeclarationLocation,
            int newDeclarationLocationStartingPosition,
            ConflictResolution conflictResolution,
            CancellationToken cancellationToken)
        {
            {
                var renameRewriterService  = conflictResolution.NewSolution.Workspace.Services.GetLanguageServices(renamedSymbol.Language).GetService <IRenameRewriterLanguageService>();
                var implicitUsageConflicts = renameRewriterService.ComputePossibleImplicitUsageConflicts(renamedSymbol, semanticModel, originalDeclarationLocation, newDeclarationLocationStartingPosition, cancellationToken);
                foreach (var implicitUsageConflict in implicitUsageConflicts)
                {
                    conflictResolution.AddOrReplaceRelatedLocation(new RelatedLocation(implicitUsageConflict.SourceSpan, conflictResolution.OldSolution.GetDocument(implicitUsageConflict.SourceTree).Id, RelatedLocationType.UnresolvableConflict));
                }
            }

            if (implicitReferenceLocations.IsEmpty())
            {
                return;
            }

            foreach (var implicitReferenceLocationsPerLanguage in implicitReferenceLocations.GroupBy(loc => loc.Document.Project.Language))
            {
                // the location of the implicit reference defines the language rules to check.
                // E.g. foreach in C# using a MoveNext in VB that is renamed to MOVENEXT (within VB)
                var renameRewriterService = implicitReferenceLocationsPerLanguage.First().Document.Project.LanguageServices.GetService <IRenameRewriterLanguageService>();
                var implicitConflicts     = await renameRewriterService.ComputeImplicitReferenceConflictsAsync(
                    originalSymbol,
                    renamedSymbol,
                    implicitReferenceLocationsPerLanguage,
                    cancellationToken).ConfigureAwait(false);

                foreach (var implicitConflict in implicitConflicts)
                {
                    conflictResolution.AddRelatedLocation(new RelatedLocation(implicitConflict.SourceSpan, conflictResolution.OldSolution.GetDocument(implicitConflict.SourceTree).Id, RelatedLocationType.UnresolvableConflict));
                }
            }
        }
Пример #2
0
        /// <summary>
        /// Computes an adds conflicts relating to declarations, which are independent of
        /// location-based checks. Examples of these types of conflicts include renaming a member to
        /// the same name as another member of a type: binding doesn't change (at least from the
        /// perspective of find all references), but we still need to track it.
        /// </summary>
        internal static async Task AddDeclarationConflictsAsync(
            ISymbol renamedSymbol,
            ISymbol renameSymbol,
            IEnumerable <ISymbol> referencedSymbols,
            ConflictResolution conflictResolution,
            IDictionary <Location, Location> reverseMappedLocations,
            CancellationToken cancellationToken)
        {
            if (renamedSymbol.ContainingSymbol is INamedTypeSymbol)
            {
                var otherThingsNamedTheSame = renamedSymbol.ContainingType.GetMembers(renamedSymbol.Name)
                                              .Where(s => !s.Equals(renamedSymbol) && string.Equals(s.MetadataName, renamedSymbol.MetadataName, StringComparison.Ordinal) &&
                                                     (s.Kind != SymbolKind.Method || renamedSymbol.Kind != SymbolKind.Method)).ToList();

                AddConflictingSymbolLocations(otherThingsNamedTheSame, conflictResolution, reverseMappedLocations);
            }

            // Some types of symbols (namespaces, cref stuff, etc) might not have ContainingAssemblies
            if (renamedSymbol.ContainingAssembly != null)
            {
                var project = conflictResolution.NewSolution.GetProject(renamedSymbol.ContainingAssembly, cancellationToken);

                // There also might be language specific rules we need to include
                var languageRenameService = project.LanguageServices.GetService <IRenameRewriterLanguageService>();
                var languageConflicts     = await languageRenameService.ComputeDeclarationConflictsAsync(
                    conflictResolution.ReplacementText,
                    renamedSymbol,
                    renameSymbol,
                    referencedSymbols,
                    conflictResolution.OldSolution,
                    conflictResolution.NewSolution,
                    reverseMappedLocations,
                    cancellationToken).ConfigureAwait(false);

                foreach (var languageConflict in languageConflicts)
                {
                    conflictResolution.AddOrReplaceRelatedLocation(new RelatedLocation(languageConflict.SourceSpan, conflictResolution.OldSolution.GetDocument(languageConflict.SourceTree).Id, RelatedLocationType.UnresolvableConflict));
                }
            }
        }
Пример #3
0
 private static void AddConflictingSymbolLocations(IEnumerable <ISymbol> conflictingSymbols, ConflictResolution conflictResolution, IDictionary <Location, Location> reverseMappedLocations)
 {
     foreach (var newSymbol in conflictingSymbols)
     {
         foreach (var newLocation in newSymbol.Locations)
         {
             if (newLocation.IsInSource)
             {
                 if (reverseMappedLocations.TryGetValue(newLocation, out var oldLocation))
                 {
                     conflictResolution.AddOrReplaceRelatedLocation(new RelatedLocation(oldLocation.SourceSpan, conflictResolution.OldSolution.GetDocument(oldLocation.SourceTree).Id, RelatedLocationType.UnresolvableConflict));
                 }
             }
         }
     }
 }
Пример #4
0
        /// <summary>
        /// Computes an adds conflicts relating to declarations, which are independent of
        /// location-based checks. Examples of these types of conflicts include renaming a member to
        /// the same name as another member of a type: binding doesn't change (at least from the
        /// perspective of find all references), but we still need to track it.
        /// </summary>
        internal static async Task AddDeclarationConflictsAsync(
            ISymbol renamedSymbol,
            ISymbol renameSymbol,
            IEnumerable <SymbolAndProjectId> referencedSymbols,
            ConflictResolution conflictResolution,
            IDictionary <Location, Location> reverseMappedLocations,
            CancellationToken cancellationToken)
        {
            try
            {
                var project = conflictResolution.NewSolution.GetProject(renamedSymbol.ContainingAssembly, cancellationToken);

                if (renamedSymbol.ContainingSymbol.IsKind(SymbolKind.NamedType))
                {
                    var otherThingsNamedTheSame = renamedSymbol.ContainingType.GetMembers(renamedSymbol.Name)
                                                  .Where(s => !s.Equals(renamedSymbol) &&
                                                         string.Equals(s.MetadataName, renamedSymbol.MetadataName, StringComparison.Ordinal));

                    IEnumerable <ISymbol> otherThingsNamedTheSameExcludeMethodAndParameterizedProperty;

                    // Possibly overloaded symbols are excluded here and handled elsewhere
                    var semanticFactsService = project.LanguageServices.GetService <ISemanticFactsService>();
                    if (semanticFactsService.SupportsParameterizedProperties)
                    {
                        otherThingsNamedTheSameExcludeMethodAndParameterizedProperty = otherThingsNamedTheSame
                                                                                       .Where(s => !s.MatchesKind(SymbolKind.Method, SymbolKind.Property) ||
                                                                                              !renamedSymbol.MatchesKind(SymbolKind.Method, SymbolKind.Property));
                    }
                    else
                    {
                        otherThingsNamedTheSameExcludeMethodAndParameterizedProperty = otherThingsNamedTheSame
                                                                                       .Where(s => s.Kind != SymbolKind.Method || renamedSymbol.Kind != SymbolKind.Method);
                    }

                    AddConflictingSymbolLocations(otherThingsNamedTheSameExcludeMethodAndParameterizedProperty, conflictResolution, reverseMappedLocations);
                }


                if (renamedSymbol.IsKind(SymbolKind.Namespace) && renamedSymbol.ContainingSymbol.IsKind(SymbolKind.Namespace))
                {
                    var otherThingsNamedTheSame = ((INamespaceSymbol)renamedSymbol.ContainingSymbol).GetMembers(renamedSymbol.Name)
                                                  .Where(s => !s.Equals(renamedSymbol) &&
                                                         !s.IsKind(SymbolKind.Namespace) &&
                                                         string.Equals(s.MetadataName, renamedSymbol.MetadataName, StringComparison.Ordinal));

                    AddConflictingSymbolLocations(otherThingsNamedTheSame, conflictResolution, reverseMappedLocations);
                }

                if (renamedSymbol.IsKind(SymbolKind.NamedType) && renamedSymbol.ContainingSymbol is INamespaceOrTypeSymbol)
                {
                    var otherThingsNamedTheSame = ((INamespaceOrTypeSymbol)renamedSymbol.ContainingSymbol).GetMembers(renamedSymbol.Name)
                                                  .Where(s => !s.Equals(renamedSymbol) &&
                                                         string.Equals(s.MetadataName, renamedSymbol.MetadataName, StringComparison.Ordinal));

                    var conflictingSymbolLocations = otherThingsNamedTheSame.Where(s => !s.IsKind(SymbolKind.Namespace));
                    if (otherThingsNamedTheSame.Any(s => s.IsKind(SymbolKind.Namespace)))
                    {
                        conflictingSymbolLocations = conflictingSymbolLocations.Concat(renamedSymbol);
                    }

                    AddConflictingSymbolLocations(conflictingSymbolLocations, conflictResolution, reverseMappedLocations);
                }

                // Some types of symbols (namespaces, cref stuff, etc) might not have ContainingAssemblies
                if (renamedSymbol.ContainingAssembly != null)
                {
                    // There also might be language specific rules we need to include
                    var languageRenameService = project.LanguageServices.GetService <IRenameRewriterLanguageService>();
                    var languageConflicts     = await languageRenameService.ComputeDeclarationConflictsAsync(
                        conflictResolution.ReplacementText,
                        renamedSymbol,
                        renameSymbol,
                        referencedSymbols,
                        conflictResolution.OldSolution,
                        conflictResolution.NewSolution,
                        reverseMappedLocations,
                        cancellationToken).ConfigureAwait(false);

                    foreach (var languageConflict in languageConflicts)
                    {
                        conflictResolution.AddOrReplaceRelatedLocation(new RelatedLocation(languageConflict.SourceSpan, conflictResolution.OldSolution.GetDocument(languageConflict.SourceTree).Id, RelatedLocationType.UnresolvableConflict));
                    }
                }
            }
            catch (Exception e) when(FatalError.ReportUnlessCanceled(e))
            {
                // A NullReferenceException is happening in this method, but the dumps do not
                // contain information about this stack frame because this method is async and
                // therefore the exception filter in IdentifyConflictsAsync is insufficient.
                // See https://devdiv.visualstudio.com/DevDiv/_workitems?_a=edit&id=378642

                throw ExceptionUtilities.Unreachable;
            }
        }
Пример #5
0
 private static bool IsRenameValid(ConflictResolution conflictResolution, ISymbol renamedSymbol)
 {
     // if we rename an identifier and it now binds to a symbol from metadata this should be treated as
     // an invalid rename.
     return(conflictResolution.ReplacementTextValid && renamedSymbol != null && renamedSymbol.Locations.Any(loc => loc.IsInSource));
 }
Пример #6
0
            private async Task DebugVerifyNoErrorsAsync(ConflictResolution conflictResolution, IEnumerable<DocumentId> documents)
            {
                var documentIdErrorStateLookup = new Dictionary<DocumentId, bool>();

                // we only check for the documentIds we add annotations to, which is a subset of the ones we're going 
                // to change the syntax in.
                foreach (var documentId in documents)
                {
                    // remember if there were issues in the document prior to renaming it.
                    var originalDoc = conflictResolution.OldSolution.GetDocument(documentId);
                    documentIdErrorStateLookup.Add(documentId, await originalDoc.HasAnyErrorsAsync(_cancellationToken).ConfigureAwait(false));
                }

                // We want to ignore few error message introduced by rename because the user is wantedly doing it.
                var ignoreErrorCodes = new List<string>();
                ignoreErrorCodes.Add("BC30420"); // BC30420 - Sub Main missing in VB Project
                ignoreErrorCodes.Add("CS5001"); // CS5001 - Missing Main in C# Project

                // only check if rename thinks it was successful
                if (conflictResolution.ReplacementTextValid && conflictResolution.RelatedLocations.All(loc => (loc.Type & RelatedLocationType.UnresolvableConflict) == 0))
                {
                    foreach (var documentId in documents)
                    {
                        // only check documents that had no errors before rename (we might have 
                        // fixed them because of rename).  Also, don't bother checking if a custom
                        // callback was provided.  The caller might be ok with a rename that introduces
                        // errors.
                        if (!documentIdErrorStateLookup[documentId] && _hasConflictCallback == null)
                        {
                            await conflictResolution.NewSolution.GetDocument(documentId).VerifyNoErrorsAsync("Rename introduced errors in error-free code", _cancellationToken, ignoreErrorCodes).ConfigureAwait(false);
                        }
                    }
                }
            }
Пример #7
0
            /// <summary>
            /// Find conflicts in the new solution
            /// </summary>
            private async Task <bool> IdentifyConflictsAsync(
                HashSet <DocumentId> documentIdsForConflictResolution,
                IEnumerable <DocumentId> allDocumentIdsInProject,
                ProjectId projectId,
                ConflictResolution conflictResolution)
            {
                try
                {
                    _documentOfRenameSymbolHasBeenRenamed |= documentIdsForConflictResolution.Contains(_documentIdOfRenameSymbolDeclaration);

                    // Get the renamed symbol in complexified new solution
                    var renamedSymbolInNewSolution = await GetRenamedSymbolInCurrentSolutionAsync(conflictResolution).ConfigureAwait(false);

                    // if the text replacement is invalid, we just did a simple token replacement.
                    // Therefore we don't need more mapping information and can skip the rest of
                    // the loop body.
                    if (!IsRenameValid(conflictResolution, renamedSymbolInNewSolution))
                    {
                        foreach (var documentId in documentIdsForConflictResolution)
                        {
                            var newDocument = conflictResolution.NewSolution.GetDocument(documentId);
                            var syntaxRoot  = await newDocument.GetSyntaxRootAsync(_cancellationToken).ConfigureAwait(false);

                            var nodesOrTokensWithConflictCheckAnnotations = GetNodesOrTokensToCheckForConflicts(documentId, syntaxRoot);
                            foreach (var nodeOrToken in nodesOrTokensWithConflictCheckAnnotations)
                            {
                                if (nodeOrToken.annotation.IsRenameLocation)
                                {
                                    conflictResolution.AddRelatedLocation(new RelatedLocation(
                                                                              nodeOrToken.annotation.OriginalSpan, documentId, RelatedLocationType.UnresolvedConflict));
                                }
                            }
                        }

                        return(false);
                    }

                    var reverseMappedLocations = new Dictionary <Location, Location>();

                    foreach (var documentId in documentIdsForConflictResolution)
                    {
                        var newDocument = conflictResolution.NewSolution.GetDocument(documentId);
                        var syntaxRoot  = await newDocument.GetSyntaxRootAsync(_cancellationToken).ConfigureAwait(false);

                        var baseDocument   = conflictResolution.OldSolution.GetDocument(documentId);
                        var baseSyntaxTree = await baseDocument.GetSyntaxTreeAsync(_cancellationToken).ConfigureAwait(false);

                        var baseRoot = await baseDocument.GetSyntaxRootAsync(_cancellationToken).ConfigureAwait(false);

                        SemanticModel newDocumentSemanticModel = null;
                        var           syntaxFactsService       = newDocument.Project.LanguageServices.GetService <ISyntaxFactsService>();

                        // Get all tokens that need conflict check
                        var nodesOrTokensWithConflictCheckAnnotations = GetNodesOrTokensToCheckForConflicts(documentId, syntaxRoot);

                        var complexifiedLocationSpanForThisDocument =
                            _conflictLocations
                            .Where(t => t.DocumentId == documentId)
                            .Select(t => t.OriginalIdentifierSpan).ToSet();

                        foreach (var nodeAndAnnotation in nodesOrTokensWithConflictCheckAnnotations)
                        {
                            var tokenOrNode        = nodeAndAnnotation.syntax;
                            var conflictAnnotation = nodeAndAnnotation.annotation;
                            reverseMappedLocations[tokenOrNode.GetLocation()] = baseSyntaxTree.GetLocation(conflictAnnotation.OriginalSpan);
                            var originalLocation = conflictAnnotation.OriginalSpan;
                            IEnumerable <ISymbol> newReferencedSymbols = null;

                            var hasConflict = _renameAnnotations.HasAnnotation(tokenOrNode, RenameInvalidIdentifierAnnotation.Instance);
                            if (!hasConflict)
                            {
                                newDocumentSemanticModel ??= await newDocument.GetSemanticModelAsync(_cancellationToken).ConfigureAwait(false);

                                newReferencedSymbols = GetSymbolsInNewSolution(newDocument, newDocumentSemanticModel, conflictAnnotation, tokenOrNode);

                                // The semantic correctness, after rename, for each token of interest in the
                                // rename context is performed by getting the symbol pointed by each token
                                // and obtain the Symbol's First Ordered Location's  Span-Start and check to
                                // see if it is the same as before from the base solution. During rename,
                                // the spans would have been modified and so we need to adjust the old position
                                // to the new position for which we use the renameSpanTracker, which was tracking
                                // & mapping the old span -> new span during rename
                                hasConflict = _hasConflictCallback?.Invoke(newReferencedSymbols) ??
                                              await CheckForConflictAsync(conflictResolution, renamedSymbolInNewSolution, newDocument, conflictAnnotation, newReferencedSymbols).ConfigureAwait(false);
                            }

                            if (!hasConflict && !conflictAnnotation.IsInvocationExpression)
                            {
                                hasConflict = LocalVariableConflictPerLanguage((SyntaxToken)tokenOrNode, newDocument, newReferencedSymbols);
                            }

                            if (!hasConflict)
                            {
                                if (conflictAnnotation.IsRenameLocation)
                                {
                                    conflictResolution.AddRelatedLocation(
                                        new RelatedLocation(originalLocation,
                                                            documentId,
                                                            complexifiedLocationSpanForThisDocument.Contains(originalLocation) ? RelatedLocationType.ResolvedReferenceConflict : RelatedLocationType.NoConflict,
                                                            isReference: true));
                                }
                                else
                                {
                                    // if a complexified location was not a reference location, then it was a resolved conflict of a non reference location
                                    if (!conflictAnnotation.IsOriginalTextLocation && complexifiedLocationSpanForThisDocument.Contains(originalLocation))
                                    {
                                        conflictResolution.AddRelatedLocation(
                                            new RelatedLocation(originalLocation,
                                                                documentId,
                                                                RelatedLocationType.ResolvedNonReferenceConflict,
                                                                isReference: false));
                                    }
                                }
                            }
                            else
                            {
                                var baseToken          = baseRoot.FindToken(conflictAnnotation.OriginalSpan.Start, true);
                                var complexifiedTarget = GetExpansionTargetForLocationPerLanguage(baseToken, baseDocument);
                                conflictResolution.AddRelatedLocation(new RelatedLocation(
                                                                          originalLocation,
                                                                          documentId,
                                                                          complexifiedTarget != null ? RelatedLocationType.PossiblyResolvableConflict : RelatedLocationType.UnresolvableConflict,
                                                                          isReference: conflictAnnotation.IsRenameLocation,
                                                                          complexifiedTargetSpan: complexifiedTarget != null ? complexifiedTarget.Span : default));
                            }
                        }
                    }

                    // there are more conflicts that cannot be identified by checking if the tokens still reference the same
                    // symbol. These conflicts are mostly language specific. A good example is a member with the same name
                    // as the parent (yes I know, this is a simplification).
                    if (_documentIdOfRenameSymbolDeclaration.ProjectId == projectId)
                    {
                        // Calculating declaration conflicts may require location mapping in documents
                        // that were not otherwise being processed in the current rename phase, so add
                        // the annotated spans in these documents to reverseMappedLocations.
                        foreach (var unprocessedDocumentIdWithPotentialDeclarationConflicts in allDocumentIdsInProject.Where(d => !documentIdsForConflictResolution.Contains(d)))
                        {
                            var newDocument = conflictResolution.NewSolution.GetDocument(unprocessedDocumentIdWithPotentialDeclarationConflicts);
                            var syntaxRoot  = await newDocument.GetSyntaxRootAsync(_cancellationToken).ConfigureAwait(false);

                            var baseDocument   = conflictResolution.OldSolution.GetDocument(unprocessedDocumentIdWithPotentialDeclarationConflicts);
                            var baseSyntaxTree = await baseDocument.GetSyntaxTreeAsync(_cancellationToken).ConfigureAwait(false);

                            var nodesOrTokensWithConflictCheckAnnotations = GetNodesOrTokensToCheckForConflicts(unprocessedDocumentIdWithPotentialDeclarationConflicts, syntaxRoot);
                            foreach (var nodeAndAnnotation in nodesOrTokensWithConflictCheckAnnotations)
                            {
                                var tokenOrNode        = nodeAndAnnotation.syntax;
                                var conflictAnnotation = nodeAndAnnotation.annotation;
                                reverseMappedLocations[tokenOrNode.GetLocation()] = baseSyntaxTree.GetLocation(conflictAnnotation.OriginalSpan);
                            }
                        }

                        var referencedSymbols = _renameLocationSet.ReferencedSymbols;
                        var renameSymbol      = _renameLocationSet.Symbol;
                        await AddDeclarationConflictsAsync(
                            renamedSymbolInNewSolution, renameSymbol, referencedSymbols, conflictResolution, reverseMappedLocations, _cancellationToken).ConfigureAwait(false);
                    }

                    return(conflictResolution.RelatedLocations.Any(r => r.Type == RelatedLocationType.PossiblyResolvableConflict));
                }
                catch (Exception e) when(FatalError.ReportUnlessCanceled(e))
                {
                    throw ExceptionUtilities.Unreachable;
                }
            }
Пример #8
0
            // The method which performs rename, resolves the conflict locations and returns the result of the rename operation
            public async Task<ConflictResolution> ResolveConflictsAsync()
            {
                try
                {
                    await FindDocumentsAndPossibleNameConflicts().ConfigureAwait(false);
                    var baseSolution = _renameLocationSet.Solution;

                    // Process rename one project at a time to improve caching and reduce syntax tree serialization.
                    var documentsGroupedByTopologicallySortedProjectId = _documentsIdsToBeCheckedForConflict
                        .GroupBy(d => d.ProjectId)
                        .OrderBy(g => _topologicallySortedProjects.IndexOf(g.Key));

                    _replacementTextValid = IsIdentifierValid_Worker(baseSolution, _replacementText, documentsGroupedByTopologicallySortedProjectId.Select(g => g.Key), _cancellationToken);
                    var renamedSpansTracker = new RenamedSpansTracker();
                    var conflictResolution = new ConflictResolution(baseSolution, renamedSpansTracker, _replacementText, _replacementTextValid);

                    foreach (var documentsByProject in documentsGroupedByTopologicallySortedProjectId)
                    {
                        var documentIdsThatGetsAnnotatedAndRenamed = new HashSet<DocumentId>(documentsByProject);
                        using (baseSolution.Services.CacheService?.EnableCaching(documentsByProject.Key))
                        {
                            // Rename is going to be in 4 phases.
                            // 1st phase - Does a simple token replacement
                            // If the 1st phase results in conflict then we perform then:
                            //      2nd phase is to expand and simplify only the reference locations with conflicts
                            //      3rd phase is to expand and simplify all the conflict locations (both reference and non-reference)
                            // If there are unresolved Conflicts after the 3rd phase then in 4th phase, 
                            //      We complexify and resolve locations that were resolvable and for the other locations we perform the normal token replacement like the first the phase.
                            for (int phase = 0; phase < 4; phase++)
                            {
                                // Step 1:
                                // The rename process and annotation for the bookkeeping is performed in one-step
                                // The Process in short is,
                                // 1. If renaming a token which is no conflict then replace the token and make a map of the oldspan to the newspan
                                // 2. If we encounter a node that has to be expanded( because there was a conflict in previous phase), we expand it.
                                //    If the node happens to contain a token that needs to be renamed then we annotate it and rename it after expansion else just expand and proceed
                                // 3. Through the whole process we maintain a map of the oldspan to newspan. In case of expansion & rename, we map the expanded node and the renamed token
                                conflictResolution.UpdateCurrentSolution(await AnnotateAndRename_WorkerAsync(
                                    baseSolution,
                                    conflictResolution.NewSolution,
                                    documentIdsThatGetsAnnotatedAndRenamed,
                                    _renameLocationSet.Locations,
                                    renamedSpansTracker,
                                    _replacementTextValid).ConfigureAwait(false));

                                // Step 2: Check for conflicts in the renamed solution
                                bool foundResolvableConflicts = await IdentifyConflictsAsync(
                                    documentIdsForConflictResolution: documentIdsThatGetsAnnotatedAndRenamed,
                                    allDocumentIdsInProject: documentsByProject,
                                    projectId: documentsByProject.Key,
                                    conflictResolution: conflictResolution).ConfigureAwait(false);

                                if (!foundResolvableConflicts || phase == 3)
                                {
                                    break;
                                }

                                if (phase == 0)
                                {
                                    _conflictLocations = conflictResolution.RelatedLocations
                                        .Where(loc => (documentIdsThatGetsAnnotatedAndRenamed.Contains(loc.DocumentId) && loc.Type == RelatedLocationType.PossiblyResolvableConflict && loc.IsReference))
                                        .Select(loc => new ConflictLocationInfo(loc))
                                        .ToSet();

                                    // If there were no conflicting locations in references, then the first conflict phase has to be skipped.
                                    if (_conflictLocations.Count == 0)
                                    {
                                        phase++;
                                    }
                                }

                                if (phase == 1)
                                {
                                    _conflictLocations = _conflictLocations.Concat(conflictResolution.RelatedLocations
                                        .Where(loc => documentIdsThatGetsAnnotatedAndRenamed.Contains(loc.DocumentId) && loc.Type == RelatedLocationType.PossiblyResolvableConflict)
                                        .Select(loc => new ConflictLocationInfo(loc)))
                                        .ToSet();
                                }

                                // Set the documents with conflicts that need to be processed in the next phase.
                                // Note that we need to get the conflictLocations here since we're going to remove some locations below if phase == 2
                                documentIdsThatGetsAnnotatedAndRenamed = new HashSet<DocumentId>(_conflictLocations.Select(l => l.DocumentId));

                                if (phase == 2)
                                {
                                    // After phase 2, if there are still conflicts then remove the conflict locations from being expanded
                                    var unresolvedLocations = conflictResolution.RelatedLocations
                                        .Where(l => (l.Type & RelatedLocationType.UnresolvedConflict) != 0)
                                        .Select(l => Tuple.Create(l.ComplexifiedTargetSpan, l.DocumentId)).Distinct();

                                    _conflictLocations = _conflictLocations.Where(l => !unresolvedLocations.Any(c => c.Item2 == l.DocumentId && c.Item1.Contains(l.OriginalIdentifierSpan))).ToSet();
                                }

                                // Clean up side effects from rename before entering the next phase
                                conflictResolution.ClearDocuments(documentIdsThatGetsAnnotatedAndRenamed);
                            }

                            // Step 3: Simplify the project
                            conflictResolution.UpdateCurrentSolution(await renamedSpansTracker.SimplifyAsync(conflictResolution.NewSolution, documentsByProject, _replacementTextValid, _renameAnnotations, _cancellationToken).ConfigureAwait(false));
                            conflictResolution.RemoveAllRenameAnnotations(documentsByProject, _renameAnnotations, _cancellationToken);
                        }
                    }

                    // This rename could break implicit references of this symbol (e.g. rename MoveNext on a collection like type in a 
                    // foreach/for each statement
                    ISymbol renamedSymbolInNewSolution = await GetRenamedSymbolInCurrentSolutionAsync(conflictResolution).ConfigureAwait(false);

                    if (IsRenameValid(conflictResolution, renamedSymbolInNewSolution))
                    {
                        AddImplicitConflicts(
                            renamedSymbolInNewSolution,
                            _renameLocationSet.Symbol,
                            _renameLocationSet.ImplicitLocations,
                            await conflictResolution.NewSolution.GetDocument(_documentIdOfRenameSymbolDeclaration).GetSemanticModelAsync(_cancellationToken).ConfigureAwait(false),
                            _renameSymbolDeclarationLocation,
                            renamedSpansTracker.GetAdjustedPosition(_renameSymbolDeclarationLocation.SourceSpan.Start, _documentIdOfRenameSymbolDeclaration),
                            conflictResolution,
                            _cancellationToken);
                    }

                    foreach (var relatedLocation in conflictResolution.RelatedLocations)
                    {
                        if (relatedLocation.Type == RelatedLocationType.PossiblyResolvableConflict)
                        {
                            relatedLocation.Type = RelatedLocationType.UnresolvedConflict;
                        }
                    }
#if DEBUG
                    DebugVerifyNoErrors(conflictResolution, _documentsIdsToBeCheckedForConflict);
#endif
                    return conflictResolution;
                }
                catch (Exception e) when (FatalError.ReportUnlessCanceled(e))
                {
                    throw ExceptionUtilities.Unreachable;
                }
            }
Пример #9
0
            private async Task<bool> CheckForConflictAsync(
                ConflictResolution conflictResolution,
                ISymbol renamedSymbolInNewSolution,
                Document newDocument,
                RenameActionAnnotation conflictAnnotation,
                IEnumerable<ISymbol> newReferencedSymbols)
            {
                try
                {
                    bool hasConflict;
                    var solution = conflictResolution.NewSolution;

                    if (conflictAnnotation.IsNamespaceDeclarationReference)
                    {
                        hasConflict = false;
                    }
                    else if (conflictAnnotation.IsMemberGroupReference)
                    {
                        if (!conflictAnnotation.RenameDeclarationLocationReferences.Any())
                        {
                            hasConflict = false;
                        }
                        else
                        {
                            // Ensure newReferencedSymbols contains at least one of the original referenced
                            // symbols, and allow any new symbols to be added to the set of references.

                            hasConflict = true;

                            var newLocationTasks = newReferencedSymbols.Select(async symbol => await GetSymbolLocationAsync(solution, symbol, _cancellationToken).ConfigureAwait(false));
                            var newLocations = (await Task.WhenAll(newLocationTasks).ConfigureAwait(false)).Where(loc => loc != null && loc.IsInSource);
                            foreach (var originalReference in conflictAnnotation.RenameDeclarationLocationReferences.Where(loc => loc.IsSourceLocation))
                            {
                                var adjustedStartPosition = conflictResolution.GetAdjustedTokenStartingPosition(originalReference.TextSpan.Start, originalReference.DocumentId);
                                if (newLocations.Any(loc => loc.SourceSpan.Start == adjustedStartPosition))
                                {
                                    hasConflict = false;
                                    break;
                                }
                            }
                        }
                    }
                    else if (!conflictAnnotation.IsRenameLocation && conflictAnnotation.IsOriginalTextLocation && conflictAnnotation.RenameDeclarationLocationReferences.Length > 1 && newReferencedSymbols.Count() == 1)
                    {
                        // an ambiguous situation was resolved through rename in non reference locations
                        hasConflict = false;
                    }
                    else if (newReferencedSymbols.Count() != conflictAnnotation.RenameDeclarationLocationReferences.Length)
                    {
                        // Don't show conflicts for errors in the old solution that now bind in the new solution.
                        if (newReferencedSymbols.Count() != 0 && conflictAnnotation.RenameDeclarationLocationReferences.Length == 0)
                        {
                            hasConflict = false;
                        }
                        else
                        {
                            hasConflict = true;
                        }
                    }
                    else
                    {
                        hasConflict = false;
                        int symbolIndex = 0;
                        foreach (var symbol in newReferencedSymbols)
                        {
                            if (conflictAnnotation.RenameDeclarationLocationReferences[symbolIndex].SymbolLocationsCount != symbol.Locations.Count())
                            {
                                hasConflict = true;
                                break;
                            }

                            var newLocation = await GetSymbolLocationAsync(solution, symbol, _cancellationToken).ConfigureAwait(false);

                            if (newLocation != null && conflictAnnotation.RenameDeclarationLocationReferences[symbolIndex].IsSourceLocation)
                            {
                                // location was in source before, but not after rename
                                if (!newLocation.IsInSource)
                                {
                                    hasConflict = true;
                                    break;
                                }

                                var renameDeclarationLocationReference = conflictAnnotation.RenameDeclarationLocationReferences[symbolIndex];
                                var newAdjustedStartPosition = conflictResolution.GetAdjustedTokenStartingPosition(renameDeclarationLocationReference.TextSpan.Start, renameDeclarationLocationReference.DocumentId);
                                if (newAdjustedStartPosition != newLocation.SourceSpan.Start)
                                {
                                    hasConflict = true;
                                    break;
                                }

                                if (conflictAnnotation.RenameDeclarationLocationReferences[symbolIndex].IsOverriddenFromMetadata)
                                {
                                    var overridingSymbol = await SymbolFinder.FindSymbolAtPositionAsync(solution.GetDocument(newLocation.SourceTree), newLocation.SourceSpan.Start, cancellationToken: _cancellationToken).ConfigureAwait(false);
                                    if (overridingSymbol != null && renamedSymbolInNewSolution != overridingSymbol)
                                    {
                                        if (!overridingSymbol.IsOverride)
                                        {
                                            hasConflict = true;
                                            break;
                                        }
                                        else
                                        {
                                            var overriddenSymbol = overridingSymbol.OverriddenMember();
                                            if (overriddenSymbol == null || !overriddenSymbol.Locations.All(loc => loc.IsInMetadata))
                                            {
                                                hasConflict = true;
                                                break;
                                            }
                                        }
                                    }
                                }
                            }
                            else
                            {
                                var newMetadataName = symbol.ToDisplayString(s_metadataSymbolDisplayFormat);
                                var oldMetadataName = conflictAnnotation.RenameDeclarationLocationReferences[symbolIndex].Name;
                                if (newLocation == null ||
                                    newLocation.IsInSource ||
                                    !HeuristicMetadataNameEquivalenceCheck(
                                        oldMetadataName,
                                        newMetadataName,
                                        _originalText,
                                        _replacementText))
                                {
                                    hasConflict = true;
                                    break;
                                }
                            }

                            symbolIndex++;
                        }
                    }

                    return hasConflict;
                }
                catch (Exception e) when (FatalError.ReportUnlessCanceled(e))
                {
                    throw ExceptionUtilities.Unreachable;
                }
            }
Пример #10
0
            private async Task<ISymbol> GetRenamedSymbolInCurrentSolutionAsync(ConflictResolution conflictResolution)
            {
                // get the renamed symbol in complexified new solution
                int start = _documentOfRenameSymbolHasBeenRenamed
                    ? conflictResolution.RenamedSpansTracker.GetAdjustedPosition(_renameSymbolDeclarationLocation.SourceSpan.Start, _documentIdOfRenameSymbolDeclaration)
                    : _renameSymbolDeclarationLocation.SourceSpan.Start;

                var document = conflictResolution.NewSolution.GetDocument(_documentIdOfRenameSymbolDeclaration);
                var newSymbol = await SymbolFinder.FindSymbolAtPositionAsync(document, start, cancellationToken: _cancellationToken).ConfigureAwait(false);
                return newSymbol;
            }
Пример #11
0
            private async Task <bool> CheckForConflictAsync(
                ConflictResolution conflictResolution,
                ISymbol renamedSymbolInNewSolution,
                Document newDocument,
                RenameActionAnnotation conflictAnnotation,
                IEnumerable <ISymbol> newReferencedSymbols)
            {
                bool hasConflict;
                var  solution = conflictResolution.NewSolution;

                if (conflictAnnotation.IsNamespaceDeclarationReference)
                {
                    hasConflict = false;
                }
                else if (!conflictAnnotation.IsRenameLocation && conflictAnnotation.IsOriginalTextLocation && conflictAnnotation.RenameDeclarationLocationReferences.Length > 1 && newReferencedSymbols.Count() == 1)
                {
                    // an ambiguous situation was resolved through rename in non reference locations
                    hasConflict = false;
                }
                else if (newReferencedSymbols.Count() != conflictAnnotation.RenameDeclarationLocationReferences.Length)
                {
                    // Don't show conflicts for errors in the old solution that now bind in the new solution.
                    if (newReferencedSymbols.Count() != 0 && conflictAnnotation.RenameDeclarationLocationReferences.Length == 0)
                    {
                        hasConflict = false;
                    }
                    else
                    {
                        hasConflict = true;
                    }
                }
                else
                {
                    hasConflict = false;
                    int symbolIndex = 0;
                    foreach (var symbol in newReferencedSymbols)
                    {
                        if (conflictAnnotation.RenameDeclarationLocationReferences[symbolIndex].SymbolLocationsCount != symbol.Locations.Count())
                        {
                            hasConflict = true;
                            break;
                        }

                        var newLocation = await GetSymbolLocationAsync(solution, symbol, _cancellationToken).ConfigureAwait(false);

                        if (newLocation != null && conflictAnnotation.RenameDeclarationLocationReferences[symbolIndex].IsSourceLocation)
                        {
                            // location was in source before, but not after rename
                            if (!newLocation.IsInSource)
                            {
                                hasConflict = true;
                                break;
                            }

                            var renameDeclarationLocationReference = conflictAnnotation.RenameDeclarationLocationReferences[symbolIndex];
                            var newAdjustedStartPosition           = conflictResolution.GetAdjustedTokenStartingPosition(renameDeclarationLocationReference.TextSpan.Start, renameDeclarationLocationReference.DocumentId);
                            if (newAdjustedStartPosition != newLocation.SourceSpan.Start)
                            {
                                hasConflict = true;
                                break;
                            }

                            if (conflictAnnotation.RenameDeclarationLocationReferences[symbolIndex].IsOverriddenFromMetadata)
                            {
                                var overridingSymbol = await SymbolFinder.FindSymbolAtPositionAsync(solution.GetDocument(newLocation.SourceTree), newLocation.SourceSpan.Start, cancellationToken : _cancellationToken).ConfigureAwait(false);

                                if (overridingSymbol != null && renamedSymbolInNewSolution != overridingSymbol)
                                {
                                    if (!overridingSymbol.IsOverride)
                                    {
                                        hasConflict = true;
                                        break;
                                    }
                                    else
                                    {
                                        var overriddenSymbol = overridingSymbol.OverriddenMember();
                                        if (overriddenSymbol == null || !overriddenSymbol.Locations.All(loc => loc.IsInMetadata))
                                        {
                                            hasConflict = true;
                                            break;
                                        }
                                    }
                                }
                            }
                        }
                        else
                        {
                            var newMetadataName = symbol.ToDisplayString(s_metadataSymbolDisplayFormat);
                            var oldMetadataName = conflictAnnotation.RenameDeclarationLocationReferences[symbolIndex].Name;
                            if (newLocation == null ||
                                newLocation.IsInSource ||
                                !HeuristicMetadataNameEquivalenceCheck(
                                    oldMetadataName,
                                    newMetadataName,
                                    _originalText,
                                    _replacementText))
                            {
                                hasConflict = true;
                                break;
                            }
                        }

                        symbolIndex++;
                    }
                }

                return(hasConflict);
            }
Пример #12
0
 public TestAccessor(ConflictResolution conflictResolution)
 {
     _conflictResolution = conflictResolution;
 }
 public InlineRenameReplacementInfo(ConflictResolution conflicts)
 {
     _conflicts = conflicts;
 }
Пример #14
0
            private async Task<bool> CheckForConflictAsync(
                ConflictResolution conflictResolution,
                ISymbol renamedSymbolInNewSolution,
                Document newDocument,
                RenameActionAnnotation conflictAnnotation,
                IEnumerable<ISymbol> newReferencedSymbols)
            {
                bool hasConflict;
                var solution = conflictResolution.NewSolution;

                if (conflictAnnotation.IsNamespaceDeclarationReference)
                {
                    hasConflict = false;
                }
                else if (!conflictAnnotation.IsRenameLocation && conflictAnnotation.IsOriginalTextLocation && conflictAnnotation.RenameDeclarationLocationReferences.Length > 1 && newReferencedSymbols.Count() == 1)
                {
                    // an ambiguous situation was resolved through rename in non reference locations
                    hasConflict = false;
                }
                else if (newReferencedSymbols.Count() != conflictAnnotation.RenameDeclarationLocationReferences.Length)
                {
                    // Don't show conflicts for errors in the old solution that now bind in the new solution.
                    if (newReferencedSymbols.Count() != 0 && conflictAnnotation.RenameDeclarationLocationReferences.Length == 0)
                    {
                        hasConflict = false;
                    }
                    else
                    {
                        hasConflict = true;
                    }
                }
                else
                {
                    hasConflict = false;
                    int symbolIndex = 0;
                    foreach (var symbol in newReferencedSymbols)
                    {
                        if (conflictAnnotation.RenameDeclarationLocationReferences[symbolIndex].SymbolLocationsCount != symbol.Locations.Count())
                        {
                            hasConflict = true;
                            break;
                        }

                        var newLocation = await GetSymbolLocationAsync(solution, symbol, cancellationToken).ConfigureAwait(false);

                        if (newLocation != null && conflictAnnotation.RenameDeclarationLocationReferences[symbolIndex].IsSourceLocation)
                        {
                            // location was in source before, but not after rename
                            if (!newLocation.IsInSource)
                            {
                                hasConflict = true;
                                break;
                            }

                            var renameDeclarationLocationReference = conflictAnnotation.RenameDeclarationLocationReferences[symbolIndex];
                            var newAdjustedStartPosition = conflictResolution.GetAdjustedTokenStartingPosition(renameDeclarationLocationReference.TextSpan.Start, renameDeclarationLocationReference.DocumentId);
                            if (newAdjustedStartPosition != newLocation.SourceSpan.Start)
                            {
                                hasConflict = true;
                                break;
                            }

                            if (conflictAnnotation.RenameDeclarationLocationReferences[symbolIndex].IsOverriddenFromMetadata)
                            {
                                var overridingSymbol = await SymbolFinder.FindSymbolAtPositionAsync(solution.GetDocument(newLocation.SourceTree), newLocation.SourceSpan.Start, cancellationToken: cancellationToken).ConfigureAwait(false);
                                if (overridingSymbol != null && renamedSymbolInNewSolution != overridingSymbol)
                                {
                                    if (!overridingSymbol.IsOverride)
                                    {
                                        hasConflict = true;
                                        break;
                                    }
                                    else
                                    {
                                        var overriddenSymbol = overridingSymbol.OverriddenMember();
                                        if (overriddenSymbol == null || !overriddenSymbol.Locations.All(loc => loc.IsInMetadata))
                                        {
                                            hasConflict = true;
                                            break;
                                        }
                                    }
                                }
                            }
                        }
                        else
                        {
                            var newMetadataName = symbol.ToDisplayString(metadataSymbolDisplayFormat);
                            var oldMetadataName = conflictAnnotation.RenameDeclarationLocationReferences[symbolIndex].Name;
                            if (newLocation.IsInSource ||
                                !HeuristicMetadataNameEquivalenceCheck(
                                    oldMetadataName,
                                    newMetadataName,
                                    originalText,
                                    replacementText))
                            {
                                hasConflict = true;
                                break;
                            }
                        }

                        symbolIndex++;
                    }
                }

                return hasConflict;
            }
Пример #15
0
            private void DebugVerifyNoErrors(ConflictResolution conflictResolution, IEnumerable<DocumentId> documents)
            {
                var documentIdErrorStateLookup = new Dictionary<DocumentId, bool>();

                // we only check for the documentIds we add annotations to, which is a subset of the ones we're going 
                // to change the syntax in.
                foreach (var documentId in documents)
                {
                    // remember if there were issues in the document prior to renaming it.
                    var originalDoc = conflictResolution.OldSolution.GetDocument(documentId);
                    documentIdErrorStateLookup.Add(documentId, originalDoc.HasAnyErrors(_cancellationToken).WaitAndGetResult(_cancellationToken));
                }

                // We want to ignore few error message introduced by rename because the user is wantedly doing it.
                var ignoreErrorCodes = new List<string>();
                ignoreErrorCodes.Add("BC30420"); // BC30420 - Sub Main missing in VB Project
                ignoreErrorCodes.Add("CS5001"); // CS5001 - Missing Main in C# Project

                // only check if rename thinks it was successful
                if (conflictResolution.ReplacementTextValid && conflictResolution.RelatedLocations.All(loc => (loc.Type & RelatedLocationType.UnresolvableConflict) == 0))
                {
                    foreach (var documentId in documents)
                    {
                        // only check documents that had no errors before rename
                        // (we might have fixed them because of rename)
                        if (!documentIdErrorStateLookup[documentId])
                        {
                            conflictResolution.NewSolution.GetDocument(documentId).VerifyNoErrorsAsync("Rename introduced errors in error-free code", _cancellationToken, ignoreErrorCodes).Wait(_cancellationToken);
                        }
                    }
                }
            }
Пример #16
0
            /// <summary>
            /// Find conflicts in the new solution 
            /// </summary>
            private async Task<bool> IdentifyConflictsAsync(
                HashSet<DocumentId> documentIdsForConflictResolution,
                IEnumerable<DocumentId> allDocumentIdsInProject,
                ProjectId projectId,
                ConflictResolution conflictResolution)
            {
                try
                {
                    _documentOfRenameSymbolHasBeenRenamed |= documentIdsForConflictResolution.Contains(_documentIdOfRenameSymbolDeclaration);

                    // Get the renamed symbol in complexified new solution
                    ISymbol renamedSymbolInNewSolution = await GetRenamedSymbolInCurrentSolutionAsync(conflictResolution).ConfigureAwait(false);

                    // if the text replacement is invalid, we just did a simple token replacement.
                    // Therefore we don't need more mapping information and can skip the rest of 
                    // the loop body.
                    if (!IsRenameValid(conflictResolution, renamedSymbolInNewSolution))
                    {
                        foreach (var documentId in documentIdsForConflictResolution)
                        {
                            var newDocument = conflictResolution.NewSolution.GetDocument(documentId);
                            var syntaxRoot = await newDocument.GetSyntaxRootAsync(_cancellationToken).ConfigureAwait(false);

                            var nodesOrTokensWithConflictCheckAnnotations = GetNodesOrTokensToCheckForConflicts(documentId, syntaxRoot);
                            foreach (var nodeOrToken in nodesOrTokensWithConflictCheckAnnotations)
                            {
                                if (nodeOrToken.Item2.IsRenameLocation)
                                {
                                    conflictResolution.AddRelatedLocation(new RelatedLocation(nodeOrToken.Item2.OriginalSpan, documentId, RelatedLocationType.UnresolvedConflict));
                                }
                            }
                        }

                        return false;
                    }

                    Dictionary<Location, Location> reverseMappedLocations = new Dictionary<Location, Location>();

                    foreach (var documentId in documentIdsForConflictResolution)
                    {
                        var newDocument = conflictResolution.NewSolution.GetDocument(documentId);
                        var syntaxRoot = await newDocument.GetSyntaxRootAsync(_cancellationToken).ConfigureAwait(false);
                        var baseDocument = conflictResolution.OldSolution.GetDocument(documentId);
                        var baseSyntaxTree = await baseDocument.GetSyntaxTreeAsync(_cancellationToken).ConfigureAwait(false);
                        var baseRoot = await baseDocument.GetSyntaxRootAsync(_cancellationToken).ConfigureAwait(false);
                        SemanticModel newDocumentSemanticModel = null;
                        var syntaxFactsService = newDocument.Project.LanguageServices.GetService<ISyntaxFactsService>();

                        // Get all tokens that need conflict check
                        var nodesOrTokensWithConflictCheckAnnotations = GetNodesOrTokensToCheckForConflicts(documentId, syntaxRoot);

                        var complexifiedLocationSpanForThisDocument =
                            _conflictLocations
                            .Where(t => t.DocumentId == documentId)
                            .Select(t => t.OriginalIdentifierSpan).ToSet();

                        foreach (var nodeAndAnnotation in nodesOrTokensWithConflictCheckAnnotations)
                        {
                            var tokenOrNode = nodeAndAnnotation.Item1;
                            var conflictAnnotation = nodeAndAnnotation.Item2;
                            reverseMappedLocations[tokenOrNode.GetLocation()] = baseSyntaxTree.GetLocation(conflictAnnotation.OriginalSpan);
                            var originalLocation = conflictAnnotation.OriginalSpan;
                            IEnumerable<ISymbol> newReferencedSymbols = null;

                            var hasConflict = _renameAnnotations.HasAnnotation(tokenOrNode, RenameInvalidIdentifierAnnotation.Instance);
                            if (!hasConflict)
                            {
                                newDocumentSemanticModel = newDocumentSemanticModel ?? await newDocument.GetSemanticModelAsync(_cancellationToken).ConfigureAwait(false);
                                newReferencedSymbols = GetSymbolsInNewSolution(newDocument, newDocumentSemanticModel, conflictAnnotation, tokenOrNode);

                                // The semantic correctness, after rename, for each token of interest in the rename context is performed by getting the symbol pointed by
                                // each token and obtain the Symbol's First Ordered Location's  Span-Start and check to see if it is the same as before from the base solution.
                                // During rename, the spans would have been modified and so we need to adjust the old position to the new position for which we use the renameSpanTracker, which
                                // was tracking & mapping the old span -> new span during rename
                                hasConflict = await CheckForConflictAsync(conflictResolution, renamedSymbolInNewSolution, newDocument, conflictAnnotation, newReferencedSymbols).ConfigureAwait(false);
                            }

                            if (!hasConflict && !conflictAnnotation.IsInvocationExpression)
                            {
                                hasConflict = LocalVariableConflictPerLanguage((SyntaxToken)tokenOrNode, newDocument, newReferencedSymbols);
                            }

                            if (!hasConflict)
                            {
                                if (conflictAnnotation.IsRenameLocation)
                                {
                                    conflictResolution.AddRelatedLocation(
                                        new RelatedLocation(originalLocation,
                                        documentId,
                                        complexifiedLocationSpanForThisDocument.Contains(originalLocation) ? RelatedLocationType.ResolvedReferenceConflict : RelatedLocationType.NoConflict,
                                        isReference: true));
                                }
                                else
                                {
                                    // if a complexified location was not a reference location, then it was a resolved conflict of a non reference location
                                    if (!conflictAnnotation.IsOriginalTextLocation && complexifiedLocationSpanForThisDocument.Contains(originalLocation))
                                    {
                                        conflictResolution.AddRelatedLocation(
                                            new RelatedLocation(originalLocation,
                                            documentId,
                                            RelatedLocationType.ResolvedNonReferenceConflict,
                                            isReference: false));
                                    }
                                }
                            }
                            else
                            {
                                var baseToken = baseRoot.FindToken(conflictAnnotation.OriginalSpan.Start, true);
                                var complexifiedTarget = GetExpansionTargetForLocationPerLanguage(baseToken, baseDocument);
                                conflictResolution.AddRelatedLocation(new RelatedLocation(
                                    originalLocation,
                                    documentId,
                                    complexifiedTarget != null ? RelatedLocationType.PossiblyResolvableConflict : RelatedLocationType.UnresolvableConflict,
                                    isReference: conflictAnnotation.IsRenameLocation,
                                    complexifiedTargetSpan: complexifiedTarget != null ? complexifiedTarget.Span : default(TextSpan)));
                            }
                        }
                    }

                    // there are more conflicts that cannot be identified by checking if the tokens still reference the same
                    // symbol. These conflicts are mostly language specific. A good example is a member with the same name
                    // as the parent (yes I know, this is a simplification).
                    if (_documentIdOfRenameSymbolDeclaration.ProjectId == projectId)
                    {
                        // Calculating declaration conflicts may require location mapping in documents
                        // that were not otherwise being processed in the current rename phase, so add
                        // the annotated spans in these documents to reverseMappedLocations.
                        foreach (var unprocessedDocumentIdWithPotentialDeclarationConflicts in allDocumentIdsInProject.Where(d => !documentIdsForConflictResolution.Contains(d)))
                        {
                            var newDocument = conflictResolution.NewSolution.GetDocument(unprocessedDocumentIdWithPotentialDeclarationConflicts);
                            var syntaxRoot = await newDocument.GetSyntaxRootAsync(_cancellationToken).ConfigureAwait(false);
                            var baseDocument = conflictResolution.OldSolution.GetDocument(unprocessedDocumentIdWithPotentialDeclarationConflicts);
                            var baseSyntaxTree = await baseDocument.GetSyntaxTreeAsync(_cancellationToken).ConfigureAwait(false);

                            var nodesOrTokensWithConflictCheckAnnotations = GetNodesOrTokensToCheckForConflicts(unprocessedDocumentIdWithPotentialDeclarationConflicts, syntaxRoot);
                            foreach (var nodeAndAnnotation in nodesOrTokensWithConflictCheckAnnotations)
                            {
                                var tokenOrNode = nodeAndAnnotation.Item1;
                                var conflictAnnotation = nodeAndAnnotation.Item2;
                                reverseMappedLocations[tokenOrNode.GetLocation()] = baseSyntaxTree.GetLocation(conflictAnnotation.OriginalSpan);
                            }
                        }

                        IEnumerable<ISymbol> referencedSymbols = _renameLocationSet.ReferencedSymbols;
                        ISymbol renameSymbol = _renameLocationSet.Symbol;
                        await AddDeclarationConflictsAsync(renamedSymbolInNewSolution, renameSymbol, referencedSymbols, conflictResolution, reverseMappedLocations, _cancellationToken).ConfigureAwait(false);
                    }

                    return conflictResolution.RelatedLocations.Any(r => r.Type == RelatedLocationType.PossiblyResolvableConflict);
                }
                catch (Exception e) when (FatalError.ReportUnlessCanceled(e))
                {
                    throw ExceptionUtilities.Unreachable;
                }
            }
Пример #17
0
 private static bool IsRenameValid(ConflictResolution conflictResolution, ISymbol renamedSymbol)
 {
     // if we rename an identifier and it now binds to a symbol from metadata this should be treated as
     // an invalid rename.
     return conflictResolution.ReplacementTextValid && renamedSymbol != null && renamedSymbol.Locations.Any(loc => loc.IsInSource);
 }
Пример #18
0
            private async Task<ISymbol> GetRenamedSymbolInCurrentSolutionAsync(ConflictResolution conflictResolution)
            {
                try
                {
                    // get the renamed symbol in complexified new solution
                    int start = _documentOfRenameSymbolHasBeenRenamed
                        ? conflictResolution.RenamedSpansTracker.GetAdjustedPosition(_renameSymbolDeclarationLocation.SourceSpan.Start, _documentIdOfRenameSymbolDeclaration)
                        : _renameSymbolDeclarationLocation.SourceSpan.Start;

                    var document = conflictResolution.NewSolution.GetDocument(_documentIdOfRenameSymbolDeclaration);
                    var newSymbol = await SymbolFinder.FindSymbolAtPositionAsync(document, start, cancellationToken: _cancellationToken).ConfigureAwait(false);
                    return newSymbol;
                }
                catch (Exception e) when (FatalError.ReportUnlessCanceled(e))
                {
                    throw ExceptionUtilities.Unreachable;
                }
            }
Пример #19
0
        private static void AddImplicitConflicts(
            ISymbol renamedSymbol,
            ISymbol originalSymbol,
            IEnumerable<ReferenceLocation> implicitReferenceLocations,
            SemanticModel semanticModel,
            Location originalDeclarationLocation,
            int newDeclarationLocationStartingPosition,
            ConflictResolution conflictResolution,
            CancellationToken cancellationToken)
        {
            {
                var renameRewriterService = conflictResolution.NewSolution.Workspace.Services.GetLanguageServices(renamedSymbol.Language).GetService<IRenameRewriterLanguageService>();
                var implicitUsageConflicts = renameRewriterService.ComputePossibleImplicitUsageConflicts(renamedSymbol, semanticModel, originalDeclarationLocation, newDeclarationLocationStartingPosition, cancellationToken);
                foreach (var implicitUsageConflict in implicitUsageConflicts)
                {
                    conflictResolution.AddOrReplaceRelatedLocation(new RelatedLocation(implicitUsageConflict.SourceSpan, conflictResolution.OldSolution.GetDocument(implicitUsageConflict.SourceTree).Id, RelatedLocationType.UnresolvableConflict));
                }
            }

            if (implicitReferenceLocations.IsEmpty())
            {
                return;
            }

            foreach (var implicitReferenceLocationsPerLanguage in implicitReferenceLocations.GroupBy(loc => loc.Document.Project.Language))
            {
                // the location of the implicit reference defines the language rules to check.
                // E.g. foreach in C# using a MoveNext in VB that is renamed to MOVENEXT (within VB)
                var renameRewriterService = implicitReferenceLocationsPerLanguage.First().Document.Project.LanguageServices.GetService<IRenameRewriterLanguageService>();
                var implicitConflicts = renameRewriterService.ComputeImplicitReferenceConflicts(
                    originalSymbol,
                    renamedSymbol,
                    implicitReferenceLocationsPerLanguage,
                    cancellationToken);

                foreach (var implicitConflict in implicitConflicts)
                {
                    conflictResolution.AddRelatedLocation(new RelatedLocation(implicitConflict.SourceSpan, conflictResolution.OldSolution.GetDocument(implicitConflict.SourceTree).Id, RelatedLocationType.UnresolvableConflict));
                }
            }
        }
Пример #20
0
            // The method which performs rename, resolves the conflict locations and returns the result of the rename operation
            public async Task <ConflictResolution> ResolveConflictsAsync()
            {
                try
                {
                    await FindDocumentsAndPossibleNameConflicts().ConfigureAwait(false);

                    var baseSolution = _renameLocationSet.Solution;

                    // Process rename one project at a time to improve caching and reduce syntax tree serialization.
                    var documentsGroupedByTopologicallySortedProjectId = _documentsIdsToBeCheckedForConflict
                                                                         .GroupBy(d => d.ProjectId)
                                                                         .OrderBy(g => _topologicallySortedProjects.IndexOf(g.Key));

                    _replacementTextValid = IsIdentifierValid_Worker(baseSolution, _replacementText, documentsGroupedByTopologicallySortedProjectId.Select(g => g.Key), _cancellationToken);
                    var renamedSpansTracker = new RenamedSpansTracker();
                    var conflictResolution  = new ConflictResolution(baseSolution, renamedSpansTracker, _replacementText, _replacementTextValid);

                    foreach (var documentsByProject in documentsGroupedByTopologicallySortedProjectId)
                    {
                        var documentIdsThatGetsAnnotatedAndRenamed = new HashSet <DocumentId>(documentsByProject);
                        using (baseSolution.Services.CacheService?.EnableCaching(documentsByProject.Key))
                        {
                            // Rename is going to be in 5 phases.
                            // 1st phase - Does a simple token replacement
                            // If the 1st phase results in conflict then we perform then:
                            //      2nd phase is to expand and simplify only the reference locations with conflicts
                            //      3rd phase is to expand and simplify all the conflict locations (both reference and non-reference)
                            // If there are unresolved Conflicts after the 3rd phase then in 4th phase,
                            //      We complexify and resolve locations that were resolvable and for the other locations we perform the normal token replacement like the first the phase.
                            // If the OptionSet has RenameFile to true, we rename files with the type declaration
                            for (var phase = 0; phase < 4; phase++)
                            {
                                // Step 1:
                                // The rename process and annotation for the bookkeeping is performed in one-step
                                // The Process in short is,
                                // 1. If renaming a token which is no conflict then replace the token and make a map of the oldspan to the newspan
                                // 2. If we encounter a node that has to be expanded( because there was a conflict in previous phase), we expand it.
                                //    If the node happens to contain a token that needs to be renamed then we annotate it and rename it after expansion else just expand and proceed
                                // 3. Through the whole process we maintain a map of the oldspan to newspan. In case of expansion & rename, we map the expanded node and the renamed token
                                conflictResolution.UpdateCurrentSolution(await AnnotateAndRename_WorkerAsync(
                                                                             baseSolution,
                                                                             conflictResolution.NewSolution,
                                                                             documentIdsThatGetsAnnotatedAndRenamed,
                                                                             _renameLocationSet.Locations,
                                                                             renamedSpansTracker,
                                                                             _replacementTextValid).ConfigureAwait(false));

                                // Step 2: Check for conflicts in the renamed solution
                                var foundResolvableConflicts = await IdentifyConflictsAsync(
                                    documentIdsForConflictResolution : documentIdsThatGetsAnnotatedAndRenamed,
                                    allDocumentIdsInProject : documentsByProject,
                                    projectId : documentsByProject.Key,
                                    conflictResolution : conflictResolution).ConfigureAwait(false);

                                if (!foundResolvableConflicts || phase == 3)
                                {
                                    break;
                                }

                                if (phase == 0)
                                {
                                    _conflictLocations = conflictResolution.RelatedLocations
                                                         .Where(loc => (documentIdsThatGetsAnnotatedAndRenamed.Contains(loc.DocumentId) && loc.Type == RelatedLocationType.PossiblyResolvableConflict && loc.IsReference))
                                                         .Select(loc => new ConflictLocationInfo(loc))
                                                         .ToSet();

                                    // If there were no conflicting locations in references, then the first conflict phase has to be skipped.
                                    if (_conflictLocations.Count == 0)
                                    {
                                        phase++;
                                    }
                                }

                                if (phase == 1)
                                {
                                    _conflictLocations = _conflictLocations.Concat(conflictResolution.RelatedLocations
                                                                                   .Where(loc => documentIdsThatGetsAnnotatedAndRenamed.Contains(loc.DocumentId) && loc.Type == RelatedLocationType.PossiblyResolvableConflict)
                                                                                   .Select(loc => new ConflictLocationInfo(loc)))
                                                         .ToSet();
                                }

                                // Set the documents with conflicts that need to be processed in the next phase.
                                // Note that we need to get the conflictLocations here since we're going to remove some locations below if phase == 2
                                documentIdsThatGetsAnnotatedAndRenamed = new HashSet <DocumentId>(_conflictLocations.Select(l => l.DocumentId));

                                if (phase == 2)
                                {
                                    // After phase 2, if there are still conflicts then remove the conflict locations from being expanded
                                    var unresolvedLocations = conflictResolution.RelatedLocations
                                                              .Where(l => (l.Type & RelatedLocationType.UnresolvedConflict) != 0)
                                                              .Select(l => Tuple.Create(l.ComplexifiedTargetSpan, l.DocumentId)).Distinct();

                                    _conflictLocations = _conflictLocations.Where(l => !unresolvedLocations.Any(c => c.Item2 == l.DocumentId && c.Item1.Contains(l.OriginalIdentifierSpan))).ToSet();
                                }

                                // Clean up side effects from rename before entering the next phase
                                conflictResolution.ClearDocuments(documentIdsThatGetsAnnotatedAndRenamed);
                            }

                            // Step 3: Simplify the project
                            conflictResolution.UpdateCurrentSolution(await renamedSpansTracker.SimplifyAsync(conflictResolution.NewSolution, documentsByProject, _replacementTextValid, _renameAnnotations, _cancellationToken).ConfigureAwait(false));
                            await conflictResolution.RemoveAllRenameAnnotationsAsync(documentsByProject, _renameAnnotations, _cancellationToken).ConfigureAwait(false);
                        }
                    }

                    // This rename could break implicit references of this symbol (e.g. rename MoveNext on a collection like type in a
                    // foreach/for each statement
                    var renamedSymbolInNewSolution = await GetRenamedSymbolInCurrentSolutionAsync(conflictResolution).ConfigureAwait(false);

                    if (IsRenameValid(conflictResolution, renamedSymbolInNewSolution))
                    {
                        await AddImplicitConflictsAsync(
                            renamedSymbolInNewSolution,
                            _renameLocationSet.Symbol,
                            _renameLocationSet.ImplicitLocations,
                            await conflictResolution.NewSolution.GetDocument(_documentIdOfRenameSymbolDeclaration).GetSemanticModelAsync(_cancellationToken).ConfigureAwait(false),
                            _renameSymbolDeclarationLocation,
                            renamedSpansTracker.GetAdjustedPosition(_renameSymbolDeclarationLocation.SourceSpan.Start, _documentIdOfRenameSymbolDeclaration),
                            conflictResolution,
                            _cancellationToken).ConfigureAwait(false);
                    }

                    foreach (var relatedLocation in conflictResolution.RelatedLocations)
                    {
                        if (relatedLocation.Type == RelatedLocationType.PossiblyResolvableConflict)
                        {
                            relatedLocation.Type = RelatedLocationType.UnresolvedConflict;
                        }
                    }
#if DEBUG
                    await DebugVerifyNoErrorsAsync(conflictResolution, _documentsIdsToBeCheckedForConflict).ConfigureAwait(false);
#endif

                    // Step 5: Rename declaration files
                    if (_optionSet.GetOption(RenameOptions.RenameFile))
                    {
                        var definitionLocations = _renameLocationSet.Symbol.Locations;
                        var definitionDocuments = definitionLocations
                                                  .Select(l => conflictResolution.OldSolution.GetDocument(l.SourceTree))
                                                  .Distinct();

                        if (definitionDocuments.Count() == 1)
                        {
                            // At the moment, only single document renaming is allowed
                            conflictResolution.RenameDocumentToMatchNewSymbol(definitionDocuments.Single());
                        }
                    }

                    return(conflictResolution);
                }
                catch (Exception e) when(FatalError.ReportUnlessCanceled(e))
                {
                    throw ExceptionUtilities.Unreachable;
                }
            }
Пример #21
0
        /// <summary>
        /// Computes an adds conflicts relating to declarations, which are independent of
        /// location-based checks. Examples of these types of conflicts include renaming a member to
        /// the same name as another member of a type: binding doesn't change (at least from the
        /// perspective of find all references), but we still need to track it.
        /// </summary>
        internal static async Task AddDeclarationConflictsAsync(
            ISymbol renamedSymbol,
            ISymbol renameSymbol,
            IEnumerable<ISymbol> referencedSymbols,
            ConflictResolution conflictResolution,
            IDictionary<Location, Location> reverseMappedLocations,
            CancellationToken cancellationToken)
        {
            if (renamedSymbol.ContainingSymbol is INamedTypeSymbol)
            {
                var otherThingsNamedTheSame = renamedSymbol.ContainingType.GetMembers(renamedSymbol.Name)
                                                       .Where(s => !s.Equals(renamedSymbol) && string.Equals(s.MetadataName, renamedSymbol.MetadataName, StringComparison.Ordinal) &&
                                                              (s.Kind != SymbolKind.Method || renamedSymbol.Kind != SymbolKind.Method)).ToList();

                AddConflictingSymbolLocations(otherThingsNamedTheSame, conflictResolution, reverseMappedLocations);
            }

            // Some types of symbols (namespaces, cref stuff, etc) might not have ContainingAssemblies
            if (renamedSymbol.ContainingAssembly != null)
            {
                var project = conflictResolution.NewSolution.GetProject(renamedSymbol.ContainingAssembly, cancellationToken);

                // There also might be language specific rules we need to include
                var languageRenameService = project.LanguageServices.GetService<IRenameRewriterLanguageService>();
                var languageConflicts = await languageRenameService.ComputeDeclarationConflictsAsync(
                    conflictResolution.ReplacementText,
                    renamedSymbol,
                    renameSymbol,
                    referencedSymbols,
                    conflictResolution.OldSolution,
                    conflictResolution.NewSolution,
                    reverseMappedLocations,
                    cancellationToken).ConfigureAwait(false);

                foreach (var languageConflict in languageConflicts)
                {
                    conflictResolution.AddOrReplaceRelatedLocation(new RelatedLocation(languageConflict.SourceSpan, conflictResolution.OldSolution.GetDocument(languageConflict.SourceTree).Id, RelatedLocationType.UnresolvableConflict));
                }
            }
        }
Пример #22
0
            private async Task <bool> CheckForConflictAsync(
                ConflictResolution conflictResolution,
                ISymbol renamedSymbolInNewSolution,
                Document newDocument,
                RenameActionAnnotation conflictAnnotation,
                IEnumerable <ISymbol> newReferencedSymbols)
            {
                try
                {
                    bool hasConflict;
                    var  solution = conflictResolution.NewSolution;

                    if (conflictAnnotation.IsNamespaceDeclarationReference)
                    {
                        hasConflict = false;
                    }
                    else if (conflictAnnotation.IsMemberGroupReference)
                    {
                        if (!conflictAnnotation.RenameDeclarationLocationReferences.Any())
                        {
                            hasConflict = false;
                        }
                        else
                        {
                            // Ensure newReferencedSymbols contains at least one of the original referenced
                            // symbols, and allow any new symbols to be added to the set of references.

                            hasConflict = true;

                            var newLocationTasks = newReferencedSymbols.Select(async symbol => await GetSymbolLocationAsync(solution, symbol, _cancellationToken).ConfigureAwait(false));
                            var newLocations     = (await Task.WhenAll(newLocationTasks).ConfigureAwait(false)).Where(loc => loc != null && loc.IsInSource);
                            foreach (var originalReference in conflictAnnotation.RenameDeclarationLocationReferences.Where(loc => loc.IsSourceLocation))
                            {
                                var adjustedStartPosition = conflictResolution.GetAdjustedTokenStartingPosition(originalReference.TextSpan.Start, originalReference.DocumentId);
                                if (newLocations.Any(loc => loc.SourceSpan.Start == adjustedStartPosition))
                                {
                                    hasConflict = false;
                                    break;
                                }
                            }
                        }
                    }
                    else if (!conflictAnnotation.IsRenameLocation && conflictAnnotation.IsOriginalTextLocation && conflictAnnotation.RenameDeclarationLocationReferences.Length > 1 && newReferencedSymbols.Count() == 1)
                    {
                        // an ambiguous situation was resolved through rename in non reference locations
                        hasConflict = false;
                    }
                    else if (newReferencedSymbols.Count() != conflictAnnotation.RenameDeclarationLocationReferences.Length)
                    {
                        // Don't show conflicts for errors in the old solution that now bind in the new solution.
                        if (newReferencedSymbols.Count() != 0 && conflictAnnotation.RenameDeclarationLocationReferences.Length == 0)
                        {
                            hasConflict = false;
                        }
                        else
                        {
                            hasConflict = true;
                        }
                    }
                    else
                    {
                        hasConflict = false;
                        var symbolIndex = 0;
                        foreach (var symbol in newReferencedSymbols)
                        {
                            if (conflictAnnotation.RenameDeclarationLocationReferences[symbolIndex].SymbolLocationsCount != symbol.Locations.Length)
                            {
                                hasConflict = true;
                                break;
                            }

                            var newLocation = await GetSymbolLocationAsync(solution, symbol, _cancellationToken).ConfigureAwait(false);

                            if (newLocation != null && conflictAnnotation.RenameDeclarationLocationReferences[symbolIndex].IsSourceLocation)
                            {
                                // location was in source before, but not after rename
                                if (!newLocation.IsInSource)
                                {
                                    hasConflict = true;
                                    break;
                                }

                                var renameDeclarationLocationReference = conflictAnnotation.RenameDeclarationLocationReferences[symbolIndex];
                                var newAdjustedStartPosition           = conflictResolution.GetAdjustedTokenStartingPosition(renameDeclarationLocationReference.TextSpan.Start, renameDeclarationLocationReference.DocumentId);
                                if (newAdjustedStartPosition != newLocation.SourceSpan.Start)
                                {
                                    hasConflict = true;
                                    break;
                                }

                                if (conflictAnnotation.RenameDeclarationLocationReferences[symbolIndex].IsOverriddenFromMetadata)
                                {
                                    var overridingSymbol = await SymbolFinder.FindSymbolAtPositionAsync(solution.GetDocument(newLocation.SourceTree), newLocation.SourceSpan.Start, cancellationToken : _cancellationToken).ConfigureAwait(false);

                                    if (overridingSymbol != null && !Equals(renamedSymbolInNewSolution, overridingSymbol))
                                    {
                                        if (!overridingSymbol.IsOverride)
                                        {
                                            hasConflict = true;
                                            break;
                                        }
                                        else
                                        {
                                            var overriddenSymbol = overridingSymbol.OverriddenMember();
                                            if (overriddenSymbol == null || !overriddenSymbol.Locations.All(loc => loc.IsInMetadata))
                                            {
                                                hasConflict = true;
                                                break;
                                            }
                                        }
                                    }
                                }
                            }
                            else
                            {
                                var newMetadataName = symbol.ToDisplayString(s_metadataSymbolDisplayFormat);
                                var oldMetadataName = conflictAnnotation.RenameDeclarationLocationReferences[symbolIndex].Name;
                                if (newLocation == null ||
                                    newLocation.IsInSource ||
                                    !HeuristicMetadataNameEquivalenceCheck(
                                        oldMetadataName,
                                        newMetadataName,
                                        _originalText,
                                        _replacementText))
                                {
                                    hasConflict = true;
                                    break;
                                }
                            }

                            symbolIndex++;
                        }
                    }

                    return(hasConflict);
                }
                catch (Exception e) when(FatalError.ReportUnlessCanceled(e))
                {
                    throw ExceptionUtilities.Unreachable;
                }
            }
Пример #23
0
 private static void AddConflictingSymbolLocations(IEnumerable<ISymbol> conflictingSymbols, ConflictResolution conflictResolution, IDictionary<Location, Location> reverseMappedLocations)
 {
     foreach (var newSymbol in conflictingSymbols)
     {
         foreach (var newLocation in newSymbol.Locations)
         {
             if (newLocation.IsInSource)
             {
                 Location oldLocation;
                 if (reverseMappedLocations.TryGetValue(newLocation, out oldLocation))
                 {
                     conflictResolution.AddOrReplaceRelatedLocation(new RelatedLocation(oldLocation.SourceSpan, conflictResolution.OldSolution.GetDocument(oldLocation.SourceTree).Id, RelatedLocationType.UnresolvableConflict));
                 }
             }
         }
     }
 }