From 83cd52763a248c053041b6dc68f99e456f02969d Mon Sep 17 00:00:00 2001 From: Josef Pihrt Date: Sat, 26 Mar 2022 14:16:53 +0100 Subject: [PATCH] Improve indentation analysis (RCS0054) --- src/CSharp/CSharp/IndentationAnalysis.cs | 71 +++++++++++++++++ .../FixFormattingOfCallChainAnalyzer.cs | 20 ++++- .../RCS0054FixFormattingOfCallChainTests.cs | 77 +++++++++++++++++++ 3 files changed, 166 insertions(+), 2 deletions(-) diff --git a/src/CSharp/CSharp/IndentationAnalysis.cs b/src/CSharp/CSharp/IndentationAnalysis.cs index bd65752508..a2a4a04349 100644 --- a/src/CSharp/CSharp/IndentationAnalysis.cs +++ b/src/CSharp/CSharp/IndentationAnalysis.cs @@ -235,6 +235,33 @@ SyntaxTrivia DetermineIndentationSize(CompilationUnitSyntax compilationUnit) if (member2 != null) return SyntaxTriviaAnalysis.DetermineIndentation(member2, cancellationToken); } + else if (member is GlobalStatementSyntax globalStatement) + { + StatementSyntax statement2 = globalStatement.Statement; + + if (statement2 is SwitchStatementSyntax switchStatement) + { + SwitchSectionSyntax switchSection = switchStatement.Sections.FirstOrDefault(); + + if (switchSection is not null) + return SyntaxTriviaAnalysis.DetermineIndentation(switchSection, cancellationToken); + + break; + } + else + { + StatementSyntax statement3 = GetContainedStatement(statement2); + + if (statement3 is not null) + { + if (statement3 is BlockSyntax block) + statement3 = block.Statements.FirstOrDefault(); + + if (statement3 is not null) + return SyntaxTriviaAnalysis.DetermineIndentation(statement3, cancellationToken); + } + } + } } return default; @@ -260,6 +287,50 @@ SyntaxTrivia DetermineIndentationSize(CompilationUnitSyntax compilationUnit) return default; } + + StatementSyntax GetContainedStatement(StatementSyntax statement) + { + switch (statement.Kind()) + { + case SyntaxKind.WhileStatement: + return ((WhileStatementSyntax)statement).Statement; + + case SyntaxKind.DoStatement: + return ((DoStatementSyntax)statement).Statement; + + case SyntaxKind.ForStatement: + return ((ForStatementSyntax)statement).Statement; + + case SyntaxKind.ForEachStatement: + case SyntaxKind.ForEachVariableStatement: + return ((CommonForEachStatementSyntax)statement).Statement; + + case SyntaxKind.UsingStatement: + return ((UsingStatementSyntax)statement).Statement; + + case SyntaxKind.FixedStatement: + return ((FixedStatementSyntax)statement).Statement; + + case SyntaxKind.CheckedStatement: + case SyntaxKind.UncheckedStatement: + return ((CheckedStatementSyntax)statement).Block; + + case SyntaxKind.UnsafeStatement: + return ((UnsafeStatementSyntax)statement).Block; + + case SyntaxKind.LockStatement: + return ((LockStatementSyntax)statement).Statement; + + case SyntaxKind.IfStatement: + return ((IfStatementSyntax)statement).Statement; + + case SyntaxKind.TryStatement: + return ((TryStatementSyntax)statement).Block; + + default: + return null; + } + } } } } diff --git a/src/Formatting.Analyzers/CSharp/FixFormattingOfCallChainAnalyzer.cs b/src/Formatting.Analyzers/CSharp/FixFormattingOfCallChainAnalyzer.cs index ba1d7addb8..5cc31cabe7 100644 --- a/src/Formatting.Analyzers/CSharp/FixFormattingOfCallChainAnalyzer.cs +++ b/src/Formatting.Analyzers/CSharp/FixFormattingOfCallChainAnalyzer.cs @@ -123,7 +123,13 @@ bool AnalyzeToken(SyntaxToken token) int endLine = lines.IndexOf(token.SpanStart); if (startLine != endLine) - ReportDiagnostic(); + { + if (!indentationAnalysis.IsDefault + || !AnalyzeIndentation(expression).IsDefault) + { + ReportDiagnostic(); + } + } return true; } @@ -133,8 +139,13 @@ bool AnalyzeToken(SyntaxToken token) case SyntaxKind.WhitespaceTrivia: { if (indentationAnalysis.IsDefault) + { indentationAnalysis = AnalyzeIndentation(expression); + if (indentationAnalysis.IsDefault) + return true; + } + if (en.Current.Span.Length != indentationAnalysis.IncreasedIndentationLength) { if (!en.MoveNext() @@ -156,7 +167,12 @@ bool AnalyzeToken(SyntaxToken token) { if (expression.FindTrivia(token.FullSpan.Start - 1).IsEndOfLineTrivia()) { - ReportDiagnostic(); + if (!indentationAnalysis.IsDefault + || !AnalyzeIndentation(expression).IsDefault) + { + ReportDiagnostic(); + } + return true; } diff --git a/src/Tests/Formatting.Analyzers.Tests/RCS0054FixFormattingOfCallChainTests.cs b/src/Tests/Formatting.Analyzers.Tests/RCS0054FixFormattingOfCallChainTests.cs index 5587a192fc..7a273cebda 100644 --- a/src/Tests/Formatting.Analyzers.Tests/RCS0054FixFormattingOfCallChainTests.cs +++ b/src/Tests/Formatting.Analyzers.Tests/RCS0054FixFormattingOfCallChainTests.cs @@ -314,6 +314,60 @@ C M(string s) "); } + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.FixFormattingOfCallChain)] + public async Task Test_TopLevelStatement_SwitchStatement() + { + await VerifyDiagnosticAndFixAsync(@" +var s = """"; + +s = [|s.ToString().ToString() +.ToString()|]; + +switch (s) +{ + default: + break; +} +", @" +var s = """"; + +s = s.ToString().ToString() + .ToString(); + +switch (s) +{ + default: + break; +} +", options: Options.WithCompilationOptions(Options.CompilationOptions.WithOutputKind(OutputKind.ConsoleApplication))); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.FixFormattingOfCallChain)] + public async Task Test_TopLevelStatement_ForEachStatement() + { + await VerifyDiagnosticAndFixAsync(@" +var s = """"; + +s = [|s.ToString().ToString() +.ToString()|]; + +foreach (char ch in s) +{ + var x = ch; +} +", @" +var s = """"; + +s = s.ToString().ToString() + .ToString(); + +foreach (char ch in s) +{ + var x = ch; +} +", options: Options.WithCompilationOptions(Options.CompilationOptions.WithOutputKind(OutputKind.ConsoleApplication))); + } + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.FixFormattingOfCallChain)] public async Task Test_TopLevelStatement() { @@ -332,6 +386,29 @@ void Main(string[] args) ", options: Options.WithCompilationOptions(Options.CompilationOptions.WithOutputKind(OutputKind.ConsoleApplication))); } + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.FixFormattingOfCallChain)] + public async Task Test_TopLevelStatement2() + { + await VerifyNoDiagnosticAsync(@" +var s = """"; + +s = s.ToString().ToString() +.ToString(); +", options: Options.WithCompilationOptions(Options.CompilationOptions.WithOutputKind(OutputKind.ConsoleApplication))); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.FixFormattingOfCallChain)] + public async Task Test_TopLevelStatement3() + { + await VerifyNoDiagnosticAsync(@" +var s = """"; + +s = s.ToString().ToString() + +.ToString(); +", options: Options.WithCompilationOptions(Options.CompilationOptions.WithOutputKind(OutputKind.ConsoleApplication))); + } + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.FixFormattingOfCallChain)] public async Task TestNoDiagnostic() {