Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove interpolation and concatenation from RCS1197 #1370

Merged
merged 3 commits into from
Jan 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 static async Task<Document> RefactorAsync(
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 static async Task<Document> RefactorAsync(
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
Loading