Skip to content

Commit

Permalink
Fix analyzer RCS0053 (#1512)
Browse files Browse the repository at this point in the history
  • Loading branch information
josefpihrt authored Sep 13, 2024
1 parent 6a0519c commit 4130b73
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 75 deletions.
1 change: 1 addition & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fix analyzer [RCS1214](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1214) ([PR](https://github.com/dotnet/roslynator/pull/1500))
- Fix analyzer [RCS1018](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1018) ([PR](https://github.com/dotnet/roslynator/pull/1510))
- Fix analyzer [RCS1264](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1264) ([PR](https://github.com/dotnet/roslynator/pull/1511))
- Fix analyzer [RCS0053](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS0053) ([PR](https://github.com/dotnet/roslynator/pull/1512))

### Changed

Expand Down
9 changes: 4 additions & 5 deletions src/Formatting.Analyzers.CodeFixes/CSharp/CodeFixHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,6 @@ internal static List<TextChange> GetFixListChanges<TNode>(
}

var textChanges = new List<TextChange>();
TextLineCollection lines = null;
string endOfLine = DetermineEndOfLine(containingNode).ToString();

for (int i = 0; i < nodes.Count; i++)
Expand Down Expand Up @@ -749,9 +748,9 @@ internal static List<TextChange> GetFixListChanges<TNode>(
if (nodes.Count == 1
&& node is ArgumentSyntax argument)
{
LambdaBlock lambdaBlock = GetLambdaBlock(argument, lines ??= argument.SyntaxTree.GetText(cancellationToken).Lines);
BracesBlock bracesBlock = GetBracesBlock(argument);

if (lambdaBlock.Block is not null)
if (!bracesBlock.Token.IsKind(SyntaxKind.None))
increasedIndentation = indentationAnalysis.Indentation.ToString();
}

Expand All @@ -775,9 +774,9 @@ internal static List<TextChange> GetFixListChanges<TNode>(
if (!indentations.Any())
continue;

LambdaBlock lambdaBlock2 = GetLambdaBlock(node, lines ??= node.SyntaxTree.GetText(cancellationToken).Lines);
BracesBlock bracesBlock2 = GetBracesBlock(node);

bool isLambdaBlockWithOpenBraceAtEndOfLine = lambdaBlock2.Token == indentations.Last().Token;
bool isLambdaBlockWithOpenBraceAtEndOfLine = bracesBlock2.Token == indentations.Last().Token;

int baseIndentationLength = (isLambdaBlockWithOpenBraceAtEndOfLine)
? indentations.Last().Span.Length
Expand Down
171 changes: 101 additions & 70 deletions src/Formatting.Analyzers/CSharp/FixFormattingOfListAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Text;
using Roslynator.CSharp;
using static Roslynator.CSharp.SyntaxTriviaAnalysis;

namespace Roslynator.Formatting.CSharp;

Expand Down Expand Up @@ -166,9 +165,7 @@ private static void Analyze<TNode>(
else
{
TextLineCollection lines = null;

IndentationAnalysis indentationAnalysis = IndentationAnalysis.Create(openNodeOrToken.Parent, context.GetConfigOptions());

int indentationLength = indentationAnalysis.IncreasedIndentationLength;

if (indentationLength == 0)
Expand All @@ -184,10 +181,13 @@ private static void Analyze<TNode>(

if (TriviaBlock.FromTrailing(nodeOrToken).IsWrapped)
{
if (AnalyzeBlock(nodes, indentationAnalysis, SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken))
return;

if (ShouldFixIndentation(nodes[i].GetLeadingTrivia(), indentationLength))
{
ReportDiagnostic();
break;
return;
}
}
else
Expand All @@ -196,45 +196,13 @@ private static void Analyze<TNode>(
&& ShouldWrapAndIndent(context.Node, i))
{
ReportDiagnostic();
break;
return;
}

if (nodes.Count == 1
&& first.IsKind(SyntaxKind.Argument))
{
var argument = (ArgumentSyntax)(SyntaxNode)first;

LambdaBlock lambdaBlock = GetLambdaBlock(argument, lines ??= first.SyntaxTree.GetText().Lines);

if (lambdaBlock.Block is not null)
{
SyntaxToken token = lambdaBlock.Token;
SyntaxTriviaList leading = token.LeadingTrivia;

if (leading.Any())
{
SyntaxTrivia trivia = leading.Last();

if (trivia.IsWhitespaceTrivia()
&& trivia.SpanStart == lambdaBlock.LineStartIndex
&& trivia.Span.Length != indentationAnalysis.IndentationLength)
{
ReportDiagnostic();
break;
}
}
else if (lambdaBlock.LineStartIndex == token.SpanStart)
{
ReportDiagnostic();
break;
}

return;
}
}
if (AnalyzeBlock(nodes, indentationAnalysis, SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken))
return;

if (lines is null)
lines = first.SyntaxTree.GetText().Lines;
lines ??= first.SyntaxTree.GetText().Lines;

int lineIndex = lines.IndexOf(span.Start);
if (lineIndex < lines.Count - 1)
Expand All @@ -260,14 +228,14 @@ private static void Analyze<TNode>(
&& trivia.Span.Length != indentationLength)
{
ReportDiagnostic();
break;
return;
}
}
}
else if (lineStartIndex == token.SpanStart)
{
ReportDiagnostic();
break;
return;
}
}
}
Expand Down Expand Up @@ -345,78 +313,144 @@ string GetTitle()
throw new InvalidOperationException();
}
}

bool AnalyzeBlock(
SeparatedSyntaxList<TNode> nodes,
IndentationAnalysis indentationAnalysis,
SyntaxKind kind1,
SyntaxKind kind2)
{
if (nodes.Count == 1
&& nodes[0].IsKind(SyntaxKind.Argument))
{
BracesBlock block = GetBracesBlock(nodes[0]);

if (block.Token.IsKind(kind1, kind2))
{
SyntaxToken token = block.Token;
SyntaxTriviaList leading = token.LeadingTrivia;

if (leading.Any())
{
SyntaxTrivia trivia = leading.Last();

if (trivia.IsWhitespaceTrivia()
&& trivia.SpanStart == block.LineStartIndex
&& trivia.Span.Length != indentationAnalysis.IndentationLength)
{
ReportDiagnostic();
}
}
else if (block.LineStartIndex == token.SpanStart)
{
ReportDiagnostic();
}
return true;
}
}

return false;
}
}

internal static LambdaBlock GetLambdaBlock(SyntaxNode node, TextLineCollection lines)
internal static BracesBlock GetBracesBlock(SyntaxNode node)
{
TextLineCollection lines = node.SyntaxTree.GetText().Lines;
TextLine line = lines.GetLineFromPosition(node.SpanStart);

int startIndex = line.End;

if (!node.FullSpan.Contains(startIndex))
return default;

SyntaxToken openBrace = node.FindToken(startIndex);
BlockSyntax block = null;
SyntaxToken openToken = node.FindToken(startIndex);
SyntaxNode block = null;
var isOpenBraceAtEndOfLine = false;

if (IsBraceToken(openBrace, SyntaxKind.OpenBraceToken))
if (AnalyzeToken(openToken, isOpen: true))
{
SyntaxTriviaList trailing = openBrace.TrailingTrivia;
SyntaxTriviaList trailing = openToken.TrailingTrivia;

if (trailing.Any()
&& trailing.Span.Contains(startIndex))
{
block = (BlockSyntax)openBrace.Parent;
block = openToken.Parent;
isOpenBraceAtEndOfLine = true;
}
}

if (block is null)
{
startIndex = line.EndIncludingLineBreak;
openBrace = node.FindToken(startIndex);
openToken = node.FindToken(startIndex);

if (IsBraceToken(openBrace, SyntaxKind.OpenBraceToken))
if (AnalyzeToken(openToken, isOpen: true))
{
SyntaxTriviaList leading = openBrace.LeadingTrivia;
SyntaxTriviaList leading = openToken.LeadingTrivia;

if ((leading.Any() && leading.Span.Contains(startIndex))
|| (!leading.Any() && openBrace.SpanStart == startIndex))
|| (!leading.Any() && openToken.SpanStart == startIndex))
{
block = (BlockSyntax)openBrace.Parent;
block = openToken.Parent;
}
}
}

if (block is not null)
{
int endIndex = lines.GetLineFromPosition(node.Span.End).Start;
SyntaxToken closeBrace = node.FindToken(endIndex);
SyntaxToken closeToken = node.FindToken(endIndex);

if (IsBraceToken(closeBrace, SyntaxKind.CloseBraceToken)
&& object.ReferenceEquals(block, closeBrace.Parent))
if (AnalyzeToken(closeToken, isOpen: false)
&& object.ReferenceEquals(block, closeToken.Parent))
{
SyntaxTriviaList leading = closeBrace.LeadingTrivia;
SyntaxTriviaList leading = closeToken.LeadingTrivia;

if ((leading.Any() && leading.Span.Contains(endIndex))
|| (!leading.Any() && closeBrace.SpanStart == endIndex))
|| (!leading.Any() && closeToken.SpanStart == endIndex))
{
return new LambdaBlock(
block,
(isOpenBraceAtEndOfLine) ? closeBrace : openBrace,
return new BracesBlock(
(isOpenBraceAtEndOfLine) ? closeToken : openToken,
(isOpenBraceAtEndOfLine) ? endIndex : startIndex);
}
}
}

return default;

static bool IsBraceToken(SyntaxToken token, SyntaxKind kind)
static bool AnalyzeToken(SyntaxToken token, bool isOpen)
{
return token.IsKind(kind)
&& token.IsParentKind(SyntaxKind.Block)
&& CSharpFacts.IsAnonymousFunctionExpression(token.Parent.Parent.Kind());
if (token.IsKind((isOpen) ? SyntaxKind.OpenBraceToken : SyntaxKind.CloseBraceToken))
{
if (token.IsParentKind(SyntaxKind.Block)
&& (CSharpFacts.IsAnonymousFunctionExpression(token.Parent.Parent.Kind())))
{
return true;
}

if (token.IsParentKind(SyntaxKind.ObjectInitializerExpression)
&& token.Parent.Parent.IsKind(
SyntaxKind.ObjectCreationExpression,
SyntaxKind.AnonymousObjectCreationExpression,
SyntaxKind.ImplicitObjectCreationExpression))
{
return true;
}

if (token.IsParentKind(SyntaxKind.ArrayInitializerExpression)
&& token.Parent.Parent.IsKind(
SyntaxKind.ArrayCreationExpression,
SyntaxKind.ImplicitArrayCreationExpression))
{
return true;
}
}
#if ROSLYN_4_7
return token.IsKind((isOpen) ? SyntaxKind.OpenBracketToken : SyntaxKind.CloseBracketToken)
&& token.IsParentKind(SyntaxKind.CollectionExpression);
#else
return false;
#endif
}
}

Expand All @@ -439,17 +473,14 @@ internal static bool ShouldWrapAndIndent(SyntaxNode node, int index)
return true;
}

internal readonly struct LambdaBlock
internal readonly struct BracesBlock
{
public LambdaBlock(BlockSyntax block, SyntaxToken token, int lineStartIndex)
public BracesBlock(SyntaxToken token, int lineStartIndex)
{
Block = block;
Token = token;
LineStartIndex = lineStartIndex;
}

public BlockSyntax Block { get; }

public SyntaxToken Token { get; }

public int LineStartIndex { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1296,4 +1296,69 @@ internal static void Method(Foo[] foo)
}
", }, options: Options.WithCompilationOptions(Options.CompilationOptions.WithOutputKind(OutputKind.ConsoleApplication)));
}

[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.FixFormattingOfList)]
public async Task TestNoDiagnostic_Multiline_ObjectInitializer()
{
await VerifyNoDiagnosticAsync("""
class C
{
public C() { }

public C(C value) { }

public string P { get; set; }

C M()
{
return new C(new C
{
P = ""
});
}
}
""");
}

[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.FixFormattingOfList)]
public async Task TestNoDiagnostic_Multiline_CollectionExpression()
{
await VerifyNoDiagnosticAsync("""

class C
{
public C P { get; set; }

public string M1(string[] values)
{
string x =
P
.M1(
[
// x
null,
])
.ToString();

return x;
}

public string M2(string value, string[] values)
{
string x =
P
.M2(
"",
[
// x
null,
])
.ToString();

return x;
}
}

""");
}
}

0 comments on commit 4130b73

Please sign in to comment.