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)); } } }
/// <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.annotation.IsRenameLocation) { conflictResolution.AddRelatedLocation(new RelatedLocation( nodeOrToken.annotation.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.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 = 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; } }