diff --git a/MiKo.Analyzer.Shared/Rules/Spacing/MiKo_6048_LogicalConditionsAreOnSameLineAnalyzer.cs b/MiKo.Analyzer.Shared/Rules/Spacing/MiKo_6048_LogicalConditionsAreOnSameLineAnalyzer.cs index bc906667b..9450e87c3 100644 --- a/MiKo.Analyzer.Shared/Rules/Spacing/MiKo_6048_LogicalConditionsAreOnSameLineAnalyzer.cs +++ b/MiKo.Analyzer.Shared/Rules/Spacing/MiKo_6048_LogicalConditionsAreOnSameLineAnalyzer.cs @@ -12,8 +12,29 @@ public sealed class MiKo_6048_LogicalConditionsAreOnSameLineAnalyzer : SpacingAn private static readonly SyntaxKind[] Expressions = { + SyntaxKind.AddExpression, + SyntaxKind.AsExpression, + SyntaxKind.BitwiseAndExpression, + SyntaxKind.BitwiseOrExpression, + SyntaxKind.CoalesceExpression, + SyntaxKind.DivideExpression, + SyntaxKind.EqualsExpression, + SyntaxKind.ExclusiveOrExpression, + SyntaxKind.GreaterThanExpression, + SyntaxKind.GreaterThanOrEqualExpression, + SyntaxKind.IsExpression, + SyntaxKind.IsPatternExpression, + SyntaxKind.LeftShiftExpression, + SyntaxKind.LessThanExpression, + SyntaxKind.LessThanOrEqualExpression, SyntaxKind.LogicalAndExpression, SyntaxKind.LogicalOrExpression, + SyntaxKind.ModuloExpression, + SyntaxKind.MultiplyExpression, + SyntaxKind.NotEqualsExpression, + SyntaxKind.ParenthesizedExpression, + SyntaxKind.RightShiftExpression, + SyntaxKind.SubtractExpression, }; public MiKo_6048_LogicalConditionsAreOnSameLineAnalyzer() : base(Id) @@ -29,7 +50,7 @@ private static bool IsOnSingleLineLocal(SyntaxNode s) return span.StartLinePosition.Line == span.EndLinePosition.Line; } - private static bool IsOnSingleLine(ExpressionSyntax syntax) + private static bool IsOnSingleLine(SyntaxNode syntax) { switch (syntax) { @@ -39,6 +60,9 @@ private static bool IsOnSingleLine(ExpressionSyntax syntax) case BinaryExpressionSyntax binary: return IsOnSingleLine(binary); + case IsPatternExpressionSyntax isPattern: + return IsOnSingleLine(isPattern); + default: return IsOnSingleLineLocal(syntax); } @@ -71,7 +95,7 @@ private static bool IsOnSingleLine(BinaryExpressionSyntax binary) } // they span different lines - return IsOnSingleLine(leftCondition) && IsOnSingleLine(rightCondition); + return false; } private static bool IsOnSingleLine(ParenthesizedExpressionSyntax parenthesized) @@ -91,7 +115,37 @@ private static bool IsOnSingleLine(ParenthesizedExpressionSyntax parenthesized) return false; } - private static bool ShallAnalyzeNode(BinaryExpressionSyntax syntax) + private static bool IsOnSingleLine(IsPatternExpressionSyntax isPattern) + { + if (IsOnSingleLineLocal(isPattern)) + { + return true; + } + + var leftCondition = isPattern.Expression; + var rightCondition = isPattern.Pattern; + + var leftSpan = leftCondition.GetLocation().GetLineSpan(); + var rightSpan = rightCondition.GetLocation().GetLineSpan(); + + // let's see if both conditions are on same line + if (leftSpan.EndLinePosition.Line == rightSpan.StartLinePosition.Line) + { + if (leftSpan.StartLinePosition.Line == rightSpan.EndLinePosition.Line) + { + // both are on same line + return true; + } + + // at least one condition spans multiple lines + return false; + } + + // they span different lines + return false; + } + + private static bool ShallAnalyzeNode(ExpressionSyntax syntax) { switch (syntax.Parent) { @@ -106,7 +160,7 @@ private static bool ShallAnalyzeNode(BinaryExpressionSyntax syntax) private void AnalyzeNode(SyntaxNodeAnalysisContext context) { - if (context.Node is BinaryExpressionSyntax syntax && ShallAnalyzeNode(syntax)) + if (context.Node is ExpressionSyntax syntax && ShallAnalyzeNode(syntax)) { if (IsOnSingleLine(syntax)) { diff --git a/MiKo.Analyzer.Tests/Rules/Spacing/MiKo_6048_LogicalConditionsAreOnSameLineAnalyzerTests.cs b/MiKo.Analyzer.Tests/Rules/Spacing/MiKo_6048_LogicalConditionsAreOnSameLineAnalyzerTests.cs index feea23fda..75998d698 100644 --- a/MiKo.Analyzer.Tests/Rules/Spacing/MiKo_6048_LogicalConditionsAreOnSameLineAnalyzerTests.cs +++ b/MiKo.Analyzer.Tests/Rules/Spacing/MiKo_6048_LogicalConditionsAreOnSameLineAnalyzerTests.cs @@ -26,7 +26,7 @@ public void DoSomething(bool flag1, bool flag2) "); [Test] - public void No_issue_is_reported_if_logical_condition_parts_are_all_on_their_own_line_with_condition_on_same_line_as_first() => No_issue_is_reported_for(@" + public void An_issue_is_reported_if_logical_condition_parts_are_all_on_their_own_line_with_condition_on_same_line_as_first() => An_issue_is_reported_for(@" using System; public class TestMe @@ -41,7 +41,7 @@ public void DoSomething(bool flag1, bool flag2) "); [Test] - public void No_issue_is_reported_if_logical_condition_parts_are_all_on_their_own_line_with_condition_on_same_line_as_last() => No_issue_is_reported_for(@" + public void An_issue_is_reported_if_logical_condition_parts_are_all_on_their_own_line_with_condition_on_same_line_as_last() => An_issue_is_reported_for(@" using System; public class TestMe @@ -56,7 +56,7 @@ public void DoSomething(bool flag1, bool flag2) "); [Test] - public void No_issue_is_reported_if_multiple_logical_condition_parts_are_all_on_their_own_lines() => No_issue_is_reported_for(@" + public void An_issue_is_reported_if_multiple_logical_condition_parts_are_all_on_their_own_lines() => An_issue_is_reported_for(@" using System; public class TestMe @@ -75,7 +75,7 @@ public void DoSomething(bool flag1, bool flag2, bool flag3, bool flag4) "); [Test] - public void No_issue_is_reported_if_parenthesized_logical_condition_parts_are_all_on_their_own_line_and_combined_condition_is_first() => No_issue_is_reported_for(@" + public void An_issue_is_reported_if_parenthesized_logical_condition_parts_are_all_on_their_own_line_and_combined_condition_is_first() => An_issue_is_reported_for(@" using System; public class TestMe @@ -90,7 +90,7 @@ public void DoSomething(bool flag1, bool flag2, bool flag3) "); [Test] - public void No_issue_is_reported_if_parenthesized_logical_condition_parts_are_all_on_their_own_line_and_combined_condition_is_last() => No_issue_is_reported_for(@" + public void An_issue_is_reported_if_parenthesized_logical_condition_parts_are_all_on_their_own_line_and_combined_condition_is_last() => An_issue_is_reported_for(@" using System; public class TestMe @@ -105,7 +105,7 @@ public void DoSomething(bool flag1, bool flag2, bool flag3) "); [Test] - public void No_issue_is_reported_if_multiple_parenthesized_logical_condition_parts_are_all_on_multiple_lines() => No_issue_is_reported_for(@" + public void An_issue_is_reported_if_multiple_parenthesized_logical_condition_parts_are_all_on_multiple_lines() => An_issue_is_reported_for(@" using System; public class TestMe @@ -467,6 +467,187 @@ public class TestMe VerifyCSharpFix(OriginalCode, FixedCode); } + [TestCase("\r\n == null", "== null")] + [TestCase("== \r\n null", "== null")] + [TestCase("\r\n != null", "!= null")] + [TestCase("!= \r\n null", "!= null")] + [TestCase("\r\n is null", "is null")] + [TestCase("is \r\n null", "is null")] + [TestCase("\r\n is not null", "is not null")] + [TestCase("is \r\n not null", "is not null")] + [TestCase("is not \r\n null", "is not null")] + public void Code_gets_fixed_for_logical_condition_(string originalCondition, string fixedCondition) + { + const string Template = @" +using System; + +public class TestMe +{ + public object SomeProperty { get; set; } + + public void DoSomething(object o) + { + if (SomeProperty ###) + { } + } +} +"; + + VerifyCSharpFix(Template.Replace("###", originalCondition), Template.Replace("###", fixedCondition)); + } + + [Test] + public void Code_gets_fixed_if_logical_condition_with_equality_conditions_as_logical_parts_are_on_different_lines_1() + { + const string OriginalCode = @" +using System; + +public class TestMe +{ + public object SomeProperty { get; set; } + + public void DoSomething(object o) + { + if (SomeProperty != null + && SomeProperty.Equals(o)) + { } + } +} +"; + + const string FixedCode = @" +using System; + +public class TestMe +{ + public object SomeProperty { get; set; } + + public void DoSomething(object o) + { + if (SomeProperty != null && SomeProperty.Equals(o)) + { } + } +} +"; + + VerifyCSharpFix(OriginalCode, FixedCode); + } + + [Test] + public void Code_gets_fixed_if_logical_condition_with_equality_conditions_as_logical_parts_are_on_different_lines_2() + { + const string OriginalCode = @" +using System; + +public class TestMe +{ + public object SomeProperty { get; set; } + + public void DoSomething(object o) + { + if (SomeProperty + != null + && SomeProperty.Equals(o)) + { } + } +} +"; + + const string FixedCode = @" +using System; + +public class TestMe +{ + public object SomeProperty { get; set; } + + public void DoSomething(object o) + { + if (SomeProperty != null && SomeProperty.Equals(o)) + { } + } +} +"; + + VerifyCSharpFix(OriginalCode, FixedCode); + } + + [Test] + public void Code_gets_fixed_if_logical_condition_with_equality_conditions_as_logical_parts_are_on_different_lines_3() + { + const string OriginalCode = @" +using System; + +public class TestMe +{ + public object SomeProperty { get; set; } + + public void DoSomething(object o) + { + if (SomeProperty + != + null + && SomeProperty.Equals(o)) + { } + } +} +"; + + const string FixedCode = @" +using System; + +public class TestMe +{ + public object SomeProperty { get; set; } + + public void DoSomething(object o) + { + if (SomeProperty != null && SomeProperty.Equals(o)) + { } + } +} +"; + + VerifyCSharpFix(OriginalCode, FixedCode); + } + + [Test] + public void Code_gets_fixed_if_logical_condition_with_equality_conditions_as_logical_parts_are_on_different_lines_4() + { + const string OriginalCode = @" +using System; + +public class TestMe +{ + public object SomeProperty { get; set; } + + public void DoSomething(object o) + { + if (SomeProperty != null + && SomeProperty + .Equals(o)) + { } + } +} +"; + + const string FixedCode = @" +using System; + +public class TestMe +{ + public object SomeProperty { get; set; } + + public void DoSomething(object o) + { + if (SomeProperty != null && SomeProperty.Equals(o)) + { } + } +} +"; + + VerifyCSharpFix(OriginalCode, FixedCode); + } + [Test] public void Code_gets_fixed_for_invocation_on_multiple_lines() {