diff --git a/src/Features/CSharpTest/ConvertIfToSwitch/ConvertIfToSwitchTests.cs b/src/Features/CSharpTest/ConvertIfToSwitch/ConvertIfToSwitchTests.cs index e38824c41526c..f2b2f4e773eab 100644 --- a/src/Features/CSharpTest/ConvertIfToSwitch/ConvertIfToSwitchTests.cs +++ b/src/Features/CSharpTest/ConvertIfToSwitch/ConvertIfToSwitchTests.cs @@ -2794,4 +2794,15 @@ int M(R v) """, LanguageVersion = LanguageVersion.CSharp14, }.RunAsync(); + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/81714")] + public Task TestNullableBool() + => new VerifyCS.Test + { + TestCode = """ + [||]if ((bool?)null == false) { } + """, + LanguageVersion = LanguageVersion.CSharp14, + TestState = { OutputKind = OutputKind.ConsoleApplication } + }.RunAsync(); } diff --git a/src/Features/Core/Portable/ConvertIfToSwitch/AbstractConvertIfToSwitchCodeRefactoringProvider.Analyzer.cs b/src/Features/Core/Portable/ConvertIfToSwitch/AbstractConvertIfToSwitchCodeRefactoringProvider.Analyzer.cs index 269a9de5e938a..600258b09e08b 100644 --- a/src/Features/Core/Portable/ConvertIfToSwitch/AbstractConvertIfToSwitchCodeRefactoringProvider.Analyzer.cs +++ b/src/Features/Core/Portable/ConvertIfToSwitch/AbstractConvertIfToSwitchCodeRefactoringProvider.Analyzer.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis.LanguageService; using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.PooledObjects; @@ -255,8 +256,8 @@ private ConstantResult DetermineConstant(IBinaryOperation op) { return (op.LeftOperand, op.RightOperand) switch { - var (e, v) when IsConstant(v) && CheckTargetExpression(e) && CheckConstantType(v) => ConstantResult.Right, - var (v, e) when IsConstant(v) && CheckTargetExpression(e) && CheckConstantType(v) => ConstantResult.Left, + var (e, v) when IsConstant(v) && CheckTargetExpression(e, out var switchTargetType) && CheckConstantType(v, switchTargetType) => ConstantResult.Right, + var (v, e) when IsConstant(v) && CheckTargetExpression(e, out var switchTargetType) && CheckConstantType(v, switchTargetType) => ConstantResult.Left, _ => ConstantResult.None, }; } @@ -330,11 +331,11 @@ when Supports(Feature.AndPattern | Feature.CaseGuard): } case IIsTypeOperation op - when Supports(Feature.IsTypePattern) && CheckTargetExpression(op.ValueOperand) && op.Syntax is TIsExpressionSyntax node: + when Supports(Feature.IsTypePattern) && CheckTargetExpression(op.ValueOperand, out _) && op.Syntax is TIsExpressionSyntax node: return new AnalyzedPattern.Type(node); case IIsPatternOperation op - when Supports(Feature.SourcePattern) && CheckTargetExpression(op.Value) && op.Pattern.Syntax is TPatternSyntax pattern: + when Supports(Feature.SourcePattern) && CheckTargetExpression(op.Value, out _) && op.Pattern.Syntax is TPatternSyntax pattern: return new AnalyzedPattern.Source(pattern); case IParenthesizedOperation op: @@ -390,7 +391,7 @@ when CheckTargetExpression(low.Expression, high.Expression) => (low.Value.Syntax }; bool CheckTargetExpression(IOperation left, IOperation right) - => _syntaxFacts.AreEquivalent(left.Syntax, right.Syntax) && this.CheckTargetExpression(left); + => _syntaxFacts.AreEquivalent(left.Syntax, right.Syntax) && this.CheckTargetExpression(left, out _); } private static (BoundKind Kind, IOperation Expression, IOperation Value) GetRangeBound(IBinaryOperation op) @@ -447,33 +448,34 @@ private static bool IsConstant(IOperation operation) : operation.ConstantValue.HasValue; } - private bool CheckTargetExpression(IOperation operation) + private bool CheckTargetExpression(IOperation operation, [NotNullWhen(true)] out ITypeSymbol? switchTargetType) { operation = operation.WalkDownConversion(); if (operation.Syntax is not TExpressionSyntax expression) + { + switchTargetType = null; return false; + } - // If we have not figured the switch expression yet, - // we will assume that the first expression is the one. + // If we have not figured the switch expression yet, we will assume that the first expression is the one. if (_switchTargetExpression is null) { RoslynDebug.Assert(_switchTargetType is null); _switchTargetExpression = expression; _switchTargetType = operation.Type; - return true; } - return _syntaxFacts.AreEquivalent(expression, _switchTargetExpression); + switchTargetType = _switchTargetType; + return switchTargetType != null && _syntaxFacts.AreEquivalent(expression, _switchTargetExpression); } - private bool CheckConstantType(IOperation operation) + private bool CheckConstantType(IOperation operation, ITypeSymbol switchTargetType) { RoslynDebug.AssertNotNull(operation.SemanticModel); - RoslynDebug.AssertNotNull(_switchTargetType); - return CanImplicitlyConvert(operation.SemanticModel, operation.Syntax, _switchTargetType); + return CanImplicitlyConvert(operation.SemanticModel, operation.Syntax, switchTargetType); } }