diff --git a/ChangeLog.md b/ChangeLog.md index 2d66503da0..1d60fb0fe7 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -36,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix [RCS1208](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RCS1208.md) ([#1119](https://github.com/JosefPihrt/Roslynator/pull/1119)). - [CLI] Fix member full declaration in generated documentation (command `generate-doc`) ([#1130](https://github.com/josefpihrt/roslynator/pull/1130)). - Append `?` to nullable reference types. +- Fix [RCS1179](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RCS1179.md) ([#1129](https://github.com/JosefPihrt/Roslynator/pull/1129)). ## [4.3.0] - 2023-04-24 diff --git a/src/Analyzers/CSharp/Analysis/UnnecessaryAssignmentAnalyzer.cs b/src/Analyzers/CSharp/Analysis/UnnecessaryAssignmentAnalyzer.cs index d992e996a6..308ef81bcd 100644 --- a/src/Analyzers/CSharp/Analysis/UnnecessaryAssignmentAnalyzer.cs +++ b/src/Analyzers/CSharp/Analysis/UnnecessaryAssignmentAnalyzer.cs @@ -70,6 +70,8 @@ private static void AnalyzeIfStatement(SyntaxNodeAnalysisContext context) if (!IsLocalDeclaredInScopeOrNonRefOrOutParameterOfEnclosingSymbol(symbol, statementsInfo.Parent, semanticModel, cancellationToken)) return; + ITypeSymbol returnTypeSymbol = semanticModel.GetTypeSymbol(expression, cancellationToken); + foreach (IfStatementOrElseClause ifOrElse in ifStatement.AsCascade()) { StatementSyntax statement = ifOrElse.Statement; @@ -78,7 +80,7 @@ private static void AnalyzeIfStatement(SyntaxNodeAnalysisContext context) statement = ((BlockSyntax)statement).Statements.LastOrDefault(); if (!statement.IsKind(SyntaxKind.ThrowStatement) - && !IsSymbolAssignedInStatement(symbol, statement, semanticModel, cancellationToken)) + && !IsSymbolAssignedInStatementWithCorrectType(symbol, statement, semanticModel, returnTypeSymbol, cancellationToken)) { return; } @@ -113,6 +115,7 @@ private static void AnalyzeSwitchStatement(SyntaxNodeAnalysisContext context) CancellationToken cancellationToken = context.CancellationToken; ISymbol symbol = semanticModel.GetSymbol(expression, cancellationToken); + ITypeSymbol returnTypeSymbol = semanticModel.GetTypeInfo(expression, cancellationToken).Type; if (symbol is null) return; @@ -136,7 +139,7 @@ private static void AnalyzeSwitchStatement(SyntaxNodeAnalysisContext context) case SyntaxKind.BreakStatement: { if (statements.Count == 1 - || !IsSymbolAssignedInStatement(symbol, statements.LastButOne(), semanticModel, cancellationToken)) + || !IsSymbolAssignedInStatementWithCorrectType(symbol, statements.LastButOne(), semanticModel, returnTypeSymbol, cancellationToken)) { return; } @@ -204,11 +207,12 @@ private static bool IsLocalDeclaredInScopeOrNonRefOrOutParameterOfEnclosingSymbo return false; } - private static bool IsSymbolAssignedInStatement(ISymbol symbol, StatementSyntax statement, SemanticModel semanticModel, CancellationToken cancellationToken) + private static bool IsSymbolAssignedInStatementWithCorrectType(ISymbol symbol, StatementSyntax statement, SemanticModel semanticModel, ITypeSymbol typeSymbol, CancellationToken cancellationToken) { SimpleAssignmentStatementInfo assignmentInfo = SyntaxInfo.SimpleAssignmentStatementInfo(statement); return assignmentInfo.Success - && SymbolEqualityComparer.Default.Equals(semanticModel.GetSymbol(assignmentInfo.Left, cancellationToken), symbol); + && SymbolEqualityComparer.Default.Equals(semanticModel.GetSymbol(assignmentInfo.Left, cancellationToken), symbol) + && SymbolEqualityComparer.Default.Equals(typeSymbol, semanticModel.GetTypeSymbol(assignmentInfo.Right, cancellationToken)); } } diff --git a/src/Tests/Analyzers.Tests/RCS1179UnnecessaryAssignmentTests.cs b/src/Tests/Analyzers.Tests/RCS1179UnnecessaryAssignmentTests.cs index 49b8ffdb9b..f81cdd15b7 100644 --- a/src/Tests/Analyzers.Tests/RCS1179UnnecessaryAssignmentTests.cs +++ b/src/Tests/Analyzers.Tests/RCS1179UnnecessaryAssignmentTests.cs @@ -309,6 +309,67 @@ int M() } } } +"); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UnnecessaryAssignment)] + public async Task Test_NoDiagnostic_ForPolymorphicIf() + { + await VerifyNoDiagnosticAsync( + @" +class A {} +class B {} +class C +{ + void M() + { + var fun = (bool flag) => + { + object x; + if (flag) + { + x = new A(); + } + else + { + x = new B(); + } + + return x; + }; + } +} +"); + } + + [Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UnnecessaryAssignment)] + public async Task Test_NoDiagnostic_ForPolymorphicSwitch() + { + await VerifyNoDiagnosticAsync( + @" +class A {} +class B {} +class C +{ + void M() + { + var fun = (object o) => + { + object x; + switch(o) + { + case int: + x = new A(); + break; + default: + x = new B(); + break; + } + + return x; + }; + } +} "); } }