Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/monodevelop.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs')
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs574
1 files changed, 574 insertions, 0 deletions
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs
new file mode 100644
index 0000000000..b5951ab7bf
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeRefactorings/InlineTemporary/InlineTemporaryCodeRefactoringProvider.cs
@@ -0,0 +1,574 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Composition;
+using System.Diagnostics;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.CodeRefactorings;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Symbols;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.FindSymbols;
+using Microsoft.CodeAnalysis.Formatting;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Simplification;
+using Roslyn.Utilities;
+using Microsoft.CodeAnalysis;
+using ICSharpCode.NRefactory6.CSharp;
+using Microsoft.CodeAnalysis.CSharp;
+using ICSharpCode.NRefactory6.CSharp.Refactoring;
+using MonoDevelop.Core;
+
+namespace MonoDevelop.CSharp.CodeRefactorings.InlineTemporary
+{
+ [ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = PredefinedCodeRefactoringProviderNames.InlineTemporary), Shared]
+ internal partial class InlineTemporaryCodeRefactoringProvider : CodeRefactoringProvider
+ {
+ internal static readonly SyntaxAnnotation DefinitionAnnotation = new SyntaxAnnotation();
+ internal static readonly SyntaxAnnotation ReferenceAnnotation = new SyntaxAnnotation();
+ internal static readonly SyntaxAnnotation InitializerAnnotation = new SyntaxAnnotation();
+ internal static readonly SyntaxAnnotation ExpressionToInlineAnnotation = new SyntaxAnnotation();
+
+ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
+ {
+ var document = context.Document;
+ var textSpan = context.Span;
+ var cancellationToken = context.CancellationToken;
+
+ if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles)
+ {
+ return;
+ }
+ var model = await document.GetSemanticModelAsync (cancellationToken).ConfigureAwait (false);
+ if (model.IsFromGeneratedCode (cancellationToken))
+ return;
+ var root = await document.GetCSharpSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
+ var token = root.FindToken(textSpan.Start);
+
+ if (!token.Span.Contains(textSpan))
+ {
+ return;
+ }
+
+ var node = token.Parent;
+
+ if (!node.IsKind(SyntaxKind.VariableDeclarator) ||
+ !node.IsParentKind(SyntaxKind.VariableDeclaration) ||
+ !node.Parent.IsParentKind(SyntaxKind.LocalDeclarationStatement))
+ {
+ return;
+ }
+
+ var variableDeclarator = (VariableDeclaratorSyntax)node;
+ var variableDeclaration = (VariableDeclarationSyntax)variableDeclarator.Parent;
+ var localDeclarationStatement = (LocalDeclarationStatementSyntax)variableDeclaration.Parent;
+
+ if (variableDeclarator.Identifier != token ||
+ variableDeclarator.Initializer == null ||
+ variableDeclarator.Initializer.Value.IsMissing ||
+ variableDeclarator.Initializer.Value.IsKind(SyntaxKind.StackAllocArrayCreationExpression))
+ {
+ return;
+ }
+
+ if (localDeclarationStatement.ContainsDiagnostics)
+ {
+ return;
+ }
+
+ var references = await GetReferencesAsync(document, variableDeclarator, cancellationToken).ConfigureAwait(false);
+ if (!references.Any())
+ {
+ return;
+ }
+
+ context.RegisterRefactoring(
+ new DocumentChangeAction(node.Span, DiagnosticSeverity.Info,
+ GettextCatalog.GetString ("Inline temporary variable"),
+ (c) => this.InlineTemporaryAsync(document, variableDeclarator, c)));
+ }
+
+ private async Task<IEnumerable<ReferenceLocation>> GetReferencesAsync(
+ Document document,
+ VariableDeclaratorSyntax variableDeclarator,
+ CancellationToken cancellationToken)
+ {
+ var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
+ var local = semanticModel.GetDeclaredSymbol(variableDeclarator, cancellationToken);
+
+ if (local != null)
+ {
+ var findReferencesResult = await SymbolFinder.FindReferencesAsync(local, document.Project.Solution, cancellationToken).ConfigureAwait(false);
+ var locations = findReferencesResult.Single(r => r.Definition == local).Locations;
+ if (!locations.Any(loc => semanticModel.SyntaxTree.OverlapsHiddenPosition(loc.Location.SourceSpan, cancellationToken)))
+ {
+ return locations;
+ }
+ }
+
+ return SpecializedCollections.EmptyEnumerable<ReferenceLocation>();
+ }
+
+ private static bool HasConflict(IdentifierNameSyntax identifier, VariableDeclaratorSyntax variableDeclarator)
+ {
+ // TODO: Check for more conflict types.
+ if (identifier.SpanStart < variableDeclarator.SpanStart)
+ {
+ return true;
+ }
+
+ var identifierNode = identifier
+ .Ancestors()
+ .TakeWhile(n => n.Kind() == SyntaxKind.ParenthesizedExpression || n.Kind() == SyntaxKind.CastExpression)
+ .LastOrDefault();
+
+ if (identifierNode == null)
+ {
+ identifierNode = identifier;
+ }
+
+ if (identifierNode.IsParentKind(SyntaxKind.Argument))
+ {
+ var argument = (ArgumentSyntax)identifierNode.Parent;
+ if (argument.RefOrOutKeyword.Kind() != SyntaxKind.None)
+ {
+ return true;
+ }
+ }
+ else if (identifierNode.Parent.IsKind(
+ SyntaxKind.PreDecrementExpression,
+ SyntaxKind.PreIncrementExpression,
+ SyntaxKind.PostDecrementExpression,
+ SyntaxKind.PostIncrementExpression,
+ SyntaxKind.AddressOfExpression))
+ {
+ return true;
+ }
+ else if (identifierNode.Parent is AssignmentExpressionSyntax)
+ {
+ var binaryExpression = (AssignmentExpressionSyntax)identifierNode.Parent;
+ if (binaryExpression.Left == identifierNode)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private static SyntaxAnnotation CreateConflictAnnotation()
+ {
+ return ConflictAnnotation.Create(GettextCatalog.GetString ("Conflict(s) detected."));
+ }
+
+ private async Task<Document> InlineTemporaryAsync(Document document, VariableDeclaratorSyntax declarator, CancellationToken cancellationToken)
+ {
+ var workspace = document.Project.Solution.Workspace;
+
+ // Annotate the variable declarator so that we can get back to it later.
+ var updatedDocument = await document.ReplaceNodeAsync(declarator, declarator.WithAdditionalAnnotations(DefinitionAnnotation), cancellationToken).ConfigureAwait(false);
+ var semanticModel = await updatedDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
+
+ var variableDeclarator = await FindDeclaratorAsync(updatedDocument, cancellationToken).ConfigureAwait(false);
+
+ // Create the expression that we're actually going to inline.
+ var expressionToInline = await CreateExpressionToInlineAsync(variableDeclarator, updatedDocument, cancellationToken).ConfigureAwait(false);
+
+ // Collect the identifier names for each reference.
+ var local = (ILocalSymbol)semanticModel.GetDeclaredSymbol(variableDeclarator, cancellationToken);
+ var symbolRefs = await SymbolFinder.FindReferencesAsync(local, updatedDocument.Project.Solution, cancellationToken).ConfigureAwait(false);
+ var references = symbolRefs.Single(r => r.Definition == local).Locations;
+ var syntaxRoot = await updatedDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
+
+ // Collect the topmost parenting expression for each reference.
+ var nonConflictingIdentifierNodes = references
+ .Select(loc => (IdentifierNameSyntax)syntaxRoot.FindToken(loc.Location.SourceSpan.Start).Parent)
+ .Where(ident => !HasConflict(ident, variableDeclarator));
+
+ // Add referenceAnnotions to identifier nodes being replaced.
+ updatedDocument = await updatedDocument.ReplaceNodesAsync(
+ nonConflictingIdentifierNodes,
+ (o, n) => n.WithAdditionalAnnotations(ReferenceAnnotation),
+ cancellationToken).ConfigureAwait(false);
+
+ semanticModel = await updatedDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
+ variableDeclarator = await FindDeclaratorAsync(updatedDocument, cancellationToken).ConfigureAwait(false);
+
+ // Get the annotated reference nodes.
+ nonConflictingIdentifierNodes = await FindReferenceAnnotatedNodesAsync(updatedDocument, cancellationToken).ConfigureAwait(false);
+
+ var topmostParentingExpressions = nonConflictingIdentifierNodes
+ .Select(ident => GetTopMostParentingExpression(ident))
+ .Distinct();
+
+ var originalInitializerSymbolInfo = semanticModel.GetSymbolInfo(variableDeclarator.Initializer.Value, cancellationToken);
+
+ // Make each topmost parenting statement or Equals Clause Expressions semantically explicit.
+ updatedDocument = await updatedDocument.ReplaceNodesAsync(topmostParentingExpressions, (o, n) => Simplifier.Expand(n, semanticModel, workspace, cancellationToken: cancellationToken), cancellationToken).ConfigureAwait(false);
+ semanticModel = await updatedDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
+ var semanticModelBeforeInline = semanticModel;
+
+ variableDeclarator = await FindDeclaratorAsync(updatedDocument, cancellationToken).ConfigureAwait(false);
+ var scope = GetScope(variableDeclarator);
+
+ var newScope = ReferenceRewriter.Visit(semanticModel, scope, variableDeclarator, expressionToInline, cancellationToken);
+
+ updatedDocument = await updatedDocument.ReplaceNodeAsync(scope, newScope, cancellationToken).ConfigureAwait(false);
+ semanticModel = await updatedDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
+
+ variableDeclarator = await FindDeclaratorAsync(updatedDocument, cancellationToken).ConfigureAwait(false);
+ newScope = GetScope(variableDeclarator);
+ var conflicts = newScope.GetAnnotatedNodesAndTokens(ConflictAnnotation.Kind);
+ var declaratorConflicts = variableDeclarator.GetAnnotatedNodesAndTokens(ConflictAnnotation.Kind);
+
+ // Note that we only remove the local declaration if there weren't any conflicts,
+ // unless those conflicts are inside the local declaration.
+ if (conflicts.Count() == declaratorConflicts.Count())
+ {
+ // Certain semantic conflicts can be detected only after the reference rewriter has inlined the expression
+ var newDocument = await DetectSemanticConflicts(updatedDocument,
+ semanticModel,
+ semanticModelBeforeInline,
+ originalInitializerSymbolInfo,
+ cancellationToken).ConfigureAwait(false);
+
+ if (updatedDocument == newDocument)
+ {
+ // No semantic conflicts, we can remove the definition.
+ updatedDocument = await updatedDocument.ReplaceNodeAsync(newScope, RemoveDeclaratorFromScope(variableDeclarator, newScope), cancellationToken).ConfigureAwait(false);
+ }
+ else
+ {
+ // There were some semantic conflicts, don't remove the definition.
+ updatedDocument = newDocument;
+ }
+ }
+
+ return updatedDocument;
+ }
+
+ private static async Task<VariableDeclaratorSyntax> FindDeclaratorAsync(Document document, CancellationToken cancellationToken)
+ {
+ return await FindNodeWithAnnotationAsync<VariableDeclaratorSyntax>(document, DefinitionAnnotation, cancellationToken).ConfigureAwait(false);
+ }
+
+ private static async Task<ExpressionSyntax> FindInitializerAsync(Document document, CancellationToken cancellationToken)
+ {
+ return await FindNodeWithAnnotationAsync<ExpressionSyntax>(document, InitializerAnnotation, cancellationToken).ConfigureAwait(false);
+ }
+
+ private static async Task<T> FindNodeWithAnnotationAsync<T>(Document document, SyntaxAnnotation annotation, CancellationToken cancellationToken)
+ where T : SyntaxNode
+ {
+ var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
+ return root
+ .GetAnnotatedNodesAndTokens(annotation)
+ .Single()
+ .AsNode() as T;
+ }
+
+ private static async Task<IEnumerable<IdentifierNameSyntax>> FindReferenceAnnotatedNodesAsync(Document document, CancellationToken cancellationToken)
+ {
+ var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
+ return FindReferenceAnnotatedNodes(root);
+ }
+
+ private static IEnumerable<IdentifierNameSyntax> FindReferenceAnnotatedNodes(SyntaxNode root)
+ {
+ var annotatedNodesAndTokens = root.GetAnnotatedNodesAndTokens(ReferenceAnnotation);
+ foreach (var nodeOrToken in annotatedNodesAndTokens)
+ {
+ if (nodeOrToken.IsNode && nodeOrToken.AsNode().IsKind(SyntaxKind.IdentifierName))
+ {
+ yield return (IdentifierNameSyntax)nodeOrToken.AsNode();
+ }
+ }
+ }
+
+ private SyntaxNode GetScope(VariableDeclaratorSyntax variableDeclarator)
+ {
+ var variableDeclaration = (VariableDeclarationSyntax)variableDeclarator.Parent;
+ var localDeclaration = (LocalDeclarationStatementSyntax)variableDeclaration.Parent;
+ var scope = localDeclaration.Parent;
+
+ while (scope.IsKind(SyntaxKind.LabeledStatement))
+ {
+ scope = scope.Parent;
+ }
+
+ var parentExpressions = scope.AncestorsAndSelf().OfType<ExpressionSyntax>();
+ if (parentExpressions.Any())
+ {
+ scope = parentExpressions.LastOrDefault().Parent;
+ }
+
+ return scope;
+ }
+
+ private VariableDeclaratorSyntax FindDeclarator(SyntaxNode node)
+ {
+ var annotatedNodesOrTokens = node.GetAnnotatedNodesAndTokens(DefinitionAnnotation).ToList();
+
+ return (VariableDeclaratorSyntax)annotatedNodesOrTokens.First().AsNode();
+ }
+
+ private SyntaxTriviaList GetTriviaToPreserve(SyntaxTriviaList syntaxTriviaList)
+ {
+ return ShouldPreserve(syntaxTriviaList) ? syntaxTriviaList : default(SyntaxTriviaList);
+ }
+
+ private static bool ShouldPreserve(SyntaxTriviaList trivia)
+ {
+ return trivia.Any(
+ t => t.Kind() == SyntaxKind.SingleLineCommentTrivia ||
+ t.Kind() == SyntaxKind.MultiLineCommentTrivia ||
+ t.IsDirective);
+ }
+
+ private SyntaxNode RemoveDeclaratorFromVariableList(VariableDeclaratorSyntax variableDeclarator, VariableDeclarationSyntax variableDeclaration)
+ {
+ Debug.Assert(variableDeclaration.Variables.Count > 1);
+ Debug.Assert(variableDeclaration.Variables.Contains(variableDeclarator));
+
+ var localDeclaration = (LocalDeclarationStatementSyntax)variableDeclaration.Parent;
+ var scope = GetScope(variableDeclarator);
+
+ var newLocalDeclaration = localDeclaration.RemoveNode(variableDeclarator, SyntaxRemoveOptions.KeepNoTrivia)
+ .WithAdditionalAnnotations(Formatter.Annotation);
+
+ return scope.ReplaceNode(localDeclaration, newLocalDeclaration);
+ }
+
+ private SyntaxNode RemoveDeclaratorFromScope(VariableDeclaratorSyntax variableDeclarator, SyntaxNode scope)
+ {
+ var variableDeclaration = (VariableDeclarationSyntax)variableDeclarator.Parent;
+
+ // If there is more than one variable declarator, remove this one from the variable declaration.
+ if (variableDeclaration.Variables.Count > 1)
+ {
+ return RemoveDeclaratorFromVariableList(variableDeclarator, variableDeclaration);
+ }
+
+ var localDeclaration = (LocalDeclarationStatementSyntax)variableDeclaration.Parent;
+
+ // There's only one variable declarator, so we'll remove the local declaration
+ // statement entirely. This means that we'll concatenate the leading and trailing
+ // trivia of this declaration and move it to the next statement.
+ var leadingTrivia = localDeclaration
+ .GetLeadingTrivia()
+ .Reverse()
+ .SkipWhile(t => t.MatchesKind(SyntaxKind.WhitespaceTrivia))
+ .Reverse()
+ .ToSyntaxTriviaList();
+
+ var trailingTrivia = localDeclaration
+ .GetTrailingTrivia()
+ .SkipWhile(t => t.MatchesKind(SyntaxKind.WhitespaceTrivia, SyntaxKind.EndOfLineTrivia))
+ .ToSyntaxTriviaList();
+
+ var newLeadingTrivia = leadingTrivia.Concat(trailingTrivia);
+
+ var nextToken = localDeclaration.GetLastToken().GetNextTokenOrEndOfFile();
+ var newNextToken = nextToken.WithPrependedLeadingTrivia(newLeadingTrivia)
+ .WithAdditionalAnnotations(Formatter.Annotation);
+
+ var newScope = scope.ReplaceToken(nextToken, newNextToken);
+
+ var newLocalDeclaration = (LocalDeclarationStatementSyntax)FindDeclarator(newScope).Parent.Parent;
+
+ // If the local is parented by a label statement, we can't remove this statement. Instead,
+ // we'll replace the local declaration with an empty expression statement.
+ if (newLocalDeclaration.IsParentKind(SyntaxKind.LabeledStatement))
+ {
+ var labeledStatement = (LabeledStatementSyntax)newLocalDeclaration.Parent;
+ var newLabeledStatement = labeledStatement.ReplaceNode(newLocalDeclaration, SyntaxFactory.ParseStatement(""));
+
+ return newScope.ReplaceNode(labeledStatement, newLabeledStatement);
+ }
+
+ return newScope.RemoveNode(newLocalDeclaration, SyntaxRemoveOptions.KeepNoTrivia);
+ }
+
+ private ExpressionSyntax SkipRedundantExteriorParentheses(ExpressionSyntax expression)
+ {
+ while (expression.IsKind(SyntaxKind.ParenthesizedExpression))
+ {
+ var parenthesized = (ParenthesizedExpressionSyntax)expression;
+ if (parenthesized.Expression == null ||
+ parenthesized.Expression.IsMissing)
+ {
+ break;
+ }
+
+ if (parenthesized.Expression.IsKind(SyntaxKind.ParenthesizedExpression) ||
+ parenthesized.Expression.IsKind(SyntaxKind.IdentifierName))
+ {
+ expression = parenthesized.Expression;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ return expression;
+ }
+
+ public static IEnumerable<SyntaxTrivia> SkipInitialWhitespace(SyntaxTriviaList triviaList)
+ {
+ return triviaList.SkipWhile(t => t.Kind() == SyntaxKind.WhitespaceTrivia);
+ }
+
+ private async Task<ExpressionSyntax> CreateExpressionToInlineAsync(
+ VariableDeclaratorSyntax variableDeclarator,
+ Document document,
+ CancellationToken cancellationToken)
+ {
+ var updatedDocument = document;
+
+ var expression = SkipRedundantExteriorParentheses(variableDeclarator.Initializer.Value);
+ var semanticModel = await updatedDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
+ var localSymbol = (ILocalSymbol)semanticModel.GetDeclaredSymbol(variableDeclarator, cancellationToken);
+ var newExpression = InitializerRewriter.Visit(expression, localSymbol, semanticModel);
+
+ // If this is an array initializer, we need to transform it into an array creation
+ // expression for inlining.
+ if (newExpression.Kind() == SyntaxKind.ArrayInitializerExpression)
+ {
+ var arrayType = (ArrayTypeSyntax)localSymbol.Type.GenerateTypeSyntax();
+ var arrayInitializer = (InitializerExpressionSyntax)newExpression;
+
+ // Add any non-whitespace trailing trivia from the equals clause to the type.
+ var equalsToken = variableDeclarator.Initializer.EqualsToken;
+ if (equalsToken.HasTrailingTrivia)
+ {
+ var trailingTrivia = SkipInitialWhitespace(equalsToken.TrailingTrivia);
+ if (trailingTrivia.Any())
+ {
+ arrayType = arrayType.WithTrailingTrivia(trailingTrivia);
+ }
+ }
+
+ newExpression = SyntaxFactory.ArrayCreationExpression(arrayType, arrayInitializer);
+ }
+
+ newExpression = newExpression.WithAdditionalAnnotations(InitializerAnnotation);
+
+ updatedDocument = await updatedDocument.ReplaceNodeAsync(variableDeclarator.Initializer.Value, newExpression, cancellationToken).ConfigureAwait(false);
+ semanticModel = await updatedDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
+ newExpression = await FindInitializerAsync(updatedDocument, cancellationToken).ConfigureAwait(false);
+ var newVariableDeclarator = await FindDeclaratorAsync(updatedDocument, cancellationToken).ConfigureAwait(false);
+ localSymbol = (ILocalSymbol)semanticModel.GetDeclaredSymbol(newVariableDeclarator, cancellationToken);
+
+ bool wasCastAdded;
+ var explicitCastExpression = newExpression.CastIfPossible(localSymbol.Type, newVariableDeclarator.SpanStart, semanticModel, out wasCastAdded);
+
+ if (wasCastAdded)
+ {
+ updatedDocument = await updatedDocument.ReplaceNodeAsync(newExpression, explicitCastExpression, cancellationToken).ConfigureAwait(false);
+ semanticModel = await updatedDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
+ newVariableDeclarator = await FindDeclaratorAsync(updatedDocument, cancellationToken).ConfigureAwait(false);
+ }
+
+ // Now that the variable declarator is normalized, make its initializer
+ // value semantically explicit.
+ newExpression = await Simplifier.ExpandAsync(newVariableDeclarator.Initializer.Value, updatedDocument, cancellationToken: cancellationToken).ConfigureAwait(false);
+ return newExpression.WithAdditionalAnnotations(ExpressionToInlineAnnotation);
+ }
+
+ private static SyntaxNode GetTopMostParentingExpression(ExpressionSyntax expression)
+ {
+ return expression.AncestorsAndSelf().OfType<ExpressionSyntax>().Last();
+ }
+
+ private static async Task<Document> DetectSemanticConflicts(
+ Document inlinedDocument,
+ SemanticModel newSemanticModelForInlinedDocument,
+ SemanticModel semanticModelBeforeInline,
+ SymbolInfo originalInitializerSymbolInfo,
+ CancellationToken cancellationToken)
+ {
+ // In this method we detect if inlining the expression introduced the following semantic change:
+ // The symbol info associated with any of the inlined expressions does not match the symbol info for original initializer expression prior to inline.
+
+ // If any semantic changes were introduced by inlining, we update the document with conflict annotations.
+ // Otherwise we return the given inlined document without any changes.
+
+ var syntaxRootBeforeInline = await semanticModelBeforeInline.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false);
+
+ // Get all the identifier nodes which were replaced with inlined expression.
+ var originalIdentifierNodes = FindReferenceAnnotatedNodes(syntaxRootBeforeInline).ToList ();
+
+ if (!originalIdentifierNodes.Any())
+ {
+ // No conflicts
+ return inlinedDocument;
+ }
+
+ // Get all the inlined expression nodes.
+ var syntaxRootAfterInline = await inlinedDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
+ var inlinedExprNodes = syntaxRootAfterInline.GetAnnotatedNodesAndTokens(ExpressionToInlineAnnotation).ToList ();
+ Debug.Assert(originalIdentifierNodes.Count() == inlinedExprNodes.Count());
+
+ Dictionary<SyntaxNode, SyntaxNode> replacementNodesWithChangedSemantics = null;
+ using (var originalNodesEnum = originalIdentifierNodes.GetEnumerator())
+ {
+ using (var inlinedNodesOrTokensEnum = inlinedExprNodes.GetEnumerator())
+ {
+ while (originalNodesEnum.MoveNext())
+ {
+ inlinedNodesOrTokensEnum.MoveNext();
+ var originalNode = originalNodesEnum.Current;
+
+ // expressionToInline is Parenthesized prior to replacement, so get the parenting parenthesized expression.
+ var inlinedNode = (ExpressionSyntax)inlinedNodesOrTokensEnum.Current.Parent;
+ Debug.Assert(inlinedNode.IsKind(SyntaxKind.ParenthesizedExpression));
+
+ // inlinedNode is the expanded form of the actual initializer expression in the original document.
+ // We have annotated the inner initializer with a special syntax annotation "InitializerAnnotation".
+ // Get this annotated node and compute the symbol info for this node in the inlined document.
+ var innerInitializerInInlineNodeorToken = inlinedNode.GetAnnotatedNodesAndTokens(InitializerAnnotation).First();
+
+ ExpressionSyntax innerInitializerInInlineNode = (ExpressionSyntax)(innerInitializerInInlineNodeorToken.IsNode ?
+ innerInitializerInInlineNodeorToken.AsNode() :
+ innerInitializerInInlineNodeorToken.AsToken().Parent);
+ var newInializerSymbolInfo = newSemanticModelForInlinedDocument.GetSymbolInfo(innerInitializerInInlineNode, cancellationToken);
+
+ // Verification: The symbol info associated with any of the inlined expressions does not match the symbol info for original initializer expression prior to inline.
+ if (!SpeculationAnalyzer.SymbolInfosAreCompatible(originalInitializerSymbolInfo, newInializerSymbolInfo, performEquivalenceCheck: true))
+ {
+ newInializerSymbolInfo = newSemanticModelForInlinedDocument.GetSymbolInfo(inlinedNode, cancellationToken);
+ if (!SpeculationAnalyzer.SymbolInfosAreCompatible(originalInitializerSymbolInfo, newInializerSymbolInfo, performEquivalenceCheck: true))
+ {
+ if (replacementNodesWithChangedSemantics == null)
+ {
+ replacementNodesWithChangedSemantics = new Dictionary<SyntaxNode, SyntaxNode>();
+ }
+
+ replacementNodesWithChangedSemantics.Add(inlinedNode, originalNode);
+ }
+ }
+ }
+ }
+ }
+
+ if (replacementNodesWithChangedSemantics == null)
+ {
+ // No conflicts.
+ return inlinedDocument;
+ }
+
+ // Replace the conflicting inlined nodes with the original nodes annotated with conflict annotation.
+ Func<SyntaxNode, SyntaxNode, SyntaxNode> conflictAnnotationAdder =
+ (SyntaxNode oldNode, SyntaxNode newNode) =>
+ newNode.WithAdditionalAnnotations(ConflictAnnotation.Create(GettextCatalog.GetString ("Conflict(s) detected.")));
+
+ return await inlinedDocument.ReplaceNodesAsync(replacementNodesWithChangedSemantics.Keys, conflictAnnotationAdder, cancellationToken).ConfigureAwait(false);
+ }
+ }
+}