Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/Rules/MA0071.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Sources: [AvoidUsingRedundantElseAnalyzer.cs](https://github.com/meziantou/Mezia
<!-- sources -->

When an `if` block contains a jump statement (`break`, `continue`, `goto`, `return`, `throw`, `yield break`), using an `else` block is redundant and needlessly maintains a higher nesting level.
In an `else if` chain, this simplification only applies when all previous `if` branches in the chain also jump unconditionally.

The rule helps reduce overall nesting, as well as the total number of lines. Refer to [Computer Programming/Coding Style/Minimize nesting](https://en.wikibooks.org/wiki/Computer_Programming/Coding_Style/Minimize_nesting).

Expand Down
23 changes: 23 additions & 0 deletions src/Meziantou.Analyzer/Rules/AvoidUsingRedundantElseAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,35 @@ private static void AnalyzeElseClause(SyntaxNodeAnalysisContext context)
if (controlFlowAnalysis is null || !controlFlowAnalysis.Succeeded)
return;

if (!AllPreviousBranchesJumpUnconditionally(context.SemanticModel, ifStatement))
return;

if (!controlFlowAnalysis.EndPointIsReachable)
{
context.ReportDiagnostic(Rule, elseClause.ElseKeyword);
}
}

private static bool AllPreviousBranchesJumpUnconditionally(SemanticModel semanticModel, IfStatementSyntax ifStatement)
{
var currentIfStatement = ifStatement;
while (currentIfStatement.Parent is ElseClauseSyntax { Parent: IfStatementSyntax parentIfStatement })
{
var controlFlowAnalysis = semanticModel.AnalyzeControlFlow(parentIfStatement.Statement);
if (!IsUnreachableEndpoint(controlFlowAnalysis))
return false;

currentIfStatement = parentIfStatement;
}

return true;
}

private static bool IsUnreachableEndpoint(ControlFlowAnalysis? controlFlowAnalysis)
{
return controlFlowAnalysis is { Succeeded: true, EndPointIsReachable: false };
}

private static IEnumerable<string> FindLocalIdentifiersIn(SyntaxNode node)
{
foreach (var child in node.DescendantNodes())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,44 @@ await CreateProjectBuilder()
.ValidateAsync();
}

[Fact]
public async Task Test_ElseIfChainWithReachablePreviousThenAndMethodInvocationCondition_NoDiagnosticReported()
{
var originalCode = """
class TestClass
{
void Test()
{
var test = new TestClass();
foreach (var cmp in new[] { "first", "second", "third" })
{
if (test.Verify("first", cmp) is not null)
{
System.Console.WriteLine("Handled");
}
else if (test.Verify("second", cmp) is not null)
{
System.Console.WriteLine("Handled");
continue;
}
else
{
System.Console.WriteLine("Not handled");
}
}
}

int? Verify(string tag, string cmp)
{
return tag.Equals(cmp, System.StringComparison.Ordinal) ? 1 : null;
}
}
""";
await CreateProjectBuilder()
.WithSourceCode(originalCode)
.ValidateAsync();
}

[Fact]
public async Task Test_SeveralNestedIfElseBlocksWithIfsThatJump_AllProblematicElsesRemoved()
{
Expand Down
Loading