diff --git a/ChangeLog.md b/ChangeLog.md index bef300d235..3885def36e 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add SECURITY.md ([#1147](https://github.com/josefpihrt/roslynator/pull/1147)) - Add custom FixAllProvider for [RCS1014](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RCS1014.md) ([#1070](https://github.com/JosefPihrt/Roslynator/pull/1070)). +- Add more cases to [RCS1097](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RCS1097.md) ([#1160](https://github.com/JosefPihrt/Roslynator/pull/1160)). ### Fixed diff --git a/src/Analyzers/CSharp/Analysis/RemoveRedundantToStringCallAnalysis.cs b/src/Analyzers/CSharp/Analysis/RemoveRedundantToStringCallAnalysis.cs index 4ca091a00d..ecab35e2f4 100644 --- a/src/Analyzers/CSharp/Analysis/RemoveRedundantToStringCallAnalysis.cs +++ b/src/Analyzers/CSharp/Analysis/RemoveRedundantToStringCallAnalysis.cs @@ -48,12 +48,7 @@ private static bool IsFixable( IMethodSymbol methodSymbol = semanticModel.GetMethodSymbol(invocationExpression, cancellationToken); - if (methodSymbol?.DeclaredAccessibility == Accessibility.Public - && !methodSymbol.IsStatic - && !methodSymbol.IsGenericMethod - && string.Equals(methodSymbol.Name, "ToString", StringComparison.Ordinal) - && methodSymbol.ReturnType.SpecialType == SpecialType.System_String - && !methodSymbol.Parameters.Any()) + if (IsToString(methodSymbol)) { INamedTypeSymbol containingType = methodSymbol.ContainingType; @@ -64,15 +59,38 @@ private static bool IsFixable( if (containingType.SpecialType == SpecialType.System_String) return true; - if (invocationExpression.WalkUpParentheses().IsParentKind(SyntaxKind.Interpolation) - && IsNotHidden(methodSymbol, containingType)) + ExpressionSyntax expression = invocationExpression.WalkUpParentheses(); + switch (expression.Parent.Kind()) { - return true; + case SyntaxKind.Interpolation: + { + return IsNotHidden(methodSymbol, containingType); + } + case SyntaxKind.AddExpression: + { + var addExpression = (BinaryExpressionSyntax)expression.Parent; + if (addExpression.Right == expression) + return semanticModel.GetTypeInfo(addExpression.Left, cancellationToken).Type?.SpecialType == SpecialType.System_String; + + return semanticModel.GetTypeInfo(addExpression.Right, cancellationToken).Type?.SpecialType == SpecialType.System_String + && (addExpression.Right.WalkDownParentheses() is not InvocationExpressionSyntax invocationExpression2 + || !IsToString(semanticModel.GetMethodSymbol(invocationExpression2, cancellationToken))); + } } } } return false; + + static bool IsToString(IMethodSymbol methodSymbol) + { + return methodSymbol?.DeclaredAccessibility == Accessibility.Public + && !methodSymbol.IsStatic + && !methodSymbol.IsGenericMethod + && string.Equals(methodSymbol.Name, "ToString", StringComparison.Ordinal) + && methodSymbol.ReturnType.SpecialType == SpecialType.System_String + && !methodSymbol.Parameters.Any(); + } } private static bool IsNotHidden(IMethodSymbol methodSymbol, INamedTypeSymbol containingType) diff --git a/src/Tests/Analyzers.Tests/RCS1097RemoveRedundantToStringCallTests.cs b/src/Tests/Analyzers.Tests/RCS1097RemoveRedundantToStringCallTests.cs index 12f401c2be..66e590a928 100644 --- a/src/Tests/Analyzers.Tests/RCS1097RemoveRedundantToStringCallTests.cs +++ b/src/Tests/Analyzers.Tests/RCS1097RemoveRedundantToStringCallTests.cs @@ -80,6 +80,32 @@ void M() "); } + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.RemoveRedundantToStringCall)] + public async Task Test_PlusString() + { + await VerifyDiagnosticAndFixAsync(@" +class C +{ + void M(object o) + { + string s = """" + o[|.ToString()|]; + string s2 = o[|.ToString()|] + """"; + string s3 = o.ToString() + o[|.ToString()|]; + } +} +", @" +class C +{ + void M(object o) + { + string s = """" + o; + string s2 = o + """"; + string s3 = o.ToString() + o; + } +} +"); + } + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.RemoveRedundantToStringCall)] public async Task Test_Interpolation() {