Skip to content

Commit

Permalink
Remove interpolation and concatenation from RCS1197 (#1370)
Browse files Browse the repository at this point in the history
  • Loading branch information
josefpihrt committed Jan 21, 2024
1 parent 10ac107 commit 72d8f0c
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 465 deletions.
5 changes: 5 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [CLI] Spellcheck file names ([PR](https://github.com/dotnet/roslynator/pull/1368))
- `roslynator spellcheck --scope file-name`

### Changed

- Update analyzer [RCS1197](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1197) ([PR](https://github.com/dotnet/roslynator/pull/1370))
- Do not report interpolated string and string concatenation

### Fixed

- Fix analyzer [RCS1055](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1055) ([PR](https://github.com/dotnet/roslynator/pull/1361))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Roslynator.CodeFixes;
using Roslynator.CSharp.Refactorings;
using Roslynator.CSharp.Syntax;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
using static Roslynator.CSharp.CSharpFactory;
Expand Down Expand Up @@ -94,83 +93,19 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
SimpleMemberInvocationExpressionInfo invocationInfo,
CancellationToken cancellationToken)
{
SemanticModel semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

InvocationExpressionSyntax invocation = invocationInfo.InvocationExpression;
InvocationExpressionSyntax newInvocation;

bool isAppendLine = string.Equals(invocationInfo.NameText, "AppendLine", StringComparison.Ordinal);

ExpressionSyntax expression = argument.Expression;

switch (expression.Kind())
{
case SyntaxKind.InterpolatedStringExpression:
{
newInvocation = ConvertInterpolatedStringExpressionToInvocationExpression((InterpolatedStringExpressionSyntax)expression, invocationInfo, semanticModel);
break;
}
case SyntaxKind.AddExpression:
{
ImmutableArray<ExpressionSyntax> expressions = SyntaxInfo.BinaryExpressionInfo((BinaryExpressionSyntax)expression)
.AsChain()
.ToImmutableArray();

newInvocation = invocation
.ReplaceNode(invocationInfo.Name, IdentifierName("Append").WithTriviaFrom(invocationInfo.Name))
.WithArgumentList(invocation.ArgumentList.WithArguments(SingletonSeparatedList(Argument(ReplaceStringLiteralWithCharacterLiteral(expressions[0])))).WithoutTrailingTrivia());

for (int i = 1; i < expressions.Length; i++)
{
ExpressionSyntax argumentExpression = expressions[i];

string methodName;
if (i == expressions.Length - 1
&& isAppendLine
&& semanticModel
.GetTypeInfo(argumentExpression, cancellationToken)
.ConvertedType?
.SpecialType == SpecialType.System_String)
{
methodName = "AppendLine";
}
else
{
methodName = "Append";

argumentExpression = ReplaceStringLiteralWithCharacterLiteral(argumentExpression);
}

newInvocation = SimpleMemberInvocationExpression(
newInvocation,
IdentifierName(methodName),
ArgumentList(Argument(argumentExpression)));

if (i == expressions.Length - 1
&& isAppendLine
&& !string.Equals(methodName, "AppendLine", StringComparison.Ordinal))
{
newInvocation = SimpleMemberInvocationExpression(
newInvocation,
IdentifierName("AppendLine"),
ArgumentList());
}
}

break;
}
default:
{
newInvocation = CreateInvocationExpression(
(InvocationExpressionSyntax)expression,
invocation);
newInvocation = CreateInvocationExpression(
(InvocationExpressionSyntax)expression,
invocation);

if (isAppendLine)
newInvocation = SimpleMemberInvocationExpression(newInvocation, IdentifierName("AppendLine"), ArgumentList());

break;
}
}
if (isAppendLine)
newInvocation = SimpleMemberInvocationExpression(newInvocation, IdentifierName("AppendLine"), ArgumentList());

newInvocation = newInvocation
.WithTriviaFrom(invocation)
Expand All @@ -179,68 +114,6 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
return await document.ReplaceNodeAsync(invocation, newInvocation, cancellationToken).ConfigureAwait(false);
}

private static InvocationExpressionSyntax ConvertInterpolatedStringExpressionToInvocationExpression(
InterpolatedStringExpressionSyntax interpolatedString,
in SimpleMemberInvocationExpressionInfo invocationInfo,
SemanticModel semanticModel)
{
bool isVerbatim = interpolatedString.IsVerbatim();

bool isAppendLine = string.Equals(invocationInfo.NameText, "AppendLine", StringComparison.Ordinal);

InvocationExpressionSyntax invocation = invocationInfo.InvocationExpression;

InvocationExpressionSyntax newExpression = null;

SyntaxList<InterpolatedStringContentSyntax> contents = interpolatedString.Contents;

for (int i = 0; i < contents.Count; i++)
{
(SyntaxKind contentKind, string methodName, ImmutableArray<ArgumentSyntax> arguments) = ConvertInterpolatedStringToStringBuilderMethodRefactoring.Refactor(contents[i], isVerbatim);

if (i == contents.Count - 1
&& isAppendLine
&& string.Equals(methodName, "Append", StringComparison.Ordinal)
&& (contentKind == SyntaxKind.InterpolatedStringText
|| semanticModel.IsImplicitConversion(((InterpolationSyntax)contents[i]).Expression, semanticModel.Compilation.GetSpecialType(SpecialType.System_String))))
{
methodName = "AppendLine";
}
else if (methodName == "Append")
{
arguments = ReplaceStringLiteralWithCharacterLiteral(arguments);
}

if (newExpression is null)
{
arguments = arguments.Replace(arguments[0], arguments[0].WithLeadingTrivia(interpolatedString.GetLeadingTrivia()));

newExpression = invocation
.ReplaceNode(invocationInfo.Name, IdentifierName(methodName).WithTriviaFrom(invocationInfo.Name))
.WithArgumentList(invocation.ArgumentList.WithArguments(arguments.ToSeparatedSyntaxList()).WithoutTrailingTrivia());
}
else
{
newExpression = SimpleMemberInvocationExpression(
newExpression,
IdentifierName(methodName),
ArgumentList(arguments.ToSeparatedSyntaxList()));
}

if (i == contents.Count - 1
&& isAppendLine
&& !string.Equals(methodName, "AppendLine", StringComparison.Ordinal))
{
newExpression = SimpleMemberInvocationExpression(
newExpression,
IdentifierName("AppendLine"),
ArgumentList());
}
}

return newExpression;
}

private static InvocationExpressionSyntax CreateInvocationExpression(
InvocationExpressionSyntax innerInvocationExpression,
InvocationExpressionSyntax outerInvocationExpression)
Expand Down Expand Up @@ -317,37 +190,4 @@ private static InvocationExpressionSyntax CreateNewInvocationExpression(Invocati
.WithExpression(memberAccess.WithName(IdentifierName(methodName).WithTriviaFrom(memberAccess.Name)))
.WithArgumentList(argumentList);
}

private static ExpressionSyntax ReplaceStringLiteralWithCharacterLiteral(ExpressionSyntax expression)
{
if (expression.IsKind(SyntaxKind.StringLiteralExpression))
{
var literalExpression = (LiteralExpressionSyntax)expression;

if (literalExpression.Token.ValueText.Length == 1)
return SyntaxRefactorings.ReplaceStringLiteralWithCharacterLiteral(literalExpression);
}

return expression;
}

private static ImmutableArray<ArgumentSyntax> ReplaceStringLiteralWithCharacterLiteral(ImmutableArray<ArgumentSyntax> arguments)
{
ArgumentSyntax argument = arguments.SingleOrDefault(shouldThrow: false);

if (argument is not null)
{
ExpressionSyntax expression = argument.Expression;

if (expression is not null)
{
ExpressionSyntax newExpression = ReplaceStringLiteralWithCharacterLiteral(expression);

if (newExpression != expression)
arguments = arguments.Replace(argument, argument.WithExpression(newExpression));
}
}

return arguments;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,33 +69,6 @@ public static void Analyze(SyntaxNodeAnalysisContext context, in SimpleMemberInv

SyntaxKind expressionKind = expression.Kind();

switch (expressionKind)
{
case SyntaxKind.InterpolatedStringExpression:
{
if (((CSharpCompilation)context.Compilation).LanguageVersion <= LanguageVersion.CSharp9
|| !context.SemanticModel.HasConstantValue(expression, context.CancellationToken))
{
ReportDiagnostic(argument);
}

return;
}
case SyntaxKind.AddExpression:
{
BinaryExpressionInfo binaryExpressionInfo = SyntaxInfo.BinaryExpressionInfo((BinaryExpressionSyntax)expression);

if (binaryExpressionInfo.Success
&& binaryExpressionInfo.AsChain().Reverse().IsStringConcatenation(context.SemanticModel, context.CancellationToken)
&& !context.SemanticModel.GetConstantValue(expression, context.CancellationToken).HasValue)
{
ReportDiagnostic(argument);
}

return;
}
}

if (expressionKind != SyntaxKind.InvocationExpression)
return;

Expand Down

0 comments on commit 72d8f0c

Please sign in to comment.