diff --git a/ChangeLog.md b/ChangeLog.md index 7ac863a75c..048cc09096 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix analyzer [RCS1055](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1055) ([PR](https://github.com/dotnet/roslynator/pull/1361)) - Fix analyzer [RCS1261](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1261) ([PR](https://github.com/dotnet/roslynator/pull/1374)) +- Fix analyzer [RCS0056](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS0056) ([PR](https://github.com/dotnet/roslynator/pull/1373)) ## [4.9.0] - 2024-01-10 diff --git a/src/Formatting.Analyzers/CSharp/LineIsTooLongAnalyzer.cs b/src/Formatting.Analyzers/CSharp/LineIsTooLongAnalyzer.cs index aaf06c3efe..b243558c22 100644 --- a/src/Formatting.Analyzers/CSharp/LineIsTooLongAnalyzer.cs +++ b/src/Formatting.Analyzers/CSharp/LineIsTooLongAnalyzer.cs @@ -82,23 +82,76 @@ private static void AnalyzeSyntaxTree(SyntaxTreeAnalysisContext context) if (line.Span.Length <= maxLength) continue; + int start = line.Start; int end = line.End; SyntaxToken token = root.FindToken(end); - if (token.IsKind(SyntaxKind.StringLiteralToken)) + if (token.IsKind(SyntaxKind.None) + || token.Span.End < start) { - TextSpan span = token.Span; + continue; + } + + SyntaxToken token2 = token; - if (span.End == end) + if (token2.IsKind(SyntaxKind.CommaToken, SyntaxKind.SemicolonToken)) + token2 = token2.GetPreviousToken(); + + if (!token2.IsKind(SyntaxKind.None) + && token2.Span.End >= start) + { + while (token2.IsKind( + SyntaxKind.CloseParenToken, + SyntaxKind.CloseBraceToken, + SyntaxKind.CloseBracketToken)) { - if (span.Length >= maxLength) - continue; + token2 = token2.GetPreviousToken(); } - else if (span.Contains(end) - && end - span.Start >= maxLength) + + if (token2.IsKind( + SyntaxKind.StringLiteralToken, +#if ROSLYN_4_2 + SyntaxKind.InterpolatedRawStringEndToken, +#endif + SyntaxKind.InterpolatedStringEndToken)) { - continue; + SyntaxNode parent = token2.Parent; + + if (parent.SpanStart <= start) + continue; + + token2 = parent.GetFirstToken().GetPreviousToken(); + + if (token2.IsKind(SyntaxKind.None) + || token2.Span.End < start) + { + continue; + } + + if (parent.IsParentKind( + SyntaxKind.ArrowExpressionClause, + SyntaxKind.Argument, + SyntaxKind.AttributeArgument)) + { + SyntaxToken firstToken = parent.Parent.GetFirstToken(); + + if (firstToken.SpanStart >= start) + { + SyntaxToken token3 = firstToken.GetPreviousToken(); + + if (token3.IsKind(SyntaxKind.None) + || token3.Span.End < start) + { + continue; + } + } + } + + if (parent.Span.End > end) + { + i = lines.IndexOf(parent.Span.End) - 1; + } } } diff --git a/src/Tests/Formatting.Analyzers.Tests/RCS0056LineIsTooLongTests.cs b/src/Tests/Formatting.Analyzers.Tests/RCS0056LineIsTooLongTests.cs index 5b7547ee59..8774d70d25 100644 --- a/src/Tests/Formatting.Analyzers.Tests/RCS0056LineIsTooLongTests.cs +++ b/src/Tests/Formatting.Analyzers.Tests/RCS0056LineIsTooLongTests.cs @@ -1000,12 +1000,12 @@ class C [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.LineIsTooLong)] public async Task TestNoFix_ExpressionBody_AlreadyWrapped() { - await VerifyDiagnosticAndNoFixAsync( + await VerifyNoDiagnosticAsync( @" class C { string M(object p) -[| => ""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"";|] + => ""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx""; } "); } @@ -1013,11 +1013,11 @@ string M(object p) [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.LineIsTooLong)] public async Task TestNoFix_ExpressionBody_AlreadyWrapped2() { - await VerifyDiagnosticAndNoFixAsync(@" + await VerifyNoDiagnosticAsync(@" class C { string M(object p) => -[| ""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"";|] + ""xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx""; } "); } @@ -1180,4 +1180,112 @@ void M() } "); } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.LineIsTooLong)] + public async Task TestNoDiagnostic_LongStringLiteral_Argument() + { + await VerifyNoDiagnosticAsync(""" +class C +{ + static void M(string x, string y) + { + C.M( + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + } +} +"""); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.LineIsTooLong)] + public async Task TestNoDiagnostic_LongStringLiteral_AttributeArgument() + { + await VerifyNoDiagnosticAsync(""" +using System; + +class C +{ + [Obsolete( + "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")] + static void M() + { + } +} +"""); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.LineIsTooLong)] + public async Task TestNoDiagnostic_LongStringLiteral_AttributeArgument2() + { + await VerifyNoDiagnosticAsync(""" +using System; + +class C +{ + [My( + Value = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx")] + static void M() + { + } +} + +class MyAttribute : Attribute +{ + public string Value { get; set; } +} +"""); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.LineIsTooLong)] + public async Task TestNoDiagnostic_LongVerbatimStringLiteral() + { + await VerifyNoDiagnosticAsync(""" +class C +{ + static void M(string x, int y) + { + C.M( + @" + + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + 0); + } +} +"""); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.LineIsTooLong)] + public async Task TestNoDiagnostic_LongInterpolatedString() + { + await VerifyNoDiagnosticAsync(""" +class C +{ + static void M(string x, string y) + { + C.M( + $"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + $"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"); + } +} +"""); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.LineIsTooLong)] + public async Task TestNoDiagnostic_LongRawInterpolatedString() + { + await VerifyNoDiagnosticAsync("""" +class C +{ + static void M(string x, int y) + { + C.M( + $""" + + xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + """, + 0); + } +} +""""); + } }