private static T RemoveNode <T>( T declaration, Func <T, SyntaxList <MemberDeclarationSyntax> > getMembers, int index, SyntaxRemoveOptions removeOptions) where T : SyntaxNode { SyntaxList <MemberDeclarationSyntax> members = getMembers(declaration); T newDeclaration = declaration.RemoveNode(members[index], removeOptions); if (index == 0 && index < members.Count - 1) { members = getMembers(newDeclaration); MemberDeclarationSyntax nextMember = members[index]; SyntaxTriviaList leadingTrivia = nextMember.GetLeadingTrivia(); SyntaxTrivia trivia = leadingTrivia.FirstOrDefault(); if (trivia.IsEndOfLineTrivia()) { MemberDeclarationSyntax newNextMember = nextMember.WithLeadingTrivia(leadingTrivia.RemoveAt(0)); newDeclaration = newDeclaration.ReplaceNode(nextMember, newNextMember); } } return(newDeclaration); }
public static Task <Document> RefactorAsync( Document document, TypeParameterSyntax typeParameter, CancellationToken cancellationToken) { SyntaxNode node = typeParameter; var typeParameterList = (TypeParameterListSyntax)typeParameter.Parent; if (typeParameterList.Parameters.Count == 1) { node = typeParameterList; } SyntaxRemoveOptions options = SyntaxRemover.DefaultRemoveOptions; if (node.GetLeadingTrivia().All(f => f.IsWhitespaceTrivia())) { options &= ~SyntaxRemoveOptions.KeepLeadingTrivia; } if (node.GetTrailingTrivia().All(f => f.IsWhitespaceTrivia())) { options &= ~SyntaxRemoveOptions.KeepTrailingTrivia; } return(document.RemoveNodeAsync(node, options, cancellationToken)); }
internal static TRoot RemoveNodes <TRoot>(TRoot root, IEnumerable <SyntaxNode> nodes, SyntaxRemoveOptions options) where TRoot : SyntaxNode { if (nodes == null) { return(root); } var nodeArray = nodes.ToArray(); if (nodeArray.Length == 0) { return(root); } var remover = new SyntaxRemover(nodes.ToArray(), options); var result = remover.Visit(root); var residualTrivia = remover.ResidualTrivia; if (residualTrivia.Count > 0) { result = result.WithTrailingTrivia(result.GetTrailingTrivia().Concat(residualTrivia)); } return((TRoot)result); }
public SyntaxRemover(SyntaxNode[] nodes, SyntaxRemoveOptions options) { this._nodesToRemove = new HashSet <SyntaxNode>(nodes); this._options = options; this._searchSpan = ComputeTotalSpan(nodes); this._residualTrivia = null; }
/// <summary> /// Creates a new tree of nodes with the specified node removed. /// </summary> /// <typeparam name="TRoot">The type of the root node.</typeparam> /// <param name="root">The root node from which to remove a descendant node from.</param> /// <param name="node">The node to remove.</param> /// <param name="options">Options that determine how the node's trivia is treated.</param> public static TRoot RemoveNode <TRoot>(this TRoot root, SyntaxNode node, SyntaxRemoveOptions options) where TRoot : SyntaxNode { return((TRoot)root.RemoveNodesCore(new[] { node }, options)); }
public static Task <Document> RefactorAsync( Document document, BaseTypeSyntax baseType, CancellationToken cancellationToken) { SyntaxRemoveOptions removeOptions = RemoveHelper.DefaultRemoveOptions; if (baseType.GetLeadingTrivia().All(f => f.IsWhitespaceTrivia())) { removeOptions &= ~SyntaxRemoveOptions.KeepLeadingTrivia; } if (baseType.GetTrailingTrivia().All(f => f.IsWhitespaceTrivia())) { var baseList = (BaseListSyntax)baseType.Parent; if (baseList.Types.IsLast(baseType) && !GenericSyntax.HasConstraintClauses(baseList.Parent)) { removeOptions &= ~SyntaxRemoveOptions.KeepTrailingTrivia; } } return(document.RemoveNodeAsync(baseType, removeOptions, cancellationToken)); }
/// <summary> /// Creates a new tree of nodes with the specified nodes removed. /// </summary> /// <typeparam name="TRoot">The type of the root node.</typeparam> /// <param name="root">The root node from which to remove a descendant node from.</param> /// <param name="nodes">The nodes to remove.</param> /// <param name="options">Options that determine how the nodes' trivia is treated.</param> public static TRoot RemoveNodes <TRoot>( this TRoot root, IEnumerable <SyntaxNode> nodes, SyntaxRemoveOptions options) where TRoot : SyntaxNode { return((TRoot)root.RemoveNodesCore(nodes, options)); }
public SyntaxRemover(SyntaxNode[] nodesToRemove, SyntaxRemoveOptions options) : base(nodesToRemove.Any(n => n.IsPartOfStructuredTrivia())) { _nodesToRemove = new HashSet <SyntaxNode>(nodesToRemove); _options = options; _searchSpan = ComputeTotalSpan(nodesToRemove); _residualTrivia = SyntaxTriviaListBuilder.Create(); }
protected internal override SyntaxNode?RemoveNodesCore( IEnumerable <SyntaxNode> nodes, SyntaxRemoveOptions options ) { return(SyntaxNodeRemover .RemoveNodes(this, nodes.Cast <CSharpSyntaxNode>(), options) .AsRootOfNewTreeWithOptionsFrom(this.SyntaxTree)); }
private static SyntaxNode RemoveEmptyNamespaces(SyntaxNode node, SyntaxRemoveOptions removeOptions) { IEnumerable <NamespaceDeclarationSyntax> emptyNamespaces = node .DescendantNodes() .Where(f => f.IsKind(SyntaxKind.NamespaceDeclaration)) .Cast <NamespaceDeclarationSyntax>() .Where(f => !f.Members.Any()); return(node.RemoveNodes(emptyNamespaces, removeOptions)); }
/// <summary> /// Creates a new <see cref="StatementListInfo"/> with the specified node removed. /// </summary> /// <param name="node"></param> /// <param name="options"></param> /// <returns></returns> public StatementListInfo RemoveNode(SyntaxNode node, SyntaxRemoveOptions options) { ThrowInvalidOperationIfNotInitialized(); SyntaxNode parent = Parent; if (parent.Kind() == SyntaxKind.Block) { return(new StatementListInfo(((BlockSyntax)parent).RemoveNode(node, options))); } return(new StatementListInfo(((SwitchSectionSyntax)parent).RemoveNode(node, options))); }
public StatementContainer RemoveNode(SyntaxNode node, SyntaxRemoveOptions options) { if (IsBlock) { return(new StatementContainer(Block.RemoveNode(node, options))); } if (IsSwitchSection) { return(new StatementContainer(SwitchSection.RemoveNode(node, options))); } return(this); }
public StatementsInfo RemoveNode(SyntaxNode node, SyntaxRemoveOptions options) { if (IsInBlock) { return(new StatementsInfo(Block.RemoveNode(node, options))); } if (IsInSwitchSection) { return(new StatementsInfo(SwitchSection.RemoveNode(node, options))); } return(this); }
public static SyntaxNode RemoveEmptyNamespaces(SyntaxNode node, SyntaxRemoveOptions removeOptions) { if (node == null) { throw new ArgumentNullException(nameof(node)); } IEnumerable <NamespaceDeclarationSyntax> emptyNamespaces = node .DescendantNodes() .Where(f => f.IsKind(SyntaxKind.NamespaceDeclaration)) .Cast <NamespaceDeclarationSyntax>() .Where(f => !f.Members.Any()); return(node.RemoveNodes(emptyNamespaces, removeOptions)); }
public static Task<Document> RefactorAsync( Document document, ParameterSyntax parameter, CancellationToken cancellationToken) { SyntaxRemoveOptions options = RemoveHelper.DefaultRemoveOptions; if (parameter.GetLeadingTrivia().All(f => f.IsWhitespaceTrivia())) options &= ~SyntaxRemoveOptions.KeepLeadingTrivia; if (parameter.GetTrailingTrivia().All(f => f.IsWhitespaceTrivia())) options &= ~SyntaxRemoveOptions.KeepTrailingTrivia; return document.RemoveNodeAsync(parameter, options, cancellationToken); }
public static SyntaxRemoveOptions GetRemoveOptions(CSharpSyntaxNode node) { SyntaxRemoveOptions removeOptions = DefaultRemoveOptions; if (node.GetLeadingTrivia().IsEmptyOrWhitespace()) { removeOptions &= ~SyntaxRemoveOptions.KeepLeadingTrivia; } if (node.GetTrailingTrivia().IsEmptyOrWhitespace()) { removeOptions &= ~SyntaxRemoveOptions.KeepTrailingTrivia; } return(removeOptions); }
private static SyntaxRemoveOptions GetMemberRemoveOptions(MemberDeclarationSyntax member) { SyntaxRemoveOptions removeOptions = DefaultMemberRemoveOptions; if (member.GetLeadingTrivia().All(f => f.IsWhitespaceOrEndOfLineTrivia())) { removeOptions &= ~SyntaxRemoveOptions.KeepLeadingTrivia; } if (member.GetTrailingTrivia().All(f => f.IsWhitespaceOrEndOfLineTrivia())) { removeOptions &= ~SyntaxRemoveOptions.KeepTrailingTrivia; } return(removeOptions); }
internal static SyntaxRemoveOptions GetRemoveOptions(MemberDeclarationSyntax newMember) { SyntaxRemoveOptions removeOptions = DefaultRemoveOptions; if (newMember.GetLeadingTrivia().IsWhitespaceOrEndOfLine()) { removeOptions &= ~SyntaxRemoveOptions.KeepLeadingTrivia; } if (newMember.GetTrailingTrivia().IsWhitespaceOrEndOfLine()) { removeOptions &= ~SyntaxRemoveOptions.KeepTrailingTrivia; } return(removeOptions); }
private static SyntaxRemoveOptions GetRemoveOptions(StatementSyntax statement) { SyntaxRemoveOptions removeOptions = Remover.DefaultMemberRemoveOptions; if (statement.GetLeadingTrivia().All(f => f.IsWhitespaceOrEndOfLineTrivia())) { removeOptions &= ~SyntaxRemoveOptions.KeepLeadingTrivia; } if (statement.GetTrailingTrivia().All(f => f.IsWhitespaceOrEndOfLineTrivia())) { removeOptions &= ~SyntaxRemoveOptions.KeepTrailingTrivia; } return(removeOptions); }
public static SyntaxRemoveOptions GetRemoveOptions(CSharpSyntaxNode node) { SyntaxRemoveOptions removeOptions = DefaultRemoveOptions; if (node.GetLeadingTrivia().All(f => f.IsWhitespaceOrEndOfLineTrivia())) { removeOptions &= ~SyntaxRemoveOptions.KeepLeadingTrivia; } if (node.GetTrailingTrivia().All(f => f.IsWhitespaceOrEndOfLineTrivia())) { removeOptions &= ~SyntaxRemoveOptions.KeepTrailingTrivia; } return(removeOptions); }
/// <summary> /// Creates a new <see cref="MemberDeclarationListInfo"/> with the specified node removed. /// </summary> /// <param name="node"></param> /// <param name="options"></param> /// <returns></returns> public MemberDeclarationListInfo RemoveNode(SyntaxNode node, SyntaxRemoveOptions options) { ThrowInvalidOperationIfNotInitialized(); switch (Parent.Kind()) { case SyntaxKind.CompilationUnit: { var compilationUnit = (CompilationUnitSyntax)Parent; compilationUnit = compilationUnit.RemoveNode(node, options); return(new MemberDeclarationListInfo(compilationUnit, compilationUnit.Members)); } case SyntaxKind.NamespaceDeclaration: { var declaration = (NamespaceDeclarationSyntax)Parent; declaration = declaration.RemoveNode(node, options); return(new MemberDeclarationListInfo(declaration, declaration.Members)); } case SyntaxKind.ClassDeclaration: { var declaration = (ClassDeclarationSyntax)Parent; declaration = declaration.RemoveNode(node, options); return(new MemberDeclarationListInfo(declaration, declaration.Members)); } case SyntaxKind.StructDeclaration: { var declaration = (StructDeclarationSyntax)Parent; declaration = declaration.RemoveNode(node, options); return(new MemberDeclarationListInfo(declaration, declaration.Members)); } case SyntaxKind.InterfaceDeclaration: { var declaration = (InterfaceDeclarationSyntax)Parent; declaration = declaration.RemoveNode(node, options); return(new MemberDeclarationListInfo(declaration, declaration.Members)); } } throw new InvalidOperationException(); }
public static Task <Document> RefactorAsync( Document document, BaseTypeSyntax baseType, CancellationToken cancellationToken) { SyntaxRemoveOptions removeOptions = RemoveHelper.DefaultRemoveOptions; if (baseType.GetLeadingTrivia().All(f => f.IsWhitespaceTrivia())) { removeOptions &= ~SyntaxRemoveOptions.KeepLeadingTrivia; } if (baseType.GetTrailingTrivia().All(f => f.IsWhitespaceTrivia())) { removeOptions &= ~SyntaxRemoveOptions.KeepTrailingTrivia; } return(document.RemoveNodeAsync(baseType, removeOptions, cancellationToken)); }
internal static SyntaxRemoveOptions GetRemoveOptions(UsingDirectiveSyntax usingDirective) { SyntaxRemoveOptions removeOptions = SyntaxRemoveOptions.KeepExteriorTrivia | SyntaxRemoveOptions.KeepUnbalancedDirectives; if (!usingDirective.HasLeadingTrivia) { removeOptions &= ~SyntaxRemoveOptions.KeepLeadingTrivia; } SyntaxTriviaList trailingTrivia = usingDirective.GetTrailingTrivia(); if (trailingTrivia.Count == 1 && trailingTrivia.First().IsEndOfLineTrivia()) { removeOptions &= ~SyntaxRemoveOptions.KeepTrailingTrivia; } return(removeOptions); }
public static async Task <Document> RemoveNodesAsync( this Document document, IEnumerable <SyntaxNode> nodes, SyntaxRemoveOptions options, CancellationToken cancellationToken = default(CancellationToken)) { if (document == null) { throw new ArgumentNullException(nameof(document)); } if (nodes == null) { throw new ArgumentNullException(nameof(nodes)); } SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); SyntaxNode newRoot = root.RemoveNodes(nodes, options); return(document.WithSyntaxRoot(newRoot)); }
/// <summary> /// Creates a new <see cref="UsingDirectiveListInfo"/> with the specified node removed. /// </summary> /// <param name="node"></param> /// <param name="options"></param> /// <returns></returns> public UsingDirectiveListInfo RemoveNode(SyntaxNode node, SyntaxRemoveOptions options) { ThrowInvalidOperationIfNotInitialized(); switch (Parent.Kind()) { case SyntaxKind.CompilationUnit: { var declaration = (CompilationUnitSyntax)Parent; declaration = declaration.RemoveNode(node, options); return(new UsingDirectiveListInfo(declaration, declaration.Usings)); } case SyntaxKind.NamespaceDeclaration: { var declaration = (NamespaceDeclarationSyntax)Parent; declaration = declaration.RemoveNode(node, options); return(new UsingDirectiveListInfo(declaration, declaration.Usings)); } } throw new InvalidOperationException(); }
private static async Task <StatementListInfo> RefactorAsync <TStatement>( Document document, TStatement statement, StatementListInfo statementsInfo, Func <TStatement, TStatement> createNewStatement, int count, bool removeReturnStatement, CancellationToken cancellationToken) where TStatement : StatementSyntax { int statementIndex = statementsInfo.IndexOf(statement); var returnStatement = (ReturnStatementSyntax)statementsInfo[statementIndex + 1]; ExpressionSyntax returnExpression = returnStatement.Expression; ExpressionSyntax newReturnExpression = null; SyntaxTriviaList newTrailingTrivia = default; SemanticModel semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); ISymbol symbol = semanticModel.GetSymbol(returnExpression, cancellationToken); if (symbol.Kind == SymbolKind.Local && statementIndex > 0 && statementsInfo[statementIndex - 1] is LocalDeclarationStatementSyntax localDeclarationStatement && !localDeclarationStatement.ContainsDiagnostics && !localDeclarationStatement.SpanOrTrailingTriviaContainsDirectives() && !statement.GetLeadingTrivia().Any(f => f.IsDirective)) { SeparatedSyntaxList <VariableDeclaratorSyntax> declarators = localDeclarationStatement.Declaration.Variables; VariableDeclaratorSyntax declarator = declarators.FirstOrDefault(f => semanticModel.GetDeclaredSymbol(f, cancellationToken)?.Equals(symbol) == true); if (declarator != null) { ExpressionSyntax value = declarator.Initializer?.Value; if (removeReturnStatement || value != null) { IEnumerable <ReferencedSymbol> referencedSymbols = await SymbolFinder.FindReferencesAsync(symbol, document.Solution(), cancellationToken).ConfigureAwait(false); if (referencedSymbols.First().Locations.Count() == count + 1) { newReturnExpression = value; if (declarators.Count == 1) { if (!removeReturnStatement && returnStatement.GetTrailingTrivia().IsEmptyOrWhitespace()) { SyntaxTriviaList trailingTrivia = localDeclarationStatement.GetTrailingTrivia(); if (trailingTrivia .SkipWhile(f => f.IsWhitespaceTrivia()) .FirstOrDefault() .IsKind(SyntaxKind.SingleLineCommentTrivia)) { newTrailingTrivia = trailingTrivia; } } SyntaxRemoveOptions removeOptions = SyntaxRefactorings.GetRemoveOptions(localDeclarationStatement); if (newTrailingTrivia.Any()) { removeOptions &= ~SyntaxRemoveOptions.KeepTrailingTrivia; } statementsInfo = statementsInfo.RemoveNode(localDeclarationStatement, removeOptions); statementIndex--; } else { statementsInfo = statementsInfo.ReplaceNode(localDeclarationStatement, localDeclarationStatement.RemoveNode(declarator, SyntaxRefactorings.GetRemoveOptions(declarator))); } returnStatement = (ReturnStatementSyntax)statementsInfo[statementIndex + 1]; } } } } if (removeReturnStatement) { statementsInfo = statementsInfo.RemoveNode(returnStatement, SyntaxRefactorings.GetRemoveOptions(returnStatement)); } else if (newReturnExpression != null) { ReturnStatementSyntax newReturnStatement = returnStatement.WithExpression(newReturnExpression.WithTriviaFrom(returnExpression)); if (newTrailingTrivia.Any()) { newReturnStatement = newReturnStatement.WithTrailingTrivia(newTrailingTrivia); } statementsInfo = statementsInfo.ReplaceNode(returnStatement, newReturnStatement); } StatementSyntax oldNode = statementsInfo[statementIndex]; TStatement newNode = createNewStatement((TStatement)oldNode).WithFormatterAnnotation(); return(statementsInfo.ReplaceNode(oldNode, newNode)); }
/// <summary> /// Removes child nodes. /// </summary> /// <param name="nodes"></param> /// <param name="options"></param> /// <returns></returns> protected internal override SyntaxNode?RemoveNodesCore(IEnumerable <SyntaxNode> nodes, SyntaxRemoveOptions options) => SyntaxNodeRemover.RemoveNodes(this, nodes.Cast <LuaSyntaxNode>(), options).AsRootOfNewTreeWithOptionsFrom(SyntaxTree);
private async Task <Solution> ProcessResult(CodeFixContext context, Diagnostic diagnostic, CancellationToken cancellationToken) { var locations = diagnostic.AdditionalLocations; var propertyLocation = locations[0]; var declaratorLocation = locations[1]; var declarator = declaratorLocation.FindToken(cancellationToken).Parent.FirstAncestorOrSelf <TVariableDeclarator>(); var fieldDocument = context.Document.Project.GetDocument(declarator.SyntaxTree); var fieldSemanticModel = await fieldDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var fieldSymbol = (IFieldSymbol)fieldSemanticModel.GetDeclaredSymbol(declarator); var property = propertyLocation.FindToken(cancellationToken).Parent.FirstAncestorOrSelf <TPropertyDeclaration>(); var propertyDocument = context.Document.Project.GetDocument(property.SyntaxTree); var propertySemanticModel = await propertyDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var propertySymbol = (IPropertySymbol)propertySemanticModel.GetDeclaredSymbol(property); Debug.Assert(fieldDocument.Project == propertyDocument.Project); var project = fieldDocument.Project; var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); var solution = context.Document.Project.Solution; var fieldLocations = await Renamer.GetRenameLocationsAsync( solution, SymbolAndProjectId.Create(fieldSymbol, fieldDocument.Project.Id), solution.Options, cancellationToken).ConfigureAwait(false); // First, create the updated property we want to replace the old property with var isWrittenToOutsideOfConstructor = IsWrittenToOutsideOfConstructorOrProperty(fieldSymbol, fieldLocations, property, cancellationToken); var updatedProperty = await UpdatePropertyAsync(propertyDocument, compilation, fieldSymbol, propertySymbol, property, isWrittenToOutsideOfConstructor, cancellationToken).ConfigureAwait(false); // Now, rename all usages of the field to point at the property. Except don't actually // rename the field itself. We want to be able to find it again post rename. var updatedSolution = await Renamer.RenameAsync(fieldLocations, propertySymbol.Name, location => !location.SourceSpan.IntersectsWith(declaratorLocation.SourceSpan), symbols => HasConflict(symbols, propertySymbol, compilation, cancellationToken), cancellationToken).ConfigureAwait(false); solution = updatedSolution; // Now find the field and property again post rename. fieldDocument = solution.GetDocument(fieldDocument.Id); propertyDocument = solution.GetDocument(propertyDocument.Id); Debug.Assert(fieldDocument.Project == propertyDocument.Project); compilation = await fieldDocument.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); fieldSymbol = (IFieldSymbol)fieldSymbol.GetSymbolKey().Resolve(compilation, cancellationToken: cancellationToken).Symbol; propertySymbol = (IPropertySymbol)propertySymbol.GetSymbolKey().Resolve(compilation, cancellationToken: cancellationToken).Symbol; Debug.Assert(fieldSymbol != null && propertySymbol != null); declarator = (TVariableDeclarator)await fieldSymbol.DeclaringSyntaxReferences[0].GetSyntaxAsync(cancellationToken).ConfigureAwait(false); var temp = await propertySymbol.DeclaringSyntaxReferences[0].GetSyntaxAsync(cancellationToken).ConfigureAwait(false); property = temp.FirstAncestorOrSelf <TPropertyDeclaration>(); var nodeToRemove = GetNodeToRemove(declarator); const SyntaxRemoveOptions options = SyntaxRemoveOptions.KeepUnbalancedDirectives | SyntaxRemoveOptions.AddElasticMarker; if (fieldDocument == propertyDocument) { // Same file. Have to do this in a slightly complicated fashion. var declaratorTreeRoot = await fieldDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var editor = new SyntaxEditor(declaratorTreeRoot, fieldDocument.Project.Solution.Workspace); editor.RemoveNode(nodeToRemove, options); editor.ReplaceNode(property, updatedProperty); var newRoot = editor.GetChangedRoot(); newRoot = await FormatAsync(newRoot, fieldDocument, cancellationToken).ConfigureAwait(false); return(solution.WithDocumentSyntaxRoot( fieldDocument.Id, newRoot)); } else { // In different files. Just update both files. var fieldTreeRoot = await fieldDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var propertyTreeRoot = await propertyDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var newFieldTreeRoot = fieldTreeRoot.RemoveNode(nodeToRemove, options); var newPropertyTreeRoot = propertyTreeRoot.ReplaceNode(property, updatedProperty); newFieldTreeRoot = await FormatAsync(newFieldTreeRoot, fieldDocument, cancellationToken).ConfigureAwait(false); newPropertyTreeRoot = await FormatAsync(newPropertyTreeRoot, propertyDocument, cancellationToken).ConfigureAwait(false); updatedSolution = solution.WithDocumentSyntaxRoot(fieldDocument.Id, newFieldTreeRoot); updatedSolution = updatedSolution.WithDocumentSyntaxRoot(propertyDocument.Id, newPropertyTreeRoot); return(updatedSolution); } }
private async Task <Solution> ProcessResultAsync(CodeFixContext context, Diagnostic diagnostic, CancellationToken cancellationToken) { var locations = diagnostic.AdditionalLocations; var propertyLocation = locations[0]; var declaratorLocation = locations[1]; var declarator = declaratorLocation.FindToken(cancellationToken).Parent.FirstAncestorOrSelf <TVariableDeclarator>(); var fieldDocument = context.Document.Project.GetDocument(declarator.SyntaxTree); var fieldSemanticModel = await fieldDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var fieldSymbol = (IFieldSymbol)fieldSemanticModel.GetDeclaredSymbol(declarator); var property = propertyLocation.FindToken(cancellationToken).Parent.FirstAncestorOrSelf <TPropertyDeclaration>(); var propertyDocument = context.Document.Project.GetDocument(property.SyntaxTree); var propertySemanticModel = await propertyDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var propertySymbol = (IPropertySymbol)propertySemanticModel.GetDeclaredSymbol(property); Debug.Assert(fieldDocument.Project == propertyDocument.Project); var project = fieldDocument.Project; var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); var solution = context.Document.Project.Solution; var fieldLocations = await Renamer.GetRenameLocationsAsync( solution, SymbolAndProjectId.Create(fieldSymbol, fieldDocument.Project.Id), solution.Options, cancellationToken).ConfigureAwait(false); // First, create the updated property we want to replace the old property with var isWrittenToOutsideOfConstructor = IsWrittenToOutsideOfConstructorOrProperty(fieldSymbol, fieldLocations, property, cancellationToken); var updatedProperty = await UpdatePropertyAsync(propertyDocument, compilation, fieldSymbol, propertySymbol, property, isWrittenToOutsideOfConstructor, cancellationToken).ConfigureAwait(false); // Note: rename will try to update all the references in linked files as well. However, // this can lead to some very bad behavior as we will change the references in linked files // but only remove the field and update the property in a single document. So, you can // end in the state where you do this in one of the linked file: // // int Prop { get { return this.field; } } => int Prop { get { return this.Prop } } // // But in the main file we'll replace: // // int Prop { get { return this.field; } } => int Prop { get; } // // The workspace will see these as two irreconcilable edits. To avoid this, we disallow // any edits to the other links for the files containing the field and property. i.e. // rename will only be allowed to edit the exact same doc we're removing the field from // and the exact doc we're updating hte property in. It can't touch the other linked // files for those docs. (It can of course touch any other documents unrelated to the // docs that the field and prop are declared in). var linkedFiles = new HashSet <DocumentId>(); linkedFiles.AddRange(fieldDocument.GetLinkedDocumentIds()); linkedFiles.AddRange(propertyDocument.GetLinkedDocumentIds()); var canEdit = new Dictionary <SyntaxTree, bool>(); // Now, rename all usages of the field to point at the property. Except don't actually // rename the field itself. We want to be able to find it again post rename. var updatedSolution = await Renamer.RenameAsync(fieldLocations, propertySymbol.Name, location => !location.SourceSpan.IntersectsWith(declaratorLocation.SourceSpan) && CanEditDocument(solution, location.SourceTree, linkedFiles, canEdit), symbols => HasConflict(symbols, propertySymbol, compilation, cancellationToken), cancellationToken).ConfigureAwait(false); solution = updatedSolution; // Now find the field and property again post rename. fieldDocument = solution.GetDocument(fieldDocument.Id); propertyDocument = solution.GetDocument(propertyDocument.Id); Debug.Assert(fieldDocument.Project == propertyDocument.Project); compilation = await fieldDocument.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); fieldSymbol = (IFieldSymbol)fieldSymbol.GetSymbolKey().Resolve(compilation, cancellationToken: cancellationToken).Symbol; propertySymbol = (IPropertySymbol)propertySymbol.GetSymbolKey().Resolve(compilation, cancellationToken: cancellationToken).Symbol; Debug.Assert(fieldSymbol != null && propertySymbol != null); declarator = (TVariableDeclarator)await fieldSymbol.DeclaringSyntaxReferences[0].GetSyntaxAsync(cancellationToken).ConfigureAwait(false); var temp = await propertySymbol.DeclaringSyntaxReferences[0].GetSyntaxAsync(cancellationToken).ConfigureAwait(false); property = temp.FirstAncestorOrSelf <TPropertyDeclaration>(); var nodeToRemove = GetNodeToRemove(declarator); const SyntaxRemoveOptions options = SyntaxRemoveOptions.KeepUnbalancedDirectives | SyntaxRemoveOptions.AddElasticMarker; if (fieldDocument == propertyDocument) { // Same file. Have to do this in a slightly complicated fashion. var declaratorTreeRoot = await fieldDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var editor = new SyntaxEditor(declaratorTreeRoot, fieldDocument.Project.Solution.Workspace); editor.RemoveNode(nodeToRemove, options); editor.ReplaceNode(property, updatedProperty); var newRoot = editor.GetChangedRoot(); newRoot = await FormatAsync(newRoot, fieldDocument, cancellationToken).ConfigureAwait(false); return(solution.WithDocumentSyntaxRoot( fieldDocument.Id, newRoot)); } else { // In different files. Just update both files. var fieldTreeRoot = await fieldDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var propertyTreeRoot = await propertyDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var newFieldTreeRoot = fieldTreeRoot.RemoveNode(nodeToRemove, options); var newPropertyTreeRoot = propertyTreeRoot.ReplaceNode(property, updatedProperty); newFieldTreeRoot = await FormatAsync(newFieldTreeRoot, fieldDocument, cancellationToken).ConfigureAwait(false); newPropertyTreeRoot = await FormatAsync(newPropertyTreeRoot, propertyDocument, cancellationToken).ConfigureAwait(false); updatedSolution = solution.WithDocumentSyntaxRoot(fieldDocument.Id, newFieldTreeRoot); updatedSolution = updatedSolution.WithDocumentSyntaxRoot(propertyDocument.Id, newPropertyTreeRoot); return(updatedSolution); } }
public RemoveChange(SyntaxNode node, SyntaxRemoveOptions options) : base(node) { _options = options; }
/// <summary> /// Remove the node from the tree. /// </summary> /// <param name="node">The node to remove that currently exists as part of the tree.</param> /// <param name="options">Options that affect how node removal works.</param> public void RemoveNode(SyntaxNode node, SyntaxRemoveOptions options) { CheckNodeInTree(node); _changes.Add(new RemoveChange(node, options)); }
protected internal override SyntaxNode RemoveNodesCore(IEnumerable <SyntaxNode> nodes, SyntaxRemoveOptions options) { return(SyntaxNodeRemover.RemoveNodes(this, nodes.Cast <CSharpSyntaxNode>(), options)); }