public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles) { return; } var span = context.Span; if (!span.IsEmpty) { return; } var cancellationToken = context.CancellationToken; if (cancellationToken.IsCancellationRequested) { return; } var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (model.IsFromGeneratedCode(cancellationToken)) { return; } var root = await model.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); SyntaxToken token = root.FindToken(span.Start); if (!token.IsKind(SyntaxKind.IdentifierToken)) { return; } var property = token.Parent as PropertyDeclarationSyntax; if (property == null || !property.Identifier.Span.Contains(span)) { return; } if (IsEmptyComputedProperty(property)) { context.RegisterRefactoring( CodeActionFactory.Create( token.Span, DiagnosticSeverity.Info, GettextCatalog.GetString("Convert to auto-property"), t2 => { var newRoot = root.ReplaceNode(property, CreateNewProperty(property).WithAdditionalAnnotations(Formatter.Annotation).WithLeadingTrivia(property.GetLeadingTrivia())); return(Task.FromResult(document.WithSyntaxRoot(newRoot))); } ) ); return; } var field = GetBackingField(model, property); if (!IsValidField(field, property.Parent as TypeDeclarationSyntax)) { return; } //variable declarator->declaration->field declaration var backingFieldNode = root.FindNode(field.Locations.First().SourceSpan).Ancestors().OfType <FieldDeclarationSyntax>().First(); var propertyAnnotation = new SyntaxAnnotation(); var fieldAnnotation = new SyntaxAnnotation(); //annotate our property node and our field node root = root.ReplaceNode((SyntaxNode)property, property.WithAdditionalAnnotations(propertyAnnotation)); root = root.ReplaceNode((SyntaxNode)root.FindNode(backingFieldNode.Span), backingFieldNode.WithAdditionalAnnotations(fieldAnnotation)); context.RegisterRefactoring( CodeActionFactory.Create(token.Span, DiagnosticSeverity.Info, GettextCatalog.GetString("Convert to auto-property"), PerformAction(document, model, root, field.Name, CreateNewProperty(property), propertyAnnotation, fieldAnnotation)) ); }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles) { return; } var span = context.Span; if (!span.IsEmpty) { return; } var cancellationToken = context.CancellationToken; if (cancellationToken.IsCancellationRequested) { return; } var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (model.IsFromGeneratedCode(cancellationToken)) { return; } var root = await model.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); var token = root.FindToken(span.Start); string keyword; StatementSyntax embeddedStatement; BlockSyntax block = null; if (IsSpecialNode(token, out keyword, out embeddedStatement)) { block = embeddedStatement as BlockSyntax; if (block == null || block.Statements.Count != 1 || block.Statements.First() is LabeledStatementSyntax || block.Statements.First() is LocalDeclarationStatementSyntax) { return; } } if (block == null) { return; } context.RegisterRefactoring( CodeActionFactory.Create( token.Span, DiagnosticSeverity.Info, string.Format(GettextCatalog.GetString("Remove braces from '{0}'"), keyword), t2 => { var parent = block.Parent.ReplaceNode((SyntaxNode)block, block.Statements.First()) .WithAdditionalAnnotations(Formatter.Annotation); var newRoot = root.ReplaceNode((SyntaxNode)block.Parent, parent); return(Task.FromResult(document.WithSyntaxRoot(newRoot))); } ) ); }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles) { return; } var span = context.Span; if (!span.IsEmpty) { return; } var cancellationToken = context.CancellationToken; if (cancellationToken.IsCancellationRequested) { return; } var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (model.IsFromGeneratedCode(cancellationToken)) { return; } var root = await model.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); var property = root.FindNode(span) as PropertyStatementSyntax; if (property == null || !property.Identifier.Span.Contains(span)) { return; } var propertyBlock = property.Parent as PropertyBlockSyntax; if (propertyBlock == null) { return; } var field = GetBackingField(model, propertyBlock); if (field == null) { return; } var type = propertyBlock.Parent as TypeBlockSyntax; if (type == null) { return; } var resolvedType = model.Compilation.GetTypeSymbol("System", "EventHandler", 0, cancellationToken); if (resolvedType == null) { return; } context.RegisterRefactoring( CodeActionFactory.Create( span, DiagnosticSeverity.Info, GettextCatalog.GetString("Create changed event"), t2 => { var eventDeclaration = CreateChangedEventDeclaration(propertyBlock); var methodDeclaration = CreateEventInvocator( type.BlockStatement.Identifier.ToString(), type.BlockStatement.Modifiers.Any(m => m.IsKind(SyntaxKind.NotInheritableKeyword)), eventDeclaration.Modifiers.Any(m => m.IsKind(SyntaxKind.SharedKeyword)), eventDeclaration.Identifier.ToString(), resolvedType.GetDelegateInvokeMethod(), false ); var invocation = SyntaxFactory.ExpressionStatement(SyntaxFactory.InvocationExpression( SyntaxFactory.IdentifierName(methodDeclaration.SubOrFunctionStatement.Identifier.ToString()), SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList <ArgumentSyntax>(new[] { SyntaxFactory.SimpleArgument(SyntaxFactory.ParseExpression("System.EventArgs.Empty").WithAdditionalAnnotations(Simplifier.Annotation)) })) )); var marker = new SyntaxAnnotation(); var newPropertyBlockSetterAccessor = propertyBlock.Accessors.First(a => a.IsKind(SyntaxKind.SetAccessorBlock)); newPropertyBlockSetterAccessor = newPropertyBlockSetterAccessor.WithStatements( newPropertyBlockSetterAccessor.Statements.Add(invocation.WithAdditionalAnnotations(Formatter.Annotation, Simplifier.Annotation))); var newPropertyBlock = propertyBlock.WithAccessors( SyntaxFactory.List <AccessorBlockSyntax>(new[] { propertyBlock.Accessors.First(a => a.IsKind(SyntaxKind.GetAccessorBlock)), newPropertyBlockSetterAccessor })); var newRoot = root.ReplaceNode(propertyBlock, new SyntaxNode[] { newPropertyBlock, methodDeclaration.WithAdditionalAnnotations(Formatter.Annotation), eventDeclaration.WithTrailingTrivia(propertyBlock.GetTrailingTrivia()).WithAdditionalAnnotations(Formatter.Annotation) }); return(Task.FromResult(document.WithSyntaxRoot(newRoot))); }) ); }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles) { return; } var span = context.Span; if (!span.IsEmpty) { return; } var cancellationToken = context.CancellationToken; if (cancellationToken.IsCancellationRequested) { return; } var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (model.IsFromGeneratedCode(cancellationToken)) { return; } var root = await model.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); var token = root.FindToken(span.Start); if (!token.IsIdentifierOrAccessorOrAccessibilityModifier()) { return; } var node = token.Parent; while (node != null && !(node is MemberDeclarationSyntax || node is AccessorDeclarationSyntax)) { node = node.Parent; } if (node == null || node.IsKind(SyntaxKind.InterfaceDeclaration, SyntaxKind.EnumMemberDeclaration)) { return; } ISymbol symbol = null; var field = node as FieldDeclarationSyntax; if (field != null) { symbol = model.GetDeclaredSymbol(field.Declaration.Variables.First(), cancellationToken); } else { var member = node as MemberDeclarationSyntax; if (member != null) { symbol = model.GetDeclaredSymbol(member, cancellationToken); } else { var accessor = node as AccessorDeclarationSyntax; if (accessor != null) { symbol = model.GetDeclaredSymbol(accessor, cancellationToken); } } } if (!symbol.AccessibilityChangeable()) { return; } foreach (var accessibility in GetPossibleAccessibilities(model, symbol, node, cancellationToken)) { var modifiers = GetAccessibilityModifiers(accessibility); context.RegisterRefactoring(CodeActionFactory.Create( token.Span, DiagnosticSeverity.Info, GettextCatalog.GetString("To " + String.Join(" ", modifiers)), t => { var newRoot = root.ReplaceNode( node, node.WithoutLeadingTrivia().WithModifiers(modifiers).WithLeadingTrivia(node.GetLeadingTrivia())); return(Task.FromResult(document.WithSyntaxRoot(newRoot))); })); } }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles) { return; } var span = context.Span; if (!span.IsEmpty) { return; } var cancellationToken = context.CancellationToken; if (cancellationToken.IsCancellationRequested) { return; } var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (model.IsFromGeneratedCode(cancellationToken)) { return; } var root = await model.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); var token = root.FindToken(span.Start); var node = token.Parent as AssignmentExpressionSyntax; if (node == null || !node.OperatorToken.Span.Contains(span)) { return; } var updatedNode = ReplaceWithOperatorAssignmentCodeRefactoringProvider.CreateAssignment(node) ?? node; if ((!updatedNode.IsKind(SyntaxKind.AddAssignmentExpression) && !updatedNode.IsKind(SyntaxKind.SubtractAssignmentExpression))) { return; } var rightLiteral = updatedNode.Right as LiteralExpressionSyntax; if (rightLiteral == null || ((int)rightLiteral.Token.Value) != 1) { return; } context.RegisterRefactoring( CodeActionFactory.Create( token.Span, DiagnosticSeverity.Info, updatedNode.IsKind(SyntaxKind.AddAssignmentExpression) ? GettextCatalog.GetString("To '{0}++'") : GettextCatalog.GetString("To '{0}--'"), t2 => { var newNode = SyntaxFactory.PostfixUnaryExpression(updatedNode.IsKind(SyntaxKind.AddAssignmentExpression) ? SyntaxKind.PostIncrementExpression : SyntaxKind.PostDecrementExpression, updatedNode.Left); var newRoot = root.ReplaceNode((SyntaxNode)node, newNode.WithAdditionalAnnotations(Formatter.Annotation).WithLeadingTrivia(node.GetLeadingTrivia())); return(Task.FromResult(document.WithSyntaxRoot(newRoot))); } ) ); }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles) { return; } var span = context.Span; if (!span.IsEmpty) { return; } var cancellationToken = context.CancellationToken; if (cancellationToken.IsCancellationRequested) { return; } var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (model.IsFromGeneratedCode(cancellationToken)) { return; } var node = root.FindNode(span) as IfStatementSyntax; if (node == null) { return; } var switchExpr = ConvertIfStatementToSwitchStatementAnalyzer.GetSwitchExpression(model, node.Condition); if (switchExpr == null) { return; } var switchSections = new List <SwitchSectionSyntax>(); if (!ConvertIfStatementToSwitchStatementAnalyzer.CollectSwitchSections(switchSections, model, node, switchExpr)) { return; } context.RegisterRefactoring( CodeActionFactory.Create( span, DiagnosticSeverity.Info, GettextCatalog.GetString("To 'switch'"), ct => { var switchStatement = SyntaxFactory.SwitchStatement(switchExpr, new SyntaxList <SwitchSectionSyntax>().AddRange(switchSections)); return(Task.FromResult(document.WithSyntaxRoot(root.ReplaceNode( (SyntaxNode)node, switchStatement .WithLeadingTrivia(node.GetLeadingTrivia()) .WithAdditionalAnnotations(Formatter.Annotation))))); }) ); }
protected override IEnumerable <CodeAction> GetActions(Document document, SemanticModel semanticModel, SyntaxNode root, TextSpan span, ParameterSyntax node, CancellationToken cancellationToken) { if (!node.Identifier.Span.Contains(span)) { return(Enumerable.Empty <CodeAction>()); } var parameter = node; var bodyStatement = parameter.Parent.Parent.Parent as MethodBlockBaseSyntax; var lambdaBody = parameter.Parent.Parent.Parent as MultiLineLambdaExpressionSyntax; if (bodyStatement == null && lambdaBody == null) { return(Enumerable.Empty <CodeAction>()); } var parameterSymbol = semanticModel.GetDeclaredSymbol(node); var type = parameterSymbol.Type; if (type == null || type.IsValueType || HasNullCheck(semanticModel, parameterSymbol, (SyntaxNode)bodyStatement ?? lambdaBody)) { return(Enumerable.Empty <CodeAction>()); } return(new[] { CodeActionFactory.Create( node.Identifier.Span, DiagnosticSeverity.Info, GettextCatalog.GetString("Add 'Is Nothing' check for parameter"), t2 => { var paramName = node.Identifier.ToString(); ExpressionSyntax parameterExpr; var parseOptions = root.SyntaxTree.Options as VisualBasicParseOptions; if (parseOptions != null && parseOptions.LanguageVersion < LanguageVersion.VisualBasic14) { parameterExpr = SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(paramName)); } else { parameterExpr = SyntaxFactory.ParseExpression("NameOf(" + paramName + ")"); } var ifStatement = SyntaxFactory.IfStatement(SyntaxFactory.IsExpression(SyntaxFactory.IdentifierName(paramName), SyntaxFactory.Token(SyntaxKind.IsKeyword), SyntaxFactory.NothingLiteralExpression(SyntaxFactory.Token(SyntaxKind.NothingKeyword)))) .WithThenKeyword(SyntaxFactory.Token(SyntaxKind.ThenKeyword)); var statements = SyntaxFactory.SingletonList <StatementSyntax>(SyntaxFactory.ThrowStatement( SyntaxFactory.ObjectCreationExpression( SyntaxFactory.ParseTypeName("System.ArgumentNullException") ) .WithArgumentList(SyntaxFactory.ArgumentList( SyntaxFactory.SingletonSeparatedList <ArgumentSyntax>( SyntaxFactory.SimpleArgument( parameterExpr ) ) )) )); var ifBlock = SyntaxFactory.MultiLineIfBlock(ifStatement, statements, default(SyntaxList <ElseIfBlockSyntax>), null) .WithAdditionalAnnotations(Formatter.Annotation, Simplifier.Annotation); SyntaxNode newRoot; if (bodyStatement != null) { var newStatements = bodyStatement.Statements.Insert(0, ifBlock); var newBody = bodyStatement.WithStatements(newStatements); newRoot = root.ReplaceNode(bodyStatement, newBody); } else { var newStatements = lambdaBody.Statements.Insert(0, ifBlock); var newBody = lambdaBody.WithStatements(newStatements); newRoot = root.ReplaceNode(lambdaBody, newBody); } return Task.FromResult(document.WithSyntaxRoot(newRoot)); } ) }); }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles) { return; } var span = context.Span; if (!span.IsEmpty) { return; } var cancellationToken = context.CancellationToken; if (cancellationToken.IsCancellationRequested) { return; } var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (model.IsFromGeneratedCode(cancellationToken)) { return; } var root = await model.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); var token = root.FindToken(span.Start); var node = token.Parent; if (node.IsKind(SyntaxKind.IdentifierName) && node.SpanStart == span.Start) { node = node.Parent; if (node.Parent is ConditionalExpressionSyntax && node.Parent.SpanStart == span.Start && ((ConditionalExpressionSyntax)node.Parent).Condition == node) { node = node.Parent; } } var condExpr = node as ConditionalExpressionSyntax; if (condExpr == null) { return; } context.RegisterRefactoring( CodeActionFactory.Create( token.Span, DiagnosticSeverity.Info, GettextCatalog.GetString("Invert '?:'"), t2 => { var newRoot = root.ReplaceNode((SyntaxNode) condExpr, condExpr .WithCondition(CSharpUtil.InvertCondition(condExpr.Condition)) .WithWhenTrue(condExpr.WhenFalse) .WithWhenFalse(condExpr.WhenTrue) .WithAdditionalAnnotations(Formatter.Annotation) ); return(Task.FromResult(document.WithSyntaxRoot(newRoot))); } ) ); }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles) { return; } var span = context.Span; if (!span.IsEmpty) { return; } var cancellationToken = context.CancellationToken; if (cancellationToken.IsCancellationRequested) { return; } var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (model.IsFromGeneratedCode(cancellationToken)) { return; } var root = await model.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); var token = root.FindToken(span.Start); if (!(token.Parent is VariableDeclaratorSyntax)) { return; } var declarator = (VariableDeclaratorSyntax)token.Parent; var fieldDeclaration = declarator.Parent?.Parent as FieldDeclarationSyntax; if (fieldDeclaration == null) { return; } var enclosingType = fieldDeclaration.Parent as TypeDeclarationSyntax; if (enclosingType == null) { return; } foreach (var member in enclosingType.Members) { if (member is PropertyDeclarationSyntax && ContainsGetter(model, (PropertyDeclarationSyntax)member, declarator)) { return; } } context.RegisterRefactoring( CodeActionFactory.Create( token.Span, DiagnosticSeverity.Info, GettextCatalog.GetString("Generate getter"), t2 => { var newRoot = root.InsertNodesAfter(fieldDeclaration, new[] { GeneratePropertyDeclaration(fieldDeclaration, declarator) }); return(Task.FromResult(document.WithSyntaxRoot(newRoot))); } ) ); }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; var span = context.Span; var cancellationToken = context.CancellationToken; var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (model.IsFromGeneratedCode(cancellationToken)) { return; } var root = await model.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); var token = root.FindToken(span.Start); if (!token.IsKind(SyntaxKind.IdentifierToken, SyntaxKind.AbstractKeyword, SyntaxKind.VirtualKeyword, SyntaxKind.ThisKeyword)) { return; } MemberDeclarationSyntax declaration; ISymbol symbol = null; if (token.IsKind(SyntaxKind.IdentifierToken)) { if (token.Parent?.Parent?.IsKind(SyntaxKind.VariableDeclaration) == true) { declaration = token.Parent?.Parent?.Parent as MemberDeclarationSyntax; symbol = model.GetDeclaredSymbol(token.Parent); } else { declaration = token.Parent as MemberDeclarationSyntax; if (declaration != null) { symbol = model.GetDeclaredSymbol(declaration); } } } else { declaration = token.Parent as MemberDeclarationSyntax; if (declaration != null) { symbol = model.GetDeclaredSymbol(declaration); } } if (declaration == null || symbol == null || declaration is BaseTypeDeclarationSyntax || declaration is ConstructorDeclarationSyntax || declaration is DestructorDeclarationSyntax) { return; } var modifiers = declaration.GetModifiers(); if (modifiers.Any(m => m.IsKind(SyntaxKind.OverrideKeyword, SyntaxKind.ExternKeyword))) { return; } var containingType = symbol.ContainingType; if (symbol.DeclaredAccessibility == Accessibility.Private || containingType.IsInterfaceType()) { return; } var explicitInterface = declaration.GetExplicitInterfaceSpecifierSyntax(); if (explicitInterface != null) { return; } if (containingType.IsAbstract) { if (modifiers.Any(m => m.IsKind(SyntaxKind.AbstractKeyword))) { context.RegisterRefactoring(CodeActionFactory.Create( token.Span, DiagnosticSeverity.Info, GettextCatalog.GetString("To non-abstract"), t2 => { var newRoot = root.ReplaceNode((SyntaxNode)declaration, ImplementAbstractDeclaration(declaration).WithAdditionalAnnotations(Formatter.Annotation)); return(Task.FromResult(document.WithSyntaxRoot(newRoot))); } ) ); } else { if (CheckBody(declaration)) { context.RegisterRefactoring(CodeActionFactory.Create( token.Span, DiagnosticSeverity.Info, GettextCatalog.GetString("To abstract"), t2 => { var newRoot = root.ReplaceNode((SyntaxNode)declaration, MakeAbstractDeclaration(declaration).WithAdditionalAnnotations(Formatter.Annotation)); return(Task.FromResult(document.WithSyntaxRoot(newRoot))); } ) ); } } } if (modifiers.Any(m => m.IsKind(SyntaxKind.VirtualKeyword))) { context.RegisterRefactoring(CodeActionFactory.Create( token.Span, DiagnosticSeverity.Info, GettextCatalog.GetString("To non-virtual"), t2 => { var newRoot = root.ReplaceNode((SyntaxNode)declaration, RemoveVirtualModifier(declaration)); return(Task.FromResult(document.WithSyntaxRoot(newRoot))); } ) ); } else { if (modifiers.Any(m => m.IsKind(SyntaxKind.AbstractKeyword))) { context.RegisterRefactoring(CodeActionFactory.Create( token.Span, DiagnosticSeverity.Info, GettextCatalog.GetString("To virtual"), t2 => { var newRoot = root.ReplaceNode((SyntaxNode)declaration, ImplementAbstractDeclaration(declaration, true).WithAdditionalAnnotations(Formatter.Annotation)); return(Task.FromResult(document.WithSyntaxRoot(newRoot))); } ) ); } else if (!containingType.IsStatic) { context.RegisterRefactoring(CodeActionFactory.Create( token.Span, DiagnosticSeverity.Info, GettextCatalog.GetString("To virtual"), t2 => { var newRoot = root.ReplaceNode((SyntaxNode)declaration, AddModifier(declaration, SyntaxKind.VirtualKeyword).WithAdditionalAnnotations(Formatter.Annotation)); return(Task.FromResult(document.WithSyntaxRoot(newRoot))); } ) ); } } }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles) { return; } var span = context.Span; if (!span.IsEmpty) { return; } var cancellationToken = context.CancellationToken; if (cancellationToken.IsCancellationRequested) { return; } var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (model.IsFromGeneratedCode(cancellationToken)) { return; } var root = await model.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); var node = root.FindNode(span) as BinaryExpressionSyntax; //ignore nodes except string concat. if (node == null || !node.OperatorToken.IsKind(SyntaxKind.PlusToken)) { return; } LiteralExpressionSyntax left; var leftBinaryExpr = node.Left as BinaryExpressionSyntax; //if there is something other than a string literal on the left, then just take the right node (e.g. a+b+c => a+(b+c)) if (leftBinaryExpr != null && leftBinaryExpr.OperatorToken.IsKind(SyntaxKind.PlusToken)) { left = leftBinaryExpr.Right as LiteralExpressionSyntax; } else { left = node.Left as LiteralExpressionSyntax; } var right = node.Right as LiteralExpressionSyntax; //ignore non-string literals if (left == null || right == null || !left.IsKind(SyntaxKind.StringLiteralExpression) || !right.IsKind(SyntaxKind.StringLiteralExpression)) { return; } bool isLeftVerbatim = left.Token.IsVerbatimStringLiteral(); bool isRightVerbatim = right.Token.IsVerbatimStringLiteral(); if (isLeftVerbatim != isRightVerbatim) { return; } String newString = left.Token.ValueText + right.Token.ValueText; LiteralExpressionSyntax stringLit; if (isLeftVerbatim) { stringLit = SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal("@\"" + newString + "\"", newString)); } else { stringLit = SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(newString)); } ExpressionSyntax exprNode; if (leftBinaryExpr == null) { exprNode = stringLit; } else { exprNode = leftBinaryExpr.WithRight(stringLit); } context.RegisterRefactoring( CodeActionFactory.Create(span, DiagnosticSeverity.Info, GettextCatalog.GetString("Join strings"), document.WithSyntaxRoot(root.ReplaceNode((SyntaxNode)node, exprNode as ExpressionSyntax))) ); }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles) { return; } var span = context.Span; if (!span.IsEmpty) { return; } var cancellationToken = context.CancellationToken; if (cancellationToken.IsCancellationRequested) { return; } var options = document.Project.ParseOptions as CSharpParseOptions; if (options != null && options.LanguageVersion < LanguageVersion.CSharp6) { return; } var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (model.IsFromGeneratedCode(cancellationToken)) { return; } var root = await model.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); var node = root.FindToken(span.Start).Parent; var memberAccessExpr = node.Parent as MemberAccessExpressionSyntax; if (memberAccessExpr == null) { return; } var info = model.GetSymbolInfo(node, cancellationToken); if (info.Symbol == null || info.Symbol.Kind != SymbolKind.NamedType || !info.Symbol.IsStatic) { return; } var parentMemberAccessExpr = memberAccessExpr.Parent as MemberAccessExpressionSyntax; if (parentMemberAccessExpr != null) { var memberInfoSymbol = model.GetSymbolInfo(parentMemberAccessExpr).Symbol; if ((memberInfoSymbol == null) || memberInfoSymbol.IsExtensionMethod()) { return; } } context.RegisterRefactoring( CodeActionFactory.Create( node.Span, DiagnosticSeverity.Info, GettextCatalog.GetString("Import static class with using"), t2 => { return(ImportStaticClassWithUsing(document, model, root, node, info, t2)); } ) ); }
public async override Task RegisterCodeFixesAsync(CodeFixContext context) { var document = context.Document; var cancellationToken = context.CancellationToken; var span = context.Span; var diagnostics = context.Diagnostics; var root = await document.GetSyntaxRootAsync(cancellationToken); var diagnostic = diagnostics.First(); var node = root.FindNode(context.Span, getInnermostNodeForTie: true) as ConditionalExpressionSyntax; var newRoot = root; bool?trueBranch = GetBool(node.WhenTrue.SkipParens()); bool?falseBranch = GetBool(node.WhenFalse.SkipParens()); if (trueBranch == false && falseBranch == true) { newRoot = newRoot.ReplaceNode(node, CSharpUtil.InvertCondition(node.Condition).WithAdditionalAnnotations(Formatter.Annotation)); } else if (trueBranch == true) { newRoot = newRoot.ReplaceNode( (SyntaxNode)node, SyntaxFactory.BinaryExpression( SyntaxKind.LogicalOrExpression, node.Condition, SyntaxFactory.ParseToken(" || "), node.WhenFalse ).WithAdditionalAnnotations(Formatter.Annotation) ); } else if (trueBranch == false) { newRoot = newRoot.ReplaceNode( (SyntaxNode)node, SyntaxFactory.BinaryExpression( SyntaxKind.LogicalAndExpression, CSharpUtil.InvertCondition(node.Condition), SyntaxFactory.ParseToken(" && "), node.WhenFalse ).WithAdditionalAnnotations(Formatter.Annotation) ); } else if (falseBranch == true) { newRoot = newRoot.ReplaceNode( (SyntaxNode)node, SyntaxFactory.BinaryExpression( SyntaxKind.LogicalOrExpression, CSharpUtil.InvertCondition(node.Condition), SyntaxFactory.ParseToken(" || "), node.WhenTrue ).WithAdditionalAnnotations(Formatter.Annotation) ); } else if (falseBranch == false) { newRoot = newRoot.ReplaceNode( (SyntaxNode)node, SyntaxFactory.BinaryExpression( SyntaxKind.LogicalAndExpression, node.Condition, SyntaxFactory.ParseToken(" && "), node.WhenTrue ).WithAdditionalAnnotations(Formatter.Annotation) ); } context.RegisterCodeFix(CodeActionFactory.Create(node.Span, diagnostic.Severity, "Simplify conditional expression", document.WithSyntaxRoot(newRoot)), diagnostic); }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles) { return; } var span = context.Span; if (!span.IsEmpty) { return; } var cancellationToken = context.CancellationToken; if (cancellationToken.IsCancellationRequested) { return; } var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (model.IsFromGeneratedCode(cancellationToken)) { return; } var root = await model.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); var token = root.FindToken(span.Start); if (!(token.Parent is ParameterSyntax parameter)) { return; } if (!(parameter.Parent.Parent is ConstructorDeclarationSyntax ctor)) { return; } context.RegisterRefactoring( CodeActionFactory.Create( parameter.Span, DiagnosticSeverity.Info, GettextCatalog.GetString("Initialize field from parameter"), t2 => { var newFieldName = NameProposalService.GetNameProposal(parameter.Identifier.ValueText, SyntaxKind.FieldDeclaration, Accessibility.Private, false, context.Document, ctor.SpanStart); var newField = SyntaxFactory.FieldDeclaration( SyntaxFactory.VariableDeclaration( parameter.Type, SyntaxFactory.SingletonSeparatedList <VariableDeclaratorSyntax>( SyntaxFactory.VariableDeclarator(newFieldName))) ).WithModifiers( SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword))) .WithAdditionalAnnotations(Formatter.Annotation); var assignmentStatement = SyntaxFactory.ExpressionStatement( SyntaxFactory.AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.ThisExpression(), SyntaxFactory.IdentifierName(newFieldName)), SyntaxFactory.IdentifierName(parameter.Identifier) ) ).WithAdditionalAnnotations(Formatter.Annotation); var trackedRoot = root.TrackNodes(ctor); var newRoot = trackedRoot.InsertNodesBefore(trackedRoot.GetCurrentNode(ctor), new [] { newField }); IEnumerable <StatementSyntax> statements = new[] { assignmentStatement }; if (ctor.Body != null) { statements = statements.Concat(ctor.Body.Statements); } else if (ctor.ExpressionBody != null) { var expressionStatement = SyntaxFactory.ExpressionStatement(ctor.ExpressionBody.Expression, SyntaxFactory.Token(SyntaxKind.SemicolonToken)); statements = statements.Concat(new[] { expressionStatement }); } var ctorBody = SyntaxFactory.Block(statements); //.WithLeadingTrivia(ctor.GetLeadingTrivia()); ConstructorDeclarationSyntax newCtor = ctor.WithBody(ctorBody) .WithExpressionBody(null) .WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.None)) .WithAdditionalAnnotations(Formatter.Annotation) .WithTrailingTrivia(ctor.GetTrailingTrivia()); newRoot = newRoot.ReplaceNode(newRoot.GetCurrentNode(ctor), newCtor); return(Task.FromResult(document.WithSyntaxRoot(newRoot))); }) ); }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles) { return; } var span = context.Span; if (!span.IsEmpty) { return; } var cancellationToken = context.CancellationToken; if (cancellationToken.IsCancellationRequested) { return; } var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (model.IsFromGeneratedCode(cancellationToken)) { return; } var root = await model.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); var complexIfElseStatement = GetIfElseStatement(root, span); if (complexIfElseStatement != null) { context.RegisterRefactoring( CodeActionFactory.Create( span, DiagnosticSeverity.Info, invertIfFixMessage, t2 => { var statements = GenerateReplacementStatements(complexIfElseStatement); var newRoot = statements.Count == 1 ? root.ReplaceNode(complexIfElseStatement, statements[0]) : root.ReplaceNode(complexIfElseStatement, statements); return(Task.FromResult(document.WithSyntaxRoot(newRoot))); } ) ); return; } var simpleIfElseStatement = GetIfElseStatementSimple(root, span); if (simpleIfElseStatement != null) { context.RegisterRefactoring( CodeActionFactory.Create( span, DiagnosticSeverity.Info, invertIfFixMessage, t2 => { var newRoot = root.ReplaceNode((SyntaxNode) simpleIfElseStatement, simpleIfElseStatement .WithCondition(CSharpUtil.InvertCondition(simpleIfElseStatement.Condition)) .WithStatement(simpleIfElseStatement.Else.Statement) .WithElse(simpleIfElseStatement.Else.WithStatement(simpleIfElseStatement.Statement)) .WithAdditionalAnnotations(Formatter.Annotation) ); return(Task.FromResult(document.WithSyntaxRoot(newRoot))); } ) ); return; } var ifStatement = GetIfStatement(root, span); if (ifStatement != null) { context.RegisterRefactoring( CodeActionFactory.Create( ifStatement.Span, DiagnosticSeverity.Info, invertIfFixMessage, t2 => { var mergedIfStatement = SyntaxFactory.IfStatement(CSharpUtil.InvertCondition(ifStatement.Condition), SyntaxFactory.ReturnStatement()) .WithAdditionalAnnotations(Formatter.Annotation); var newRoot = root.ReplaceNode((SyntaxNode)ifStatement, new SyntaxNode[] { mergedIfStatement }.Concat(GetStatements(ifStatement.Statement))); return(Task.FromResult(document.WithSyntaxRoot(newRoot))); } ) ); } var ifStatementInLoop = GetIfElseStatementInLoop(root, span); if (ifStatementInLoop != null) { context.RegisterRefactoring( CodeActionFactory.Create( ifStatementInLoop.Span, DiagnosticSeverity.Info, invertIfFixMessage, t2 => { var mergedIfStatement = SyntaxFactory.IfStatement(CSharpUtil.InvertCondition(ifStatementInLoop.Condition), SyntaxFactory.ContinueStatement()) .WithAdditionalAnnotations(Formatter.Annotation); var newRoot = root.ReplaceNode((SyntaxNode)ifStatementInLoop, new SyntaxNode[] { mergedIfStatement }.Concat(GetStatements(ifStatementInLoop.Statement))); return(Task.FromResult(document.WithSyntaxRoot(newRoot))); } ) ); return; } }
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false); if (!TryFindFirstAncestorOrSelf(root, context.Span, out MemberDeclarationSyntax memberDeclaration)) { return; } foreach (Diagnostic diagnostic in context.Diagnostics) { switch (diagnostic.Id) { case DiagnosticIdentifiers.RemoveRedundantOverridingMember: { CodeAction codeAction = CodeActionFactory.RemoveMemberDeclaration(context.Document, memberDeclaration, equivalenceKey: GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.AddAccessibilityModifiers: { var accessibility = (Accessibility)Enum.Parse( typeof(Accessibility), diagnostic.Properties[nameof(Accessibility)]); CodeAction codeAction = CodeAction.Create( "Add accessibility modifiers", cancellationToken => AddDefaultAccessModifierRefactoring.RefactorAsync(context.Document, memberDeclaration, accessibility, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.RemoveRedundantSealedModifier: { ModifiersCodeFixRegistrator.RemoveModifier(context, diagnostic, memberDeclaration, SyntaxKind.SealedKeyword); break; } case DiagnosticIdentifiers.AvoidSemicolonAtEndOfDeclaration: { CodeAction codeAction = CodeAction.Create( "Remove unnecessary semicolon", cancellationToken => AvoidSemicolonAtEndOfDeclarationRefactoring.RefactorAsync(context.Document, memberDeclaration, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.OrderModifiers: { CodeAction codeAction = CodeAction.Create( "Order modifiers", ct => OrderModifiersAsync(context.Document, memberDeclaration, ct), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.MakeFieldReadOnly: { var fieldDeclaration = (FieldDeclarationSyntax)memberDeclaration; SeparatedSyntaxList <VariableDeclaratorSyntax> declarators = fieldDeclaration.Declaration.Variables; string title = (declarators.Count == 1) ? $"Make '{declarators[0].Identifier.ValueText}' read-only" : "Make fields read-only"; ModifiersCodeFixRegistrator.AddModifier(context, diagnostic, fieldDeclaration, SyntaxKind.ReadOnlyKeyword, title: title); break; } case DiagnosticIdentifiers.UseConstantInsteadOfField: { CodeAction codeAction = CodeAction.Create( "Use constant instead of field", cancellationToken => UseConstantInsteadOfFieldRefactoring.RefactorAsync(context.Document, (FieldDeclarationSyntax)memberDeclaration, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.UseReadOnlyAutoProperty: { CodeAction codeAction = CodeAction.Create( "Use read-only auto-property", cancellationToken => UseReadOnlyAutoPropertyRefactoring.RefactorAsync(context.Document, (PropertyDeclarationSyntax)memberDeclaration, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.ConvertCommentToDocumentationComment: { CodeAction codeAction = CodeAction.Create( ConvertCommentToDocumentationCommentRefactoring.Title, cancellationToken => ConvertCommentToDocumentationCommentRefactoring.RefactorAsync(context.Document, memberDeclaration, context.Span, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case DiagnosticIdentifiers.MakeMethodExtensionMethod: { var methodDeclaration = (MethodDeclarationSyntax)memberDeclaration; CodeAction codeAction = CodeAction.Create( "Make method an extension method", cancellationToken => { ParameterSyntax parameter = methodDeclaration.ParameterList.Parameters[0]; ParameterSyntax newParameter = ModifierList.Insert(parameter, SyntaxKind.ThisKeyword); return(context.Document.ReplaceNodeAsync(parameter, newParameter, cancellationToken)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } } } }
public static async Task ComputeRefactoringsAsync(RefactoringContext context, LocalFunctionStatementSyntax localFunctionStatement) { if (localFunctionStatement.IsParentKind(SyntaxKind.Block)) { BlockSyntax body = localFunctionStatement.Body; if (body != null) { if (body.OpenBraceToken.Span.Contains(context.Span) || body.CloseBraceToken.Span.Contains(context.Span)) { if (context.IsRefactoringEnabled(RefactoringDescriptors.RemoveMemberDeclaration)) { context.RegisterRefactoring(CodeActionFactory.RemoveStatement(context.Document, localFunctionStatement, equivalenceKey: EquivalenceKey.Create(RefactoringDescriptors.RemoveMemberDeclaration))); } if (context.IsRefactoringEnabled(RefactoringDescriptors.CopyMemberDeclaration)) { context.RegisterRefactoring( "Copy local function", ct => CopyMemberDeclarationRefactoring.RefactorAsync( context.Document, localFunctionStatement, copyAfter: body.CloseBraceToken.Span.Contains(context.Span), ct), RefactoringDescriptors.CopyMemberDeclaration); } if (context.IsRefactoringEnabled(RefactoringDescriptors.CommentOutMemberDeclaration)) { CommentOutRefactoring.RegisterRefactoring(context, localFunctionStatement); } } } } if (context.IsRefactoringEnabled(RefactoringDescriptors.ChangeMethodReturnTypeToVoid) && context.Span.IsEmptyAndContainedInSpan(localFunctionStatement)) { await ChangeMethodReturnTypeToVoidRefactoring.ComputeRefactoringAsync(context, localFunctionStatement).ConfigureAwait(false); } if (context.IsRefactoringEnabled(RefactoringDescriptors.AddGenericParameterToDeclaration)) { AddGenericParameterToDeclarationRefactoring.ComputeRefactoring(context, localFunctionStatement); } if (context.IsRefactoringEnabled(RefactoringDescriptors.ConvertBlockBodyToExpressionBody) && ConvertBlockBodyToExpressionBodyRefactoring.CanRefactor(localFunctionStatement, context.Span)) { context.RegisterRefactoring( ConvertBlockBodyToExpressionBodyRefactoring.Title, ct => ConvertBlockBodyToExpressionBodyRefactoring.RefactorAsync(context.Document, localFunctionStatement, ct), RefactoringDescriptors.ConvertBlockBodyToExpressionBody); } if (context.IsRefactoringEnabled(RefactoringDescriptors.MoveUnsafeContextToContainingDeclaration)) { MoveUnsafeContextToContainingDeclarationRefactoring.ComputeRefactoring(context, localFunctionStatement); } }
/* * This sample code refactoring shows some basics of refactoring implementations * and can serve as a simple template for own refactorings in RefactoringEssentials project. * * This example operates on interface declarations: It checks whether interface name begins with an "I" * and if not, suggests to add an "I" prefix. */ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { // Some general variables we need for further processing and some basic checks to exit as early as possible. var document = context.Document; if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles) { return; } var span = context.Span; if (!span.IsEmpty) { return; } var cancellationToken = context.CancellationToken; if (cancellationToken.IsCancellationRequested) { return; } var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (model.IsFromGeneratedCode(cancellationToken)) { return; } var root = await model.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); // This is the initial code element on which the refactoring is to be offered. // Example: In code "public class SomeClass { ..." user presses Ctrl+. on the "class" keyword. // Then token will point to the "class" keyword token. var token = root.FindToken(span.Start); // Now we need to find out whether "token" is inside of a code element we need to refactor. // We want to refactor an interface name. Then we need to check if "token" is // a type name identifier and is inside of an interface declaration. var interfaceDeclaration = token.Parent as InterfaceDeclarationSyntax; if (interfaceDeclaration == null) { // Not cast-able to interface declaration -> exit, we are wrong here return; } // Is "token" itself the interface's name identifier? We want to offer the refactoring exactly on the name. // Note: Checking the syntax element's Kind is another way to detect a specific type, and often is even more detailed. if (!token.IsKind(SyntaxKind.IdentifierToken)) { // Not the name identifier -> don't offer a refactoring here return; } // Get interface's name identifier and check its name var interfaceName = interfaceDeclaration.Identifier.ValueText; // Does interface name start with an "I"? // And if so: Is 2nd character a capital, too? // Then our refactoring is not needed anymore, name already has the desired format. if (interfaceName.StartsWith("I")) { if ((interfaceName.Length > 1) && char.IsUpper(interfaceName[1])) { return; } } context.RegisterRefactoring(CodeActionFactory.Create(token.Span, DiagnosticSeverity.Info, GettextCatalog.GetString("Sample: Prepend with 'I'"), t2 => { return(Task.FromResult(PerformAction(document, model, root, interfaceDeclaration, interfaceName))); })); }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles) { return; } var span = context.Span; if (!span.IsEmpty) { return; } var cancellationToken = context.CancellationToken; if (cancellationToken.IsCancellationRequested) { return; } var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (model.IsFromGeneratedCode(cancellationToken)) { return; } var root = await model.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); var node = root.FindNode(span); if (!node.IsKind(SyntaxKind.IdentifierName)) { return; } var nodeGrandparent = node.Parent?.Parent; if ((nodeGrandparent is EventDeclarationSyntax) || (nodeGrandparent is EventFieldDeclarationSyntax)) { return; } if (node.Parent.IsKind(SyntaxKind.SimpleMemberAccessExpression)) { node = node.Parent; } var info = model.GetTypeInfo(node, cancellationToken); var type = info.ConvertedType ?? info.Type; if (type == null) { return; } var invocationMethod = type.GetDelegateInvokeMethod(); if (invocationMethod == null) { return; } context.RegisterRefactoring( CodeActionFactory.Create( node.Span, DiagnosticSeverity.Info, GettextCatalog.GetString("To lambda expression"), t2 => { var expr = SyntaxFactory.InvocationExpression( (ExpressionSyntax)node, SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(invocationMethod.Parameters.Select(p => SyntaxFactory.Argument(SyntaxFactory.IdentifierName(p.Name))))) ); var parameters = invocationMethod.Parameters.Select(p => CreateParameterSyntax(model, node, p)).ToList(); var ame = SyntaxFactory.ParenthesizedLambdaExpression( SyntaxFactory.ParameterList(SyntaxFactory.SeparatedList(parameters)), expr ); var newRoot = root.ReplaceNode((SyntaxNode)node, ame.WithAdditionalAnnotations(Formatter.Annotation)); return(Task.FromResult(document.WithSyntaxRoot(newRoot))); } ) ); }
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { if (!Settings.IsAnyEnabled( CodeFixIdentifiers.UseExplicitTypeInsteadOfVar, CodeFixIdentifiers.ReplaceVariableDeclarationWithAssignment)) { return; } SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false); if (!TryFindFirstAncestorOrSelf(root, context.Span, out SyntaxNode node, predicate: f => f.IsKind( SyntaxKind.VariableDeclaration, SyntaxKind.ForEachStatement, SyntaxKind.Parameter, SyntaxKind.DeclarationPattern, SyntaxKind.DeclarationExpression))) { return; } if (node.IsKind(SyntaxKind.ForEachStatement, SyntaxKind.Parameter, SyntaxKind.DeclarationPattern, SyntaxKind.DeclarationExpression)) { return; } var variableDeclaration = (VariableDeclarationSyntax)node; foreach (Diagnostic diagnostic in context.Diagnostics) { switch (diagnostic.Id) { case CompilerDiagnosticIdentifiers.ImplicitlyTypedVariablesCannotHaveMultipleDeclarators: case CompilerDiagnosticIdentifiers.ImplicitlyTypedVariablesCannotBeConstant: { if (!Settings.IsEnabled(CodeFixIdentifiers.UseExplicitTypeInsteadOfVar)) { return; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); TypeSyntax type = variableDeclaration.Type; ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(type, context.CancellationToken); if (typeSymbol?.SupportsExplicitDeclaration() == true) { CodeAction codeAction = CodeActionFactory.ChangeType(context.Document, type, typeSymbol, semanticModel, equivalenceKey: GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); } break; } case CompilerDiagnosticIdentifiers.LocalVariableOrFunctionIsAlreadyDefinedInThisScope: case CompilerDiagnosticIdentifiers.LocalOrParameterCannotBeDeclaredInThisScopeBecauseThatNameIsUsedInEnclosingScopeToDefineLocalOrParameter: { if (!Settings.IsEnabled(CodeFixIdentifiers.ReplaceVariableDeclarationWithAssignment)) { return; } if (!(variableDeclaration.Parent is LocalDeclarationStatementSyntax localDeclaration)) { return; } VariableDeclaratorSyntax variableDeclarator = variableDeclaration.Variables.SingleOrDefault(shouldThrow: false); if (variableDeclarator == null) { break; } ExpressionSyntax value = variableDeclarator.Initializer?.Value; if (value == null) { break; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); VariableDeclaratorSyntax variableDeclarator2 = FindVariableDeclarator( variableDeclarator.Identifier.ValueText, semanticModel.GetEnclosingSymbolSyntax(localDeclaration.SpanStart, context.CancellationToken)); if (variableDeclarator2?.SpanStart < localDeclaration.SpanStart) { CodeAction codeAction = CodeAction.Create( "Replace variable declaration with assignment", cancellationToken => { ExpressionStatementSyntax newNode = CSharpFactory.SimpleAssignmentStatement( SyntaxFactory.IdentifierName(variableDeclarator.Identifier), value); newNode = newNode .WithTriviaFrom(localDeclaration) .WithFormatterAnnotation(); return(context.Document.ReplaceNodeAsync(localDeclaration, newNode, cancellationToken)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); } break; } } } }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles) { return; } var span = context.Span; if (!span.IsEmpty) { return; } var cancellationToken = context.CancellationToken; if (cancellationToken.IsCancellationRequested) { return; } var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (model.IsFromGeneratedCode(cancellationToken)) { return; } var root = await model.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); var token = root.FindToken(span.Start); if (!token.IsKind(SyntaxKind.IdentifierToken)) { return; } var node = token.Parent as DeclarationStatementSyntax; if (node == null) { return; } var declaredSymbol = model.GetDeclaredSymbol(node, cancellationToken); if (declaredSymbol == null || !string.IsNullOrEmpty(declaredSymbol.GetDocumentationCommentXml(null, false, cancellationToken))) { return; } string documentation; var baseMember = GetBaseMember(declaredSymbol, out documentation, cancellationToken); if (baseMember == null || string.IsNullOrEmpty(documentation)) { return; } XDocument doc = XDocument.Parse(documentation); var rootElement = doc.Elements().First(); var inner = string.Join(System.Environment.NewLine, rootElement.Nodes().Select(n => n.ToString())).Trim(); if (string.IsNullOrEmpty(inner)) { return; } // "Copy comments from interface" context.RegisterRefactoring( CodeActionFactory.Create( span, DiagnosticSeverity.Info, baseMember.ContainingType != null && baseMember.ContainingType.TypeKind == TypeKind.Interface ? GettextCatalog.GetString("Copy comments from interface") : GettextCatalog.GetString("Copy comments from base"), t2 => { var trivia = node.GetLeadingTrivia(); var indentTrivia = trivia.FirstOrDefault(t => t.IsKind(SyntaxKind.WhitespaceTrivia)); var indent = indentTrivia.ToString(); string[] lines = NewLine.SplitLines(inner); for (int i = 0; i < lines.Length; i++) { lines[i] = indent + "'''" + lines[i].Trim(); } var eol = "\r\n"; int idx = 0; while (idx < trivia.Count && trivia[idx].IsKind(SyntaxKind.EndOfLineTrivia)) { idx++; } trivia = trivia.Insert(idx, SyntaxFactory.SyntaxTrivia(SyntaxKind.DocumentationCommentExteriorTrivia, string.Join(eol, lines) + eol)); var newRoot = root.ReplaceNode(node, node.WithLeadingTrivia(trivia)); return(Task.FromResult(document.WithSyntaxRoot(newRoot))); } ) ); }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles) { return; } var span = context.Span; if (!span.IsEmpty) { return; } var cancellationToken = context.CancellationToken; if (cancellationToken.IsCancellationRequested) { return; } var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (model.IsFromGeneratedCode(cancellationToken)) { return; } var root = await model.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); var token = root.FindToken(span.Start); var parameter = token.Parent as ParameterSyntax; if (parameter != null) { var ctor = parameter.Parent.Parent as ConstructorDeclarationSyntax; if (ctor == null) { return; } context.RegisterRefactoring( CodeActionFactory.Create( parameter.Span, DiagnosticSeverity.Info, GettextCatalog.GetString("Initialize auto-property from parameter"), t2 => { var propertyName = GetPropertyName(parameter.Identifier.ToString()); var accessorDeclList = new SyntaxList <AccessorDeclarationSyntax>().Add(SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))).Add(SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration).WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))); var newProperty = SyntaxFactory.PropertyDeclaration(parameter.Type, propertyName) .WithAccessorList(SyntaxFactory.AccessorList(accessorDeclList)) .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword))) .WithAdditionalAnnotations(Formatter.Annotation); var assignmentStatement = SyntaxFactory.ExpressionStatement( SyntaxFactory.AssignmentExpression( SyntaxKind.SimpleAssignmentExpression, propertyName != parameter.Identifier.ToString() ? (ExpressionSyntax)SyntaxFactory.IdentifierName(propertyName) : SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.ThisExpression(), SyntaxFactory.IdentifierName(parameter.Identifier)), SyntaxFactory.IdentifierName(parameter.Identifier) ) ).WithAdditionalAnnotations(Formatter.Annotation); var trackedRoot = root.TrackNodes(ctor); var newRoot = trackedRoot.InsertNodesBefore(trackedRoot.GetCurrentNode(ctor), new List <SyntaxNode>() { newProperty }); var ctorBody = ctor.Body ?? SyntaxFactory.Block(); newRoot = newRoot.ReplaceNode(newRoot.GetCurrentNode(ctor), ctor.WithBody( ctorBody.WithStatements(SyntaxFactory.List(new[] { assignmentStatement }.Concat(ctorBody.Statements))) )); return(Task.FromResult(document.WithSyntaxRoot(newRoot))); }) ); } }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles) { return; } var span = context.Span; if (!span.IsEmpty) { return; } var cancellationToken = context.CancellationToken; if (cancellationToken.IsCancellationRequested) { return; } var root = await document.GetSyntaxRootAsync(cancellationToken); var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (model.IsFromGeneratedCode(cancellationToken)) { return; } var node = root.FindNode(span) as IfStatementSyntax; if (node == null || !(node.Parent is MultiLineIfBlockSyntax)) { return; } var ifBlock = node.Parent as MultiLineIfBlockSyntax; var selectCaseExpression = GetSelectCaseExpression(model, node.Condition); if (selectCaseExpression == null) { return; } var caseBlocks = new List <CaseBlockSyntax>(); if (!CollectCaseBlocks(caseBlocks, model, ifBlock, selectCaseExpression)) { return; } context.RegisterRefactoring( CodeActionFactory.Create( span, DiagnosticSeverity.Info, GettextCatalog.GetString("To 'Select Case'"), ct => { var selectCaseStatement = SyntaxFactory.SelectBlock(SyntaxFactory.SelectStatement(selectCaseExpression).WithCaseKeyword(SyntaxFactory.Token(SyntaxKind.CaseKeyword)), new SyntaxList <CaseBlockSyntax>().AddRange(caseBlocks)) .NormalizeWhitespace(); return(Task.FromResult(document.WithSyntaxRoot(root.ReplaceNode( ifBlock, selectCaseStatement .WithLeadingTrivia(ifBlock.GetLeadingTrivia()) .WithTrailingTrivia(ifBlock.GetTrailingTrivia()) .WithAdditionalAnnotations(Formatter.Annotation))))); }) ); }
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { SyntaxNode root = await context.GetSyntaxRootAsync().ConfigureAwait(false); if (!TryFindNode(root, context.Span, out ExpressionSyntax expression)) { return; } foreach (Diagnostic diagnostic in context.Diagnostics) { switch (diagnostic.Id) { case CompilerDiagnosticIdentifiers.CannotImplicitlyConvertTypeExplicitConversionExists: { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); TypeInfo typeInfo = semanticModel.GetTypeInfo(expression, context.CancellationToken); ITypeSymbol type = typeInfo.Type; ITypeSymbol convertedType = typeInfo.ConvertedType; if ((type is INamedTypeSymbol namedType) && namedType.IsNullableType()) { if (convertedType?.SpecialType == SpecialType.System_Boolean || AddComparisonWithBooleanLiteralRefactoring.IsCondition(expression)) { if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.AddComparisonWithBooleanLiteral)) { CodeAction codeAction = CodeAction.Create( AddComparisonWithBooleanLiteralRefactoring.GetTitle(expression), cancellationToken => AddComparisonWithBooleanLiteralRefactoring.RefactorAsync(context.Document, expression, cancellationToken), GetEquivalenceKey(diagnostic, CodeFixIdentifiers.AddComparisonWithBooleanLiteral)); context.RegisterCodeFix(codeAction, diagnostic); } } else if (SymbolEqualityComparer.Default.Equals(namedType.TypeArguments[0], convertedType)) { if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.UseCoalesceExpression)) { CodeAction codeAction = CodeAction.Create( "Use coalesce expression", cancellationToken => { ExpressionSyntax defaultValue = convertedType.GetDefaultValueSyntax(context.Document.GetDefaultSyntaxOptions()); ExpressionSyntax newNode = CoalesceExpression(expression.WithoutTrivia(), defaultValue) .WithTriviaFrom(expression) .Parenthesize() .WithFormatterAnnotation(); return(context.Document.ReplaceNodeAsync(expression, newNode, cancellationToken)); }, GetEquivalenceKey(diagnostic, CodeFixIdentifiers.UseCoalesceExpression)); context.RegisterCodeFix(codeAction, diagnostic); } } } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ChangeMemberTypeAccordingToReturnExpression) && expression.IsParentKind(SyntaxKind.ReturnStatement, SyntaxKind.YieldReturnStatement)) { ChangeMemberTypeRefactoring.ComputeCodeFix(context, diagnostic, expression, semanticModel); } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.AddCastExpression)) { CodeFixRegistrator.AddCastExpression(context, diagnostic, expression, convertedType, semanticModel); } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ChangeTypeAccordingToInitializer)) { ChangeTypeAccordingToInitializerRefactoring.ComputeCodeFix(context, diagnostic, expression, semanticModel); } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.CreateSingletonArray) && type?.IsErrorType() == false && !SymbolEqualityComparer.Default.Equals(type, convertedType) && (convertedType is IArrayTypeSymbol arrayType) && semanticModel.IsImplicitConversion(expression, arrayType.ElementType)) { CodeAction codeAction = CodeAction.Create( "Create singleton array", cancellationToken => CreateSingletonArrayRefactoring.RefactorAsync(context.Document, expression, arrayType.ElementType, semanticModel, cancellationToken), GetEquivalenceKey(diagnostic, CodeFixIdentifiers.CreateSingletonArray)); context.RegisterCodeFix(codeAction, diagnostic); } break; } case CompilerDiagnosticIdentifiers.ConstantValueCannotBeConverted: { if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.UseUncheckedExpression)) { break; } CodeAction codeAction = CodeAction.Create( "Use 'unchecked'", cancellationToken => { CheckedExpressionSyntax newNode = CSharpFactory.UncheckedExpression(expression.WithoutTrivia()); newNode = newNode.WithTriviaFrom(expression); return(context.Document.ReplaceNodeAsync(expression, newNode, cancellationToken)); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case CompilerDiagnosticIdentifiers.ExpressionBeingAssignedMustBeConstant: { SyntaxNode parent = expression.Parent; if (parent?.IsKind(SyntaxKind.EqualsValueClause) != true) { break; } parent = parent.Parent; if (parent?.IsKind(SyntaxKind.VariableDeclarator) != true) { break; } parent = parent.Parent; if (!(parent is VariableDeclarationSyntax variableDeclaration)) { break; } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.RemoveConstModifier) && variableDeclaration.Parent is LocalDeclarationStatementSyntax localDeclarationStatement) { SyntaxTokenList modifiers = localDeclarationStatement.Modifiers; if (!modifiers.Contains(SyntaxKind.ConstKeyword)) { break; } ModifiersCodeFixRegistrator.RemoveModifier(context, diagnostic, localDeclarationStatement, SyntaxKind.ConstKeyword); } else if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ReplaceConstantWithField) && variableDeclaration.Variables.Count == 1 && (variableDeclaration.Parent is FieldDeclarationSyntax fieldDeclaration) && fieldDeclaration.Modifiers.Contains(SyntaxKind.ConstKeyword)) { CodeAction codeAction = CodeAction.Create( ReplaceConstantWithFieldRefactoring.Title, cancellationToken => ReplaceConstantWithFieldRefactoring.RefactorAsync(context.Document, fieldDeclaration, cancellationToken), GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } break; } case CompilerDiagnosticIdentifiers.CannotConvertNullToTypeBecauseItIsNonNullableValueType: case CompilerDiagnosticIdentifiers.CannotConvertNullToTypeParameterBecauseItCouldBeNonNullableValueType: { if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ReplaceNullLiteralExpressionWithDefaultValue)) { break; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); CodeFixRegistrator.ReplaceNullWithDefaultValue(context, diagnostic, expression, semanticModel); break; } case CompilerDiagnosticIdentifiers.ResultOfExpressionIsAlwaysConstantSinceValueIsNeverEqualToNull: { if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.RemoveConditionThatIsAlwaysEqualToTrueOrFalse)) { break; } NullCheckExpressionInfo nullCheck = SyntaxInfo.NullCheckExpressionInfo(expression, allowedStyles: NullCheckStyles.ComparisonToNull); if (!nullCheck.Success) { break; } CodeAction codeAction = CodeAction.Create( "Remove condition", cancellationToken => { cancellationToken.ThrowIfCancellationRequested(); SyntaxNode newRoot = RemoveCondition(root, expression, nullCheck.Style == NullCheckStyles.NotEqualsToNull); cancellationToken.ThrowIfCancellationRequested(); return(Task.FromResult(context.Document.WithSyntaxRoot(newRoot))); }, GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } case CompilerDiagnosticIdentifiers.OnlyAssignmentCallIncrementDecrementAndNewObjectExpressionsCanBeUsedAsStatement: { if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.RemoveParentheses) && expression is ParenthesizedExpressionSyntax parenthesizedExpression && parenthesizedExpression?.IsMissing == false) { CodeAction codeAction = CodeActionFactory.RemoveParentheses(context.Document, parenthesizedExpression, equivalenceKey: GetEquivalenceKey(diagnostic)); context.RegisterCodeFix(codeAction, diagnostic); break; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); if (expression.Parent is ArrowExpressionClauseSyntax arrowExpresssionClause) { if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ChangeMemberTypeAccordingToReturnExpression)) { break; } ChangeMemberTypeRefactoring.ComputeCodeFix(context, diagnostic, expression, semanticModel); } else if (expression.Parent is ExpressionStatementSyntax expressionStatement) { if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.AddArgumentList) && expression.IsKind( SyntaxKind.IdentifierName, SyntaxKind.SimpleMemberAccessExpression)) { SyntaxNode invocationExpression = InvocationExpression(expression); if (semanticModel.GetSpeculativeMethodSymbol(expression.SpanStart, invocationExpression) != null) { CodeAction codeAction = CodeAction.Create( "Add argument list", cancellationToken => context.Document.ReplaceNodeAsync(expression, invocationExpression, cancellationToken), GetEquivalenceKey(diagnostic, CodeFixIdentifiers.AddArgumentList)); context.RegisterCodeFix(codeAction, diagnostic); } } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ReplaceComparisonWithAssignment) && expression.IsKind(SyntaxKind.EqualsExpression)) { BinaryExpressionInfo info = SyntaxInfo.BinaryExpressionInfo(expression); if (!info.Success) { break; } ITypeSymbol leftTypeSymbol = semanticModel.GetTypeSymbol(info.Left, context.CancellationToken); if (leftTypeSymbol?.IsErrorType() != false) { break; } if (!semanticModel.IsImplicitConversion(info.Right, leftTypeSymbol)) { break; } CodeAction codeAction = CodeAction.Create( "Replace comparison with assignment", cancellationToken => { AssignmentExpressionSyntax simpleAssignment = SimpleAssignmentExpression(info.Left, info.Right).WithTriviaFrom(expression); return(context.Document.ReplaceNodeAsync(expression, simpleAssignment, cancellationToken)); }, GetEquivalenceKey(diagnostic, CodeFixIdentifiers.ReplaceComparisonWithAssignment)); context.RegisterCodeFix(codeAction, diagnostic); } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ReplaceConditionalExpressionWithIfElse) && (expression is ConditionalExpressionSyntax conditionalExpression) && conditionalExpression.Condition != null) { ExpressionSyntax whenTrue = conditionalExpression.WhenTrue; ExpressionSyntax whenFalse = conditionalExpression.WhenFalse; if (whenTrue != null && whenFalse != null && semanticModel.GetTypeSymbol(whenTrue, context.CancellationToken)?.SpecialType == SpecialType.System_Void && semanticModel.GetTypeSymbol(whenFalse, context.CancellationToken)?.SpecialType == SpecialType.System_Void) { CodeAction codeAction = CodeAction.Create( "Replace ?: with if-else", cancellationToken => { IfStatementSyntax newNode = IfStatement( conditionalExpression.Condition.WalkDownParentheses(), Block(ExpressionStatement(whenTrue)), ElseClause(Block(ExpressionStatement(whenFalse)))); newNode = newNode .WithTriviaFrom(expressionStatement) .WithFormatterAnnotation(); return(context.Document.ReplaceNodeAsync(expressionStatement, newNode, cancellationToken)); }, GetEquivalenceKey(diagnostic, CodeFixIdentifiers.ReplaceConditionalExpressionWithIfElse)); context.RegisterCodeFix(codeAction, diagnostic); } } if (semanticModel.GetSymbol(expression, context.CancellationToken)?.IsErrorType() != false) { break; } ITypeSymbol typeSymbol = semanticModel.GetTypeSymbol(expression, context.CancellationToken); if (typeSymbol?.IsErrorType() != false) { break; } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.IntroduceLocalVariable) && !expressionStatement.IsEmbedded()) { bool addAwait = typeSymbol.OriginalDefinition.EqualsOrInheritsFromTaskOfT() && semanticModel.GetEnclosingSymbol(expressionStatement.SpanStart, context.CancellationToken).IsAsyncMethod(); CodeAction codeAction = CodeAction.Create( IntroduceLocalVariableRefactoring.GetTitle(expression), cancellationToken => IntroduceLocalVariableRefactoring.RefactorAsync(context.Document, expressionStatement, typeSymbol, addAwait, semanticModel, cancellationToken), GetEquivalenceKey(diagnostic, CodeFixIdentifiers.IntroduceLocalVariable)); context.RegisterCodeFix(codeAction, diagnostic); } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.IntroduceField)) { CodeAction codeAction = CodeAction.Create( $"Introduce field for '{expression}'", cancellationToken => IntroduceFieldRefactoring.RefactorAsync(context.Document, expressionStatement, typeSymbol, semanticModel, cancellationToken), GetEquivalenceKey(diagnostic, CodeFixIdentifiers.IntroduceField)); context.RegisterCodeFix(codeAction, diagnostic); } } break; } case CompilerDiagnosticIdentifiers.CannotImplicitlyConvertType: { if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ReplaceYieldReturnWithForEach) && expression.IsParentKind(SyntaxKind.YieldReturnStatement)) { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); ReplaceYieldReturnWithForEachRefactoring.ComputeCodeFix(context, diagnostic, expression, semanticModel); break; } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ChangeMemberTypeAccordingToReturnExpression) && expression.IsParentKind(SyntaxKind.ReturnStatement, SyntaxKind.YieldReturnStatement)) { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); ChangeMemberTypeRefactoring.ComputeCodeFix(context, diagnostic, expression, semanticModel); break; } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ChangeTypeAccordingToInitializer)) { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); CodeFixRegistrationResult result = ChangeTypeAccordingToInitializerRefactoring.ComputeCodeFix(context, diagnostic, expression, semanticModel); if (!result.Success) { RemoveAssignmentOfVoidExpression(context, diagnostic, expression, semanticModel); } break; } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.ReplaceStringLiteralWithCharacterLiteral) && expression?.Kind() == SyntaxKind.StringLiteralExpression) { var literalExpression = (LiteralExpressionSyntax)expression; if (literalExpression.Token.ValueText.Length == 1) { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); if (semanticModel.GetTypeInfo(expression, context.CancellationToken).ConvertedType?.SpecialType == SpecialType.System_Char) { CodeAction codeAction = CodeAction.Create( "Replace string literal with character literal", cancellationToken => ReplaceStringLiteralWithCharacterLiteralRefactoring.RefactorAsync(context.Document, literalExpression, cancellationToken), GetEquivalenceKey(diagnostic, CodeFixIdentifiers.ReplaceStringLiteralWithCharacterLiteral)); context.RegisterCodeFix(codeAction, diagnostic); } } } if (Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.UseYieldReturnInsteadOfReturn) && expression.IsParentKind(SyntaxKind.ReturnStatement)) { var returnStatement = (ReturnStatementSyntax)expression.Parent; SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); ISymbol containingSymbol = semanticModel.GetEnclosingSymbol(returnStatement.SpanStart, context.CancellationToken); if (containingSymbol?.Kind == SymbolKind.Method && ((IMethodSymbol)containingSymbol).ReturnType.OriginalDefinition.IsIEnumerableOrIEnumerableOfT()) { CodeAction codeAction = CodeAction.Create( "Use yield return instead of return", cancellationToken => UseYieldReturnInsteadOfReturnRefactoring.RefactorAsync(context.Document, returnStatement, SyntaxKind.YieldReturnStatement, semanticModel, cancellationToken), GetEquivalenceKey(diagnostic, CodeFixIdentifiers.UseYieldReturnInsteadOfReturn)); context.RegisterCodeFix(codeAction, diagnostic); } } break; } case CompilerDiagnosticIdentifiers.LeftHandSideOfAssignmentMustBeVariablePropertyOrIndexer: { if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.RemoveConstModifier)) { return; } if (!expression.IsKind(SyntaxKind.IdentifierName)) { return; } if (!expression.IsParentKind(SyntaxKind.SimpleAssignmentExpression)) { return; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); SymbolInfo symbolInfo = semanticModel.GetSymbolInfo(expression, context.CancellationToken); if (symbolInfo.CandidateReason != CandidateReason.NotAVariable) { return; } if (!(symbolInfo.CandidateSymbols.SingleOrDefault(shouldThrow: false) is ILocalSymbol localSymbol)) { return; } if (!localSymbol.IsConst) { return; } SyntaxNode node = localSymbol.GetSyntaxOrDefault(context.CancellationToken); if (!node.IsKind(SyntaxKind.VariableDeclarator)) { return; } node = node.Parent; if (!node.IsKind(SyntaxKind.VariableDeclaration)) { return; } node = node.Parent; if (!(node is LocalDeclarationStatementSyntax localDeclaration)) { return; } SyntaxToken constModifier = localDeclaration.Modifiers.Find(SyntaxKind.ConstKeyword); if (!constModifier.IsKind(SyntaxKind.ConstKeyword)) { return; } ModifiersCodeFixRegistrator.RemoveModifier(context, diagnostic, localDeclaration, constModifier); break; } case CompilerDiagnosticIdentifiers.ReadOnlyFieldCannotBeAssignedTo: { if (!Settings.IsEnabled(diagnostic.Id, CodeFixIdentifiers.MakeFieldWritable)) { break; } SimpleAssignmentExpressionInfo simpleAssignment = SyntaxInfo.SimpleAssignmentExpressionInfo(expression.Parent); if (!simpleAssignment.Success) { return; } if (simpleAssignment.Left != expression) { return; } SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); SymbolInfo symbolInfo = semanticModel.GetSymbolInfo(expression, context.CancellationToken); if (symbolInfo.CandidateReason != CandidateReason.NotAVariable) { return; } if (!(symbolInfo.CandidateSymbols.SingleOrDefault(shouldThrow: false) is IFieldSymbol fieldSymbol)) { return; } if (fieldSymbol.DeclaredAccessibility != Accessibility.Private) { return; } if (!(fieldSymbol.GetSyntax().Parent.Parent is FieldDeclarationSyntax fieldDeclaration)) { return; } TypeDeclarationSyntax containingTypeDeclaration = fieldDeclaration.FirstAncestor <TypeDeclarationSyntax>(); if (!expression.Ancestors().Any(f => f == containingTypeDeclaration)) { return; } ModifiersCodeFixRegistrator.RemoveModifier( context, diagnostic, fieldDeclaration, SyntaxKind.ReadOnlyKeyword, title: $"Make '{fieldSymbol.Name}' writable"); break; } } } }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles) { return; } var span = context.Span; if (!span.IsEmpty) { return; } var cancellationToken = context.CancellationToken; if (cancellationToken.IsCancellationRequested) { return; } var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (model.IsFromGeneratedCode(cancellationToken)) { return; } var root = await model.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); var node = root.FindNode(span); while (node != null && !(node is MemberDeclarationSyntax)) { node = node.Parent; } if (node == null) { return; } if (!node.IsKind(SyntaxKind.MethodDeclaration) && !node.IsKind(SyntaxKind.PropertyDeclaration) && !node.IsKind(SyntaxKind.IndexerDeclaration) && !node.IsKind(SyntaxKind.EventDeclaration)) { return; } var memberDeclaration = node as MemberDeclarationSyntax; var explicitSyntax = memberDeclaration.GetExplicitInterfaceSpecifierSyntax(); if (explicitSyntax == null || !explicitSyntax.Span.Contains(span)) { return; } var enclosingSymbol = model.GetDeclaredSymbol(memberDeclaration, cancellationToken); if (enclosingSymbol == null) { return; } var containingType = enclosingSymbol.ContainingType; foreach (var member in containingType.GetMembers()) { if (member == enclosingSymbol || member.Kind != enclosingSymbol.Kind) { continue; } switch (member.Kind) { case SymbolKind.Property: var property1 = (IPropertySymbol)enclosingSymbol; var property2 = (IPropertySymbol)member; foreach (var explictProperty in property1.ExplicitInterfaceImplementations) { if (explictProperty.Name == property2.Name) { if (SignatureComparer.HaveSameSignature(property1.Parameters, property2.Parameters)) { return; } } } break; case SymbolKind.Method: var method1 = (IMethodSymbol)enclosingSymbol; var method2 = (IMethodSymbol)member; foreach (var explictMethod in method1.ExplicitInterfaceImplementations) { if (explictMethod.Name == method2.Name) { if (SignatureComparer.HaveSameSignature(method1.Parameters, method2.Parameters)) { return; } } } break; case SymbolKind.Event: var evt1 = (IEventSymbol)enclosingSymbol; var evt2 = (IEventSymbol)member; foreach (var explictProperty in evt1.ExplicitInterfaceImplementations) { if (explictProperty.Name == evt2.Name) { return; } } break; } } context.RegisterRefactoring( CodeActionFactory.Create( span, DiagnosticSeverity.Info, GettextCatalog.GetString("To implicit implementation"), t2 => { var newNode = memberDeclaration; switch (newNode.Kind()) { case SyntaxKind.MethodDeclaration: var method = (MethodDeclarationSyntax)memberDeclaration.WithoutLeadingTrivia(); newNode = method .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword))) .WithExplicitInterfaceSpecifier(null); break; case SyntaxKind.PropertyDeclaration: var property = (PropertyDeclarationSyntax)memberDeclaration.WithoutLeadingTrivia(); newNode = property .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword))) .WithExplicitInterfaceSpecifier(null); break; case SyntaxKind.IndexerDeclaration: var indexer = (IndexerDeclarationSyntax)memberDeclaration.WithoutLeadingTrivia(); newNode = indexer .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword))) .WithExplicitInterfaceSpecifier(null); break; case SyntaxKind.EventDeclaration: var evt = (EventDeclarationSyntax)memberDeclaration.WithoutLeadingTrivia(); newNode = evt .WithModifiers(SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PublicKeyword))) .WithExplicitInterfaceSpecifier(null); break; } var newRoot = root.ReplaceNode((SyntaxNode) memberDeclaration, newNode.WithAdditionalAnnotations(Formatter.Annotation).WithLeadingTrivia(memberDeclaration.GetLeadingTrivia()) ); return(Task.FromResult(document.WithSyntaxRoot(newRoot))); } ) ); }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles) { return; } var span = context.Span; if (!span.IsEmpty) { return; } var cancellationToken = context.CancellationToken; if (cancellationToken.IsCancellationRequested) { return; } var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (model.IsFromGeneratedCode(cancellationToken)) { return; } var node = root.FindNode(span) as BinaryExpressionSyntax; if (node == null || !node.OperatorToken.IsKind(SyntaxKind.QuestionQuestionToken)) { return; } context.RegisterRefactoring( CodeActionFactory.Create( span, DiagnosticSeverity.Info, GettextCatalog.GetString("Replace '??' operator with '?:' expression"), t2 => { var left = node.Left; var info = model.GetTypeInfo(left, t2); if (info.ConvertedType.IsNullableType()) { left = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, CSharpUtil.AddParensIfRequired(left), SyntaxFactory.IdentifierName("Value")); } var ternary = SyntaxFactory.ConditionalExpression( SyntaxFactory.BinaryExpression( SyntaxKind.NotEqualsExpression, node.Left, SyntaxFactory.LiteralExpression(SyntaxKind.NullLiteralExpression) ), left, node.Right ).WithAdditionalAnnotations(Formatter.Annotation); return(Task.FromResult(document.WithSyntaxRoot(root.ReplaceNode(node, ternary)))); } ) ); }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles) { return; } var span = context.Span; if (!span.IsEmpty) { return; } var cancellationToken = context.CancellationToken; if (cancellationToken.IsCancellationRequested) { return; } var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (model.IsFromGeneratedCode(cancellationToken)) { return; } var root = await model.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); var token = root.FindToken(span.Start); if (token.Parent == null) { return; } var bracketedList = token.Parent.AncestorsAndSelf().OfType <ArgumentListSyntax>().FirstOrDefault(); if (bracketedList == null) { return; } var elementAccess = bracketedList.AncestorsAndSelf().OfType <InvocationExpressionSyntax>().FirstOrDefault(); if ((elementAccess == null) || (elementAccess.Expression == null)) { return; } var elementType = model.GetTypeInfo(elementAccess.Expression); if (elementType.Type == null) { return; } if (!IsDictionary(elementType.Type as INamedTypeSymbol) && !elementType.Type.AllInterfaces.Any(IsDictionary)) { return; } context.RegisterRefactoring( CodeActionFactory.Create( span, DiagnosticSeverity.Info, string.Format(GettextCatalog.GetString("Check 'If {0}.TryGetValue({1}, val)'"), elementAccess.Expression, elementAccess.ArgumentList.Arguments.First()), t2 => { var reservedNames = model.LookupSymbols(elementAccess.SpanStart).Select(s => s.Name); string localVariableName = NameGenerator.EnsureUniqueness("val", reservedNames, true); var parentStatement = elementAccess.Parent.AncestorsAndSelf().OfType <StatementSyntax>().FirstOrDefault(); var dict = IsDictionary(elementType.Type as INamedTypeSymbol) ? elementType.Type : elementType.Type.AllInterfaces.First(IsDictionary); var tempVariableDeclaration = SyntaxFactory.LocalDeclarationStatement( SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.DimKeyword)), SyntaxFactory.SeparatedList(new[] { SyntaxFactory.VariableDeclarator(SyntaxFactory.SeparatedList(new[] { SyntaxFactory.ModifiedIdentifier(localVariableName) }), SyntaxFactory.SimpleAsClause(SyntaxFactory.ParseTypeName(dict.GetTypeArguments()[1].GetFullName())), null) })).WithTrailingTrivia(parentStatement.GetTrailingTrivia()); var newParent = SyntaxFactory.MultiLineIfBlock( SyntaxFactory.IfStatement( SyntaxFactory.Token(SyntaxKind.IfKeyword), SyntaxFactory.InvocationExpression( SyntaxFactory.MemberAccessExpression( SyntaxKind.SimpleMemberAccessExpression, elementAccess.Expression, SyntaxFactory.Token(SyntaxKind.DotToken), SyntaxFactory.IdentifierName("TryGetValue")), SyntaxFactory.ArgumentList(elementAccess.ArgumentList.Arguments) .AddArguments(SyntaxFactory.SimpleArgument(SyntaxFactory.IdentifierName(localVariableName))) ), SyntaxFactory.Token(SyntaxKind.ThenKeyword)), SyntaxFactory.List(new[] { parentStatement.ReplaceNode(elementAccess, SyntaxFactory.IdentifierName(localVariableName)) }), SyntaxFactory.List <ElseIfBlockSyntax>(), null ).WithLeadingTrivia(parentStatement.GetLeadingTrivia()); return(Task.FromResult(document.WithSyntaxRoot(root.ReplaceNode(parentStatement, new SyntaxNode[] { tempVariableDeclaration.WithAdditionalAnnotations(Formatter.Annotation), newParent.WithAdditionalAnnotations(Formatter.Annotation) })))); } ) ); }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles) { return; } var span = context.Span; if (!span.IsEmpty) { return; } var cancellationToken = context.CancellationToken; if (cancellationToken.IsCancellationRequested) { return; } var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (model.IsFromGeneratedCode(cancellationToken)) { return; } var root = await model.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); var token = root.FindToken(span.Start); if (token.Parent == null) { return; } var bracketedList = token.Parent.AncestorsAndSelf().OfType <BracketedArgumentListSyntax>().FirstOrDefault(); if (bracketedList == null) { return; } var elementAccess = bracketedList.AncestorsAndSelf().OfType <ElementAccessExpressionSyntax>().FirstOrDefault(); if (elementAccess == null) { return; } var elementType = model.GetTypeInfo(elementAccess.Expression); var type = elementType.Type; if (type == null) { return; } if (!IsCollection(type as INamedTypeSymbol) && !type.AllInterfaces.Any(IsCollection)) { return; } var argument = elementAccess.ArgumentList?.Arguments.FirstOrDefault(); if (argument == null) { return; } context.RegisterRefactoring( CodeActionFactory.Create( span, DiagnosticSeverity.Info, string.Format(GettextCatalog.GetString("Check 'if ({0}.Count > {1})'"), elementAccess.Expression, argument), t2 => { var parentStatement = elementAccess.Parent.AncestorsAndSelf().OfType <StatementSyntax>().FirstOrDefault(); var newParent = SyntaxFactory.IfStatement( SyntaxFactory.BinaryExpression( SyntaxKind.GreaterThanExpression, SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, elementAccess.Expression, SyntaxFactory.IdentifierName("Count")), argument.Expression ), parentStatement ); return(Task.FromResult(document.WithSyntaxRoot(root.ReplaceNode((SyntaxNode)parentStatement, newParent.WithAdditionalAnnotations(Formatter.Annotation))))); } ) ); }
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { var document = context.Document; if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles) return; var span = context.Span; if (!span.IsEmpty) return; var cancellationToken = context.CancellationToken; if (cancellationToken.IsCancellationRequested) return; var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); if (model.IsFromGeneratedCode(cancellationToken)) return; var root = await model.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); var token = root.FindToken(span.Start); ExpressionSyntax identifier = token.Parent as IdentifierNameSyntax; if (identifier == null) return; // If identifier is a type name, this might be a static member access or similar, don't suggest null checks on it var identifierSymbol = model.GetSymbolInfo(identifier).Symbol; if ((identifierSymbol == null) || (identifierSymbol.IsType())) return; // Identifier might be part of a MemberAccessExpression and we need to check it for null as a whole identifier = GetOuterMemberAccessExpression(identifier) ?? identifier; if ((identifier.Parent is ExpressionSyntax) && ConditionContainsNullCheck((ExpressionSyntax)identifier.Parent, identifier)) return; var identifierAncestors = identifier.Ancestors(); // Don't surround return statements with checks if (identifierAncestors.OfType<ReturnStatementSyntax>().Any()) return; // If identifier is in a conditional ternary expression, skip refactoring is case of present null check in its condition var conditionalExprParent = identifierAncestors.OfType<ConditionalExpressionSyntax>().FirstOrDefault(); if ((conditionalExprParent != null) && ConditionContainsNullCheck(conditionalExprParent.Condition, identifier)) return; // Check identifier type, don't suggest null checks for value types! var identifierType = model.GetTypeInfo(identifier).Type; if ((identifierType == null) || (identifierType.IsValueType && !identifierType.IsNullableType())) return; var statementToWrap = identifierAncestors.OfType<StatementSyntax>().FirstOrDefault(); if (statementToWrap == null) return; // No refactoring if statement is inside of a local variable declaration if ((statementToWrap is BlockSyntax) || (statementToWrap is LocalDeclarationStatementSyntax)) return; SyntaxNode newWrappedStatement = null; // Check surrounding block var surroundingStatement = statementToWrap.Ancestors().OfType<StatementSyntax>().FirstOrDefault(); if (surroundingStatement is BlockSyntax) surroundingStatement = surroundingStatement.Parent as StatementSyntax; if (surroundingStatement != null) { if (StatementWithConditionContainsNullCheck(surroundingStatement, identifier)) return; if (surroundingStatement is IfStatementSyntax surroundingIfStatement && surroundingIfStatement.Else == null) { statementToWrap = surroundingIfStatement; } } if (statementToWrap is IfStatementSyntax) { newWrappedStatement = ExtendIfConditionWithNullCheck((IfStatementSyntax)statementToWrap, identifier); } else { newWrappedStatement = SyntaxFactory.IfStatement( SyntaxFactory.BinaryExpression(SyntaxKind.NotEqualsExpression, identifier, SyntaxFactory.LiteralExpression(SyntaxKind.NullLiteralExpression)), SyntaxFactory.Block(statementToWrap).WithLeadingTrivia(statementToWrap.GetLeadingTrivia()).WithTrailingTrivia(statementToWrap.GetTrailingTrivia()) ).WithAdditionalAnnotations(Formatter.Annotation); } context.RegisterRefactoring(CodeActionFactory.Create(token.Span, DiagnosticSeverity.Info, GettextCatalog.GetString("Add null check"), t2 => { var newRoot = root.ReplaceNode(statementToWrap, newWrappedStatement); return Task.FromResult(document.WithSyntaxRoot(newRoot)); })); }
protected override IEnumerable <CodeAction> GetActions(Document document, SemanticModel semanticModel, SyntaxNode root, TextSpan span, VariableDeclaratorSyntax node, CancellationToken cancellationToken) { var variableDeclaration = node.GetAncestor <VariableDeclarationSyntax>(); var localDeclaration = node.GetAncestor <LocalDeclarationStatementSyntax>(); if (node.Initializer == null || node.Parent.Parent != null && node.Parent.Parent.IsKind(SyntaxKind.UsingStatement)) { yield break; } var symbol = semanticModel.GetDeclaredSymbol(node) as ILocalSymbol; if (symbol == null) { yield break; } if (!symbol.Type.ImplementsSpecialTypeInterface(SpecialType.System_IDisposable)) { yield break; } var containingBlock = node.GetAncestor <BlockSyntax>(); yield return(CodeActionFactory.Create(span, DiagnosticSeverity.Info, "Put inside 'using'", ct => { var insideUsing = containingBlock.Statements.SkipWhile(x => x != localDeclaration).Skip(1).ToList(); ReduceUsingBlock(semanticModel, insideUsing, symbol); var nodesToRemove = new List <SyntaxNode>(insideUsing); var beforeUsing = new List <StatementSyntax>(); if (insideUsing.Any()) { ExtractVariableDeclarationsBeforeUsing(semanticModel, beforeUsing, insideUsing, nodesToRemove, ct); } if (IsEndingWithDispose(semanticModel, insideUsing, symbol)) { nodesToRemove.Add(insideUsing.Last()); insideUsing.RemoveAt(insideUsing.Count - 1); } var usingVariableDeclaration = SyntaxFactory.VariableDeclaration(variableDeclaration.Type.WithoutTrivia(), SyntaxFactory.SingletonSeparatedList(node)); var usingNode = SyntaxFactory.UsingStatement(usingVariableDeclaration, null, SyntaxFactory.Block(insideUsing)) .WithAdditionalAnnotations(Formatter.Annotation) .WithPrependedLeadingTrivia(variableDeclaration.GetLeadingTrivia()); var newRoot = root.TrackNodes(nodesToRemove.Concat(localDeclaration)); newRoot = newRoot.RemoveNodes(newRoot.GetCurrentNodes <SyntaxNode>(nodesToRemove), SyntaxRemoveOptions.AddElasticMarker); newRoot = newRoot.InsertNodesAfter(newRoot.GetCurrentNode(localDeclaration), beforeUsing.Concat(usingNode)); if (localDeclaration.Declaration.Variables.Count > 1) { var remainingVariables = localDeclaration.Declaration.Variables.Except(new[] { node }); newRoot = newRoot.ReplaceNode(newRoot.GetCurrentNode(localDeclaration), localDeclaration.WithDeclaration(localDeclaration.Declaration.WithVariables(SyntaxFactory.SeparatedList(remainingVariables))) .WithAdditionalAnnotations(Formatter.Annotation)); } else { newRoot = newRoot.RemoveNode(newRoot.GetCurrentNode(localDeclaration), SyntaxRemoveOptions.AddElasticMarker); } return Task.FromResult(document.WithSyntaxRoot(newRoot)); })); }