diff --git a/ChangeLog.md b/ChangeLog.md index 0ed49713b6..46e08baf68 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix [RCS1216](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RCS1216.md) ([#1094](https://github.com/JosefPihrt/Roslynator/pull/1094)). - Fix [RCS1146](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RCS1146.md) ([#1098](https://github.com/JosefPihrt/Roslynator/pull/1098)). - Fix [RCS1154](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RCS1154.md) ([#1105](https://github.com/JosefPihrt/Roslynator/pull/1105)). +- Fix [RCS1211](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RCS1211.md) ([#1095](https://github.com/JosefPihrt/Roslynator/pull/1095)). ## [4.3.0] - 2023-04-24 diff --git a/src/Analyzers/CSharp/Analysis/RemoveUnnecessaryBracesInSwitchSectionAnalyzer.cs b/src/Analyzers/CSharp/Analysis/RemoveUnnecessaryBracesInSwitchSectionAnalyzer.cs index a0c3b184e9..024aeb9822 100644 --- a/src/Analyzers/CSharp/Analysis/RemoveUnnecessaryBracesInSwitchSectionAnalyzer.cs +++ b/src/Analyzers/CSharp/Analysis/RemoveUnnecessaryBracesInSwitchSectionAnalyzer.cs @@ -93,7 +93,7 @@ public static void AnalyzerSwitchSection(SyntaxNodeAnalysisContext context) // If any of the other case blocks contain a definition for the same local variables then removing the braces would introduce a new error. if (switchSection.Parent is SwitchStatementSyntax switchStatement - && LocallyDeclaredVariablesOverlapWithAnyOtherSwitchSections(switchStatement, block, context.SemanticModel)) + && SwitchLocallyDeclaredVariablesHelper.BlockDeclaredVariablesOverlapWithOtherSwitchSections(block, switchStatement, context.SemanticModel)) { return; } @@ -107,42 +107,4 @@ static bool AnalyzeTrivia(SyntaxTriviaList trivia) } } - private static bool LocallyDeclaredVariablesOverlapWithAnyOtherSwitchSections(SwitchStatementSyntax switchStatement, BlockSyntax switchBlock, SemanticModel semanticModel) - { - ImmutableArray sectionVariablesDeclared = semanticModel.AnalyzeDataFlow(switchBlock)! - .VariablesDeclared; - - if (sectionVariablesDeclared.IsEmpty) - return false; - - ImmutableHashSet sectionDeclaredVariablesNames = sectionVariablesDeclared - .Select(s => s.Name) - .ToImmutableHashSet(); - - foreach (SwitchSectionSyntax otherSection in switchStatement.Sections) - { - if (otherSection.Span.Contains(switchBlock.Span)) - continue; - - foreach (SwitchLabelSyntax label in otherSection.Labels) - { - if (label is not CasePatternSwitchLabelSyntax casePatternSwitchLabel) - continue; - - if (PatternMatchingVariableDeclarationHelper.AnyDeclaredVariablesMatch(casePatternSwitchLabel.Pattern, sectionDeclaredVariablesNames)) - return true; - } - - foreach (StatementSyntax statement in otherSection.Statements) - { - foreach (ISymbol symbol in semanticModel.AnalyzeDataFlow(statement)!.VariablesDeclared) - { - if (sectionDeclaredVariablesNames.Contains(symbol.Name)) - return true; - } - } - } - - return false; - } } diff --git a/src/Analyzers/CSharp/Analysis/RemoveUnnecessaryElseAnalyzer.cs b/src/Analyzers/CSharp/Analysis/RemoveUnnecessaryElseAnalyzer.cs index 3abab1c3f4..f1d104c584 100644 --- a/src/Analyzers/CSharp/Analysis/RemoveUnnecessaryElseAnalyzer.cs +++ b/src/Analyzers/CSharp/Analysis/RemoveUnnecessaryElseAnalyzer.cs @@ -61,8 +61,17 @@ private static bool IsFixable(ElseClauseSyntax elseClause, SemanticModel semanti if (ifStatementStatement is not BlockSyntax ifBlock) return CSharpFacts.IsJumpStatement(ifStatementStatement.Kind()); - if (elseClause.Statement is BlockSyntax elseBlock && LocalDeclaredVariablesOverlap(elseBlock, ifBlock, semanticModel)) - return false; + if (elseClause.Statement is BlockSyntax elseBlock) + { + if (LocalDeclaredVariablesOverlap(elseBlock, ifBlock, semanticModel)) + return false; + + if (ifStatement.Parent is SwitchSectionSyntax { Parent: SwitchStatementSyntax switchStatement } + && SwitchLocallyDeclaredVariablesHelper.BlockDeclaredVariablesOverlapWithOtherSwitchSections(elseBlock, switchStatement, semanticModel)) + { + return false; + } + } StatementSyntax lastStatementInIf = ifBlock.Statements.LastOrDefault(); diff --git a/src/Analyzers/CSharp/Analysis/SwitchLocallyDeclaredVariablesHelper.cs b/src/Analyzers/CSharp/Analysis/SwitchLocallyDeclaredVariablesHelper.cs new file mode 100644 index 0000000000..af19591b7e --- /dev/null +++ b/src/Analyzers/CSharp/Analysis/SwitchLocallyDeclaredVariablesHelper.cs @@ -0,0 +1,48 @@ +using System.Collections.Immutable; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Roslynator.CSharp.Analysis; + +internal static class SwitchLocallyDeclaredVariablesHelper +{ + internal static bool BlockDeclaredVariablesOverlapWithOtherSwitchSections(BlockSyntax block, SwitchStatementSyntax switchStatement, SemanticModel semanticModel) + { + ImmutableArray sectionVariablesDeclared = semanticModel.AnalyzeDataFlow(block)! + .VariablesDeclared; + + if (sectionVariablesDeclared.IsEmpty) + return false; + + ImmutableHashSet sectionDeclaredVariablesNames = sectionVariablesDeclared + .Select(s => s.Name) + .ToImmutableHashSet(); + + foreach (SwitchSectionSyntax otherSection in switchStatement.Sections) + { + if (otherSection.Span.Contains(block.Span)) + continue; + + foreach (SwitchLabelSyntax label in otherSection.Labels) + { + if (label is not CasePatternSwitchLabelSyntax casePatternSwitchLabel) + continue; + + if (PatternMatchingVariableDeclarationHelper.AnyDeclaredVariablesMatch(casePatternSwitchLabel.Pattern, sectionDeclaredVariablesNames)) + return true; + } + + foreach (StatementSyntax statement in otherSection.Statements) + { + foreach (ISymbol symbol in semanticModel.AnalyzeDataFlow(statement)!.VariablesDeclared) + { + if (sectionDeclaredVariablesNames.Contains(symbol.Name)) + return true; + } + } + } + + return false; + } +} diff --git a/src/Tests/Analyzers.Tests/RCS1211RemoveUnnecessaryElseTests.cs b/src/Tests/Analyzers.Tests/RCS1211RemoveUnnecessaryElseTests.cs index 902a6a2a01..a96f4d595d 100644 --- a/src/Tests/Analyzers.Tests/RCS1211RemoveUnnecessaryElseTests.cs +++ b/src/Tests/Analyzers.Tests/RCS1211RemoveUnnecessaryElseTests.cs @@ -66,6 +66,41 @@ int M(bool flag) } } } +"); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.RemoveUnnecessaryElse)] + public async Task TestNoDiagnostic_OverlappingLocalVariablesWithSwitch() + { + await VerifyNoDiagnosticAsync(@" +class C +{ + int M(int i, bool flag) + { + switch(i) + { + case 0: + if(flag) + { + var z = 1; + return z; + } + break; + case 1: + if(flag) + { + var y = 1; + return y; + } + else + { + var z = 1; + return z; + } + } + return 2; + } +} "); } }