diff --git a/ChangeLog.md b/ChangeLog.md index 67b36e9f96..8491d31b0d 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -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 diff --git a/src/Formatting.Analyzers.CodeFixes/CSharp/CodeFixHelpers.cs b/src/Formatting.Analyzers.CodeFixes/CSharp/CodeFixHelpers.cs index 49f51ca947..a6b3c741a3 100644 --- a/src/Formatting.Analyzers.CodeFixes/CSharp/CodeFixHelpers.cs +++ b/src/Formatting.Analyzers.CodeFixes/CSharp/CodeFixHelpers.cs @@ -703,7 +703,6 @@ internal static List GetFixListChanges( } var textChanges = new List(); - TextLineCollection lines = null; string endOfLine = DetermineEndOfLine(containingNode).ToString(); for (int i = 0; i < nodes.Count; i++) @@ -749,9 +748,9 @@ internal static List GetFixListChanges( 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(); } @@ -775,9 +774,9 @@ internal static List GetFixListChanges( 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 diff --git a/src/Formatting.Analyzers/CSharp/FixFormattingOfListAnalyzer.cs b/src/Formatting.Analyzers/CSharp/FixFormattingOfListAnalyzer.cs index 218b6e8fff..15e9cfcb08 100644 --- a/src/Formatting.Analyzers/CSharp/FixFormattingOfListAnalyzer.cs +++ b/src/Formatting.Analyzers/CSharp/FixFormattingOfListAnalyzer.cs @@ -8,7 +8,6 @@ using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Text; using Roslynator.CSharp; -using static Roslynator.CSharp.SyntaxTriviaAnalysis; namespace Roslynator.Formatting.CSharp; @@ -166,9 +165,7 @@ private static void Analyze( else { TextLineCollection lines = null; - IndentationAnalysis indentationAnalysis = IndentationAnalysis.Create(openNodeOrToken.Parent, context.GetConfigOptions()); - int indentationLength = indentationAnalysis.IncreasedIndentationLength; if (indentationLength == 0) @@ -184,10 +181,13 @@ private static void Analyze( if (TriviaBlock.FromTrailing(nodeOrToken).IsWrapped) { + if (AnalyzeBlock(nodes, indentationAnalysis, SyntaxKind.OpenBracketToken, SyntaxKind.CloseBracketToken)) + return; + if (ShouldFixIndentation(nodes[i].GetLeadingTrivia(), indentationLength)) { ReportDiagnostic(); - break; + return; } } else @@ -196,45 +196,13 @@ private static void Analyze( && 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) @@ -260,14 +228,14 @@ private static void Analyze( && trivia.Span.Length != indentationLength) { ReportDiagnostic(); - break; + return; } } } else if (lineStartIndex == token.SpanStart) { ReportDiagnostic(); - break; + return; } } } @@ -345,10 +313,49 @@ string GetTitle() throw new InvalidOperationException(); } } + + bool AnalyzeBlock( + SeparatedSyntaxList 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; @@ -356,18 +363,18 @@ internal static LambdaBlock GetLambdaBlock(SyntaxNode node, TextLineCollection l 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; } } @@ -375,16 +382,16 @@ internal static LambdaBlock GetLambdaBlock(SyntaxNode node, TextLineCollection l 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; } } } @@ -392,19 +399,18 @@ internal static LambdaBlock GetLambdaBlock(SyntaxNode node, TextLineCollection l 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); } } @@ -412,11 +418,39 @@ internal static LambdaBlock GetLambdaBlock(SyntaxNode node, TextLineCollection l 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 } } @@ -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; } diff --git a/src/Tests/Formatting.Analyzers.Tests/RCS0053FixFormattingOfListTests.cs b/src/Tests/Formatting.Analyzers.Tests/RCS0053FixFormattingOfListTests.cs index ae95f80e74..047f4ff7be 100644 --- a/src/Tests/Formatting.Analyzers.Tests/RCS0053FixFormattingOfListTests.cs +++ b/src/Tests/Formatting.Analyzers.Tests/RCS0053FixFormattingOfListTests.cs @@ -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; + } +} + +"""); + } }