public static async Task <Document> RefactorAsync( Document document, BinaryExpressionSyntax binaryExpression, CancellationToken cancellationToken) { SemanticModel semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); StringConcatenationExpression concatenation; if (StringConcatenationExpression.TryCreate(binaryExpression, semanticModel, out concatenation)) { ExpressionSyntax newNode = null; if (concatenation.ContainsLiteralExpression) { if (concatenation.ContainsVerbatim && ContainsMultiLine(concatenation, cancellationToken)) { newNode = concatenation.ToMultilineStringLiteral(); } else { newNode = concatenation.ToStringLiteral(); } } else { newNode = concatenation.ToInterpolatedString(); } newNode = newNode.WithTriviaFrom(binaryExpression); return(await document.ReplaceNodeAsync(binaryExpression, newNode, cancellationToken).ConfigureAwait(false)); } Debug.Fail(binaryExpression.ToString()); return(document); }
private static async Task <Document> RefactorAsync( Document document, StringConcatenationExpression concatenation, StatementSyntax statement, CancellationToken cancellationToken) { SemanticModel semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); string name = NameGenerator.Default.EnsureUniqueLocalName(DefaultNames.StringBuilderVariable, semanticModel, statement.SpanStart, cancellationToken: cancellationToken); IdentifierNameSyntax stringBuilderName = IdentifierName(name); TypeSyntax type = semanticModel.GetTypeByMetadataName(MetadataNames.System_Text_StringBuilder).ToMinimalTypeSyntax(semanticModel, statement.SpanStart); var statements = new List <StatementSyntax>() { LocalDeclarationStatement(VarType(), Identifier(name).WithRenameAnnotation(), ObjectCreationExpression(type, ArgumentList())).WithLeadingTrivia(statement.GetLeadingTrivia()) }; ImmutableArray <ExpressionSyntax> expressions = concatenation.Expressions; ExpressionSyntax newInvocation = null; for (int i = 0; i < expressions.Length; i++) { if (expressions[i].IsKind(SyntaxKind.InterpolatedStringExpression)) { var interpolatedString = (InterpolatedStringExpressionSyntax)expressions[i]; bool isVerbatim = interpolatedString.IsVerbatim(); SyntaxList <InterpolatedStringContentSyntax> contents = interpolatedString.Contents; for (int j = 0; j < contents.Count; j++) { InterpolatedStringContentConversion conversion = InterpolatedStringContentConversion.Create(contents[j], isVerbatim); newInvocation = SimpleMemberInvocationExpression( newInvocation ?? stringBuilderName, IdentifierName(conversion.Name), ArgumentList(conversion.Arguments)); } } else { newInvocation = SimpleMemberInvocationExpression( newInvocation ?? stringBuilderName, IdentifierName("Append"), Argument(expressions[i].WithoutTrivia())); } } statements.Add(ExpressionStatement(newInvocation)); statements.Add(statement .ReplaceNode(concatenation.OriginalExpression, SimpleMemberInvocationExpression(stringBuilderName, IdentifierName("ToString"))) .WithTrailingTrivia(statement.GetTrailingTrivia()) .WithoutLeadingTrivia()); if (EmbeddedStatementHelper.IsEmbeddedStatement(statement)) { BlockSyntax block = Block(statements).WithFormatterAnnotation(); return(await document.ReplaceNodeAsync(statement, block, cancellationToken).ConfigureAwait(false)); } else { for (int i = 0; i < statements.Count; i++) { statements[i] = statements[i].WithFormatterAnnotation(); } return(await document.ReplaceNodeAsync(statement, statements, cancellationToken).ConfigureAwait(false)); } }
private static void RegisterRefactoring(RefactoringContext context, StringConcatenationExpression concatenation, StatementSyntax statement) { context.RegisterRefactoring( "Use StringBuilder instead of concatenation", cancellationToken => RefactorAsync(context.Document, concatenation, statement, cancellationToken)); }
public static async Task ComputeRefactoringsAsync(RefactoringContext context, BinaryExpressionSyntax binaryExpression) { if (context.IsRefactoringEnabled(RefactoringIdentifiers.NegateOperator)) { SyntaxToken operatorToken = binaryExpression.OperatorToken; if (operatorToken.Span.Contains(context.Span) && NegateOperatorRefactoring.CanBeNegated(operatorToken)) { context.RegisterRefactoring( "Negate operator", cancellationToken => NegateOperatorRefactoring.RefactorAsync(context.Document, operatorToken, cancellationToken)); } } if (context.IsRefactoringEnabled(RefactoringIdentifiers.FormatBinaryExpression)) { FormatBinaryExpressionRefactoring.ComputeRefactorings(context, binaryExpression); } if (context.IsRefactoringEnabled(RefactoringIdentifiers.NegateBinaryExpression)) { NegateBinaryExpressionRefactoring.ComputeRefactoring(context, binaryExpression); } if (context.IsRefactoringEnabled(RefactoringIdentifiers.ExpandCoalesceExpression) && binaryExpression.OperatorToken.Span.Contains(context.Span)) { ExpandCoalesceExpressionRefactoring.ComputeRefactoring(context, binaryExpression); } if (context.IsAnyRefactoringEnabled( RefactoringIdentifiers.JoinStringExpressions, RefactoringIdentifiers.UseStringBuilderInsteadOfConcatenation) && context.Span.IsBetweenSpans(binaryExpression) && binaryExpression.IsKind(SyntaxKind.AddExpression)) { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); StringConcatenationExpression concatenation; if (StringConcatenationExpression.TryCreate(binaryExpression, semanticModel, out concatenation, context.CancellationToken)) { if (context.IsRefactoringEnabled(RefactoringIdentifiers.JoinStringExpressions)) { JoinStringExpressionsRefactoring.ComputeRefactoring(context, concatenation); } if (context.IsRefactoringEnabled(RefactoringIdentifiers.UseStringBuilderInsteadOfConcatenation)) { UseStringBuilderInsteadOfConcatenationRefactoring.ComputeRefactoring(context, concatenation); } } } if (context.IsRefactoringEnabled(RefactoringIdentifiers.SwapExpressionsInBinaryExpression) && context.Span.IsBetweenSpans(binaryExpression)) { SwapExpressionsInBinaryExpressionRefactoring.ComputeRefactoring(context, binaryExpression); } if (context.IsRefactoringEnabled(RefactoringIdentifiers.ReplaceAsWithCast) && context.Span.IsEmptyAndContainedInSpanOrBetweenSpans(binaryExpression)) { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); if (ReplaceAsWithCastRefactoring.CanRefactor(binaryExpression, semanticModel, context.CancellationToken)) { context.RegisterRefactoring( "Replace as with cast", cancellationToken => ReplaceAsWithCastRefactoring.RefactorAsync(context.Document, binaryExpression, context.CancellationToken)); } } if (context.IsRefactoringEnabled(RefactoringIdentifiers.NegateIsExpression)) { NegateIsExpressionRefactoring.ComputeRefactoring(context, binaryExpression); } if (context.Span.IsContainedInSpanOrBetweenSpans(binaryExpression.OperatorToken)) { if (context.IsRefactoringEnabled(RefactoringIdentifiers.ReplaceEqualsExpressionWithStringEquals)) { await ReplaceEqualsExpressionWithStringEqualsRefactoring.ComputeRefactoringAsync(context, binaryExpression).ConfigureAwait(false); } if (context.IsAnyRefactoringEnabled( RefactoringIdentifiers.ReplaceEqualsExpressionWithStringIsNullOrEmpty, RefactoringIdentifiers.ReplaceEqualsExpressionWithStringIsNullOrWhiteSpace)) { await ReplaceEqualsExpressionRefactoring.ComputeRefactoringsAsync(context, binaryExpression).ConfigureAwait(false); } } if (!context.Span.IsBetweenSpans(binaryExpression) && context.IsAnyRefactoringEnabled( RefactoringIdentifiers.ExtractExpressionFromCondition, RefactoringIdentifiers.JoinStringExpressions)) { BinaryExpressionSelection binaryExpressionSelection = BinaryExpressionSelection.Create(binaryExpression, context.Span); if (binaryExpressionSelection.Expressions.Length > 1) { if (context.IsRefactoringEnabled(RefactoringIdentifiers.ExtractExpressionFromCondition)) { ExtractConditionRefactoring.ComputeRefactoring(context, binaryExpressionSelection); } if (context.IsRefactoringEnabled(RefactoringIdentifiers.JoinStringExpressions) && binaryExpression.IsKind(SyntaxKind.AddExpression)) { SemanticModel semanticModel = await context.GetSemanticModelAsync().ConfigureAwait(false); StringConcatenationExpression concatenation; if (StringConcatenationExpression.TryCreate(binaryExpressionSelection, semanticModel, out concatenation, context.CancellationToken)) { JoinStringExpressionsRefactoring.ComputeRefactoring(context, concatenation); } } } } }
public static void Analyze(SyntaxNodeAnalysisContext context, MemberInvocationExpression memberInvocation) { INamedTypeSymbol stringBuilderSymbol = context.GetTypeByMetadataName(MetadataNames.System_Text_StringBuilder); if (stringBuilderSymbol != null) { InvocationExpressionSyntax invocationExpression = memberInvocation.InvocationExpression; MethodInfo methodInfo; if (context.SemanticModel.TryGetMethodInfo(invocationExpression, out methodInfo, context.CancellationToken) && !methodInfo.IsExtensionMethod && methodInfo.ContainingType?.Equals(stringBuilderSymbol) == true) { ImmutableArray <IParameterSymbol> parameters = methodInfo.Parameters; SeparatedSyntaxList <ArgumentSyntax> arguments = memberInvocation.ArgumentList.Arguments; if (parameters.Length == 1 && arguments.Count == 1 && methodInfo.IsName("Append", "AppendLine")) { ArgumentSyntax argument = arguments.First(); ExpressionSyntax expression = argument.Expression; SyntaxKind expressionKind = expression.Kind(); switch (expressionKind) { case SyntaxKind.InterpolatedStringExpression: { context.ReportDiagnostic(DiagnosticDescriptors.OptimizeStringBuilderAppendCall, argument, methodInfo.Name); return; } case SyntaxKind.AddExpression: { StringConcatenationExpression concatenation; if (StringConcatenationExpression.TryCreate((BinaryExpressionSyntax)expression, context.SemanticModel, out concatenation)) { context.ReportDiagnostic(DiagnosticDescriptors.OptimizeStringBuilderAppendCall, argument, methodInfo.Name); return; } break; } default: { if (expressionKind == SyntaxKind.InvocationExpression && IsFixable((InvocationExpressionSyntax)expression, context.SemanticModel, context.CancellationToken)) { context.ReportDiagnostic(DiagnosticDescriptors.OptimizeStringBuilderAppendCall, argument, methodInfo.Name); return; } if (methodInfo.IsName("Append") && parameters.Length == 1 && parameters[0].Type.IsObject() && context.SemanticModel.GetTypeSymbol(argument.Expression, context.CancellationToken).IsValueType) { context.ReportDiagnostic(DiagnosticDescriptors.AvoidBoxingOfValueType, argument); return; } break; } } } else if (parameters.Length > 1 && methodInfo.IsName("Insert") && methodInfo.HasParameters(SpecialType.System_Int32, SpecialType.System_Object) && context.SemanticModel .GetTypeSymbol(arguments[1].Expression, context.CancellationToken) .IsValueType) { context.ReportDiagnostic(DiagnosticDescriptors.AvoidBoxingOfValueType, arguments[1]); } } } }