diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs index 2acef1c397f5c..5d23b6af38b52 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs @@ -962,11 +962,42 @@ public override BoundNode VisitPassByCopy(BoundPassByCopy node) public override BoundNode VisitIsPatternExpression(BoundIsPatternExpression node) { Debug.Assert(!IsConditionalState); - VisitRvalue(node.Expression); bool negated = node.Pattern.IsNegated(out var pattern); Debug.Assert(negated == node.IsNegated); + if (VisitPossibleConditionalAccess(node.Expression, out var stateWhenNotNull)) + { + Debug.Assert(!IsConditionalState); + switch (isTopLevelNonNullTest(pattern)) + { + case true: + SetConditionalState(stateWhenNotNull, State); + break; + case false: + SetConditionalState(State, stateWhenNotNull); + break; + } + } + else if (IsConditionalState) + { + // Patterns which only match a single boolean value should propagate conditional state + // for example, `(a != null && a.M(out x)) is true` should have the same conditional state as `(a != null && a.M(out x))`. + if (isBoolTest(pattern) is bool value) + { + if (!value) + { + SetConditionalState(StateWhenFalse, StateWhenTrue); + } + } + else + { + // Patterns which match more than a single boolean value cannot propagate conditional state + // for example, `(a != null && a.M(out x)) is bool b` should not have conditional state + Unsplit(); + } + } + VisitPattern(pattern); var reachableLabels = node.DecisionDag.ReachableLabels; if (!reachableLabels.Contains(node.WhenTrueLabel)) @@ -986,6 +1017,88 @@ public override BoundNode VisitIsPatternExpression(BoundIsPatternExpression node } return node; + + // Returns `true` if the pattern only matches if the top-level input is not null. + // Returns `false` if the pattern only matches if the top-level input is null. + // Otherwise, returns `null`. + static bool? isTopLevelNonNullTest(BoundPattern pattern) + { + switch (pattern) + { + case BoundTypePattern: + case BoundRecursivePattern: + case BoundITuplePattern: + case BoundRelationalPattern: + case BoundDeclarationPattern { IsVar: false }: + case BoundConstantPattern { ConstantValue: { IsNull: false } }: + return true; + case BoundConstantPattern { ConstantValue: { IsNull: true } }: + return false; + case BoundNegatedPattern negated: + return !isTopLevelNonNullTest(negated.Negated); + case BoundBinaryPattern binary: + if (binary.Disjunction) + { + // `a?.b(out x) is null or C` + // both subpatterns must have the same null test for the test to propagate out + var leftNullTest = isTopLevelNonNullTest(binary.Left); + return leftNullTest is null ? null : + leftNullTest != isTopLevelNonNullTest(binary.Right) ? null : + leftNullTest; + } + + // `a?.b(out x) is not null and var c` + // if any pattern performs a test, we know that test applies at the top level + // note that if the tests are different, e.g. `null and not null`, + // the pattern is a contradiction, so we expect an error diagnostic + return isTopLevelNonNullTest(binary.Left) ?? isTopLevelNonNullTest(binary.Right); + case BoundDeclarationPattern { IsVar: true }: + case BoundDiscardPattern: + return null; + default: + throw ExceptionUtilities.UnexpectedValue(pattern.Kind); + } + } + + // Returns `true` if the pattern only matches a `true` input. + // Returns `false` if the pattern only matches a `false` input. + // Otherwise, returns `null`. + static bool? isBoolTest(BoundPattern pattern) + { + switch (pattern) + { + case BoundConstantPattern { ConstantValue: { IsBoolean: true, BooleanValue: var boolValue } }: + return boolValue; + case BoundNegatedPattern negated: + return !isBoolTest(negated.Negated); + case BoundBinaryPattern binary: + if (binary.Disjunction) + { + // `(a != null && a.b(out x)) is true or true` matches `true` + // `(a != null && a.b(out x)) is true or false` matches any boolean + // both subpatterns must have the same bool test for the test to propagate out + var leftNullTest = isBoolTest(binary.Left); + return leftNullTest is null ? null : + leftNullTest != isBoolTest(binary.Right) ? null : + leftNullTest; + } + + // `(a != null && a.b(out x)) is true and true` matches `true` + // `(a != null && a.b(out x)) is true and var x` matches `true` + // `(a != null && a.b(out x)) is true and false` never matches and is a compile error + return isBoolTest(binary.Left) ?? isBoolTest(binary.Right); + case BoundConstantPattern { ConstantValue: { IsBoolean: false } }: + case BoundDiscardPattern: + case BoundTypePattern: + case BoundRecursivePattern: + case BoundITuplePattern: + case BoundRelationalPattern: + case BoundDeclarationPattern: + return null; + default: + throw ExceptionUtilities.UnexpectedValue(pattern.Kind); + } + } } public virtual void VisitPattern(BoundPattern pattern) @@ -2510,7 +2623,16 @@ public override BoundNode VisitAsOperator(BoundAsOperator node) public override BoundNode VisitIsOperator(BoundIsOperator node) { - VisitRvalue(node.Operand); + if (VisitPossibleConditionalAccess(node.Operand, out var stateWhenNotNull)) + { + Debug.Assert(!IsConditionalState); + SetConditionalState(stateWhenNotNull, State); + } + else + { + // `(a && b.M(out x)) is bool` should discard conditional state from LHS + Unsplit(); + } return null; } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index e33f540b0a4a2..c66241a16235e 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -9105,7 +9105,7 @@ private TypeWithState InferResultNullabilityOfBinaryLogicalOperator(BoundExpress var operand = node.Operand; var typeExpr = node.TargetType; - var result = base.VisitIsOperator(node); + VisitRvalue(operand); Debug.Assert(node.Type.SpecialType == SpecialType.System_Boolean); Split(); @@ -9117,7 +9117,7 @@ private TypeWithState InferResultNullabilityOfBinaryLogicalOperator(BoundExpress VisitTypeExpression(typeExpr); SetNotNullResult(node); - return result; + return null; } public override BoundNode? VisitAsOperator(BoundAsOperator node) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs index 8dfa376a19920..d90616be8be34 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs @@ -57,7 +57,7 @@ public override BoundNode VisitRecursivePattern(BoundRecursivePattern node) public override BoundNode VisitConstantPattern(BoundConstantPattern node) { - Visit(node.Value); + VisitRvalue(node.Value); return null; } diff --git a/src/Compilers/CSharp/Test/Semantic/FlowAnalysis/FlowTests.cs b/src/Compilers/CSharp/Test/Semantic/FlowAnalysis/FlowTests.cs index 0fca9adc9fb06..591e04adcc780 100644 --- a/src/Compilers/CSharp/Test/Semantic/FlowAnalysis/FlowTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/FlowAnalysis/FlowTests.cs @@ -4004,6 +4004,641 @@ public void M4(C? c) ); } + [Fact] + public void IsBool() + { + var source = @" +#nullable enable + +class C +{ + bool M0(object obj) => false; + + void M1(bool b) + { + int x, y; + _ = (b && M0(x = y = 0)) is bool // 1 + ? x.ToString() // 2 + : y.ToString(); // 3 + } +} +"; + CreateCompilation(source).VerifyDiagnostics( + // (11,13): warning CS0183: The given expression is always of the provided ('bool') type + // _ = (b && M0(x = y = 0)) is bool // 1 + Diagnostic(ErrorCode.WRN_IsAlwaysTrue, "(b && M0(x = y = 0)) is bool").WithArguments("bool").WithLocation(11, 13), + // (12,15): error CS0165: Use of unassigned local variable 'x' + // ? x.ToString() // 2 + Diagnostic(ErrorCode.ERR_UseDefViolation, "x").WithArguments("x").WithLocation(12, 15), + // (13,15): error CS0165: Use of unassigned local variable 'y' + // : y.ToString(); // 3 + Diagnostic(ErrorCode.ERR_UseDefViolation, "y").WithArguments("y").WithLocation(13, 15) + ); + } + + [Theory] + [InlineData("true", "false")] + [InlineData("!false", "!true")] + [InlineData("(true || false)", "(true && false)")] + public void IsBoolConstant_01(string @true, string @false) + { + var source = @" +#nullable enable + +class C +{ + bool M0(object obj) => false; + + void M1(bool b) + { + int x, y; + _ = (b && M0(x = y = 0)) is " + @true + @" + ? x.ToString() + : y.ToString(); // 1 + } + + void M2(bool b) + { + int x, y; + _ = (b && M0(x = y = 0)) is " + @false + @" + ? x.ToString() // 2 + : y.ToString(); + } +} +"; + CreateCompilation(source).VerifyDiagnostics( + // (13,15): error CS0165: Use of unassigned local variable 'y' + // : y.ToString(); // 1 + Diagnostic(ErrorCode.ERR_UseDefViolation, "y").WithArguments("y").WithLocation(13, 15), + // (20,15): error CS0165: Use of unassigned local variable 'x' + // ? x.ToString() // 2 + Diagnostic(ErrorCode.ERR_UseDefViolation, "x").WithArguments("x").WithLocation(20, 15) + ); + } + + [Fact] + public void IsBoolConstant_02() + { + var source = @" +#nullable enable + +class C +{ + bool M0(object obj) => false; + + void M1(bool b) + { + int x, y; + _ = (b && M0(x = y = 0)) is var z + ? x.ToString() // 1 + : y.ToString(); // unreachable + } +} +"; + CreateCompilation(source).VerifyDiagnostics( + // (12,15): error CS0165: Use of unassigned local variable 'x' + // ? x.ToString() // 1 + Diagnostic(ErrorCode.ERR_UseDefViolation, "x").WithArguments("x").WithLocation(12, 15) + ); + } + + [Fact] + public void IsBoolConstant_03() + { + var source = @" +#nullable enable +#pragma warning disable 8794 // An expression always matches the provided pattern + +class C +{ + bool M0(object obj) => false; + + void M1(bool b) + { + int x, y; + _ = (b && M0(x = y = 0)) is not true + ? x.ToString() // 1 + : y.ToString(); + } + + void M2(bool b) + { + int x, y; + _ = (b && M0(x = y = 0)) is true or false + ? x.ToString() // 2 + : y.ToString(); // unreachable + } + + void M3(bool b) + { + int x, y; + _ = (b && M0(x = y = 0)) is true and false // 3 + ? x.ToString() // unreachable + : y.ToString(); // 4 + } + + void M4(bool b) + { + int x, y; + _ = (b && M0(x = y = 0)) is true or true + ? x.ToString() + : y.ToString(); // 5 + } + + void M5(bool b) + { + int x, y; + _ = (b && M0(x = y = 0)) is true and true + ? x.ToString() + : y.ToString(); // 6 + } + + void M6(bool b) + { + int x, y; + _ = (b && M0(x = y = 0)) is false or false + ? x.ToString() // 7 + : y.ToString(); + } + + void M7(bool b) + { + int x, y; + _ = (b && M0(x = y = 0)) is false and var z + ? x.ToString() // 8 + : y.ToString(); + } + + void M8(bool b) + { + int x, y; + _ = (b && M0(x = y = 0)) is var z or true // 9 + ? x.ToString() // 10 + : y.ToString(); // unreachable + } + + void M9(bool b) + { + int x, y; + _ = (b && M0(x = y = 0)) is not (false and false) + ? x.ToString() + : y.ToString(); // 11 + } +} +"; + CreateCompilation(source).VerifyDiagnostics( + // (13,15): error CS0165: Use of unassigned local variable 'x' + // ? x.ToString() // 1 + Diagnostic(ErrorCode.ERR_UseDefViolation, "x").WithArguments("x").WithLocation(13, 15), + // (21,15): error CS0165: Use of unassigned local variable 'x' + // ? x.ToString() // 2 + Diagnostic(ErrorCode.ERR_UseDefViolation, "x").WithArguments("x").WithLocation(21, 15), + // (28,13): error CS8518: An expression of type 'bool' can never match the provided pattern. + // _ = (b && M0(x = y = 0)) is true and false // 3 + Diagnostic(ErrorCode.ERR_IsPatternImpossible, "(b && M0(x = y = 0)) is true and false").WithArguments("bool").WithLocation(28, 13), + // (30,15): error CS0165: Use of unassigned local variable 'y' + // : y.ToString(); // 4 + Diagnostic(ErrorCode.ERR_UseDefViolation, "y").WithArguments("y").WithLocation(30, 15), + // (38,15): error CS0165: Use of unassigned local variable 'y' + // : y.ToString(); // 5 + Diagnostic(ErrorCode.ERR_UseDefViolation, "y").WithArguments("y").WithLocation(38, 15), + // (46,15): error CS0165: Use of unassigned local variable 'y' + // : y.ToString(); // 6 + Diagnostic(ErrorCode.ERR_UseDefViolation, "y").WithArguments("y").WithLocation(46, 15), + // (53,15): error CS0165: Use of unassigned local variable 'x' + // ? x.ToString() // 7 + Diagnostic(ErrorCode.ERR_UseDefViolation, "x").WithArguments("x").WithLocation(53, 15), + // (61,15): error CS0165: Use of unassigned local variable 'x' + // ? x.ToString() // 8 + Diagnostic(ErrorCode.ERR_UseDefViolation, "x").WithArguments("x").WithLocation(61, 15), + // (68,41): error CS8780: A variable may not be declared within a 'not' or 'or' pattern. + // _ = (b && M0(x = y = 0)) is var z or true // 9 + Diagnostic(ErrorCode.ERR_DesignatorBeneathPatternCombinator, "z").WithLocation(68, 41), + // (69,15): error CS0165: Use of unassigned local variable 'x' + // ? x.ToString() // 10 + Diagnostic(ErrorCode.ERR_UseDefViolation, "x").WithArguments("x").WithLocation(69, 15), + // (78,15): error CS0165: Use of unassigned local variable 'y' + // : y.ToString(); // 11 + Diagnostic(ErrorCode.ERR_UseDefViolation, "y").WithArguments("y").WithLocation(78, 15) + ); + } + + [Fact] + public void IsCondAccess_01() + { + var source = @" +#nullable enable + +class C +{ + C M0(object obj) => this; + + void M1(C? c) + { + int x, y; + _ = c?.M0(x = y = 0) is C + ? x.ToString() + : y.ToString(); // 1 + } + + void M2(C? c) + { + int x, y; + _ = c?.Equals(x = y = 0) is bool + ? x.ToString() + : y.ToString(); // 2 + } +} +"; + CreateCompilation(source).VerifyDiagnostics( + // (13,15): error CS0165: Use of unassigned local variable 'y' + // : y.ToString(); // 1 + Diagnostic(ErrorCode.ERR_UseDefViolation, "y").WithArguments("y").WithLocation(13, 15), + // (21,15): error CS0165: Use of unassigned local variable 'y' + // : y.ToString(); // 2 + Diagnostic(ErrorCode.ERR_UseDefViolation, "y").WithArguments("y").WithLocation(21, 15) + ); + } + + [Fact] + public void IsCondAccess_02() + { + var source = @" +#nullable enable + +class C +{ + C M0(object obj) => this; + + void M1(C? c) + { + int x, y; + _ = c?.M0(x = y = 0) is (_) + ? x.ToString() // 1 + : y.ToString(); // unreachable + } + + void M2(C? c) + { + int x, y; + _ = c?.M0(x = y = 0) is var z + ? x.ToString() // 2 + : y.ToString(); // unreachable + } + + void M3(C? c) + { + int x, y; + _ = c?.M0(x = y = 0) is { } + ? x.ToString() + : y.ToString(); // 3 + } + + void M4(C? c) + { + int x, y; + _ = c?.M0(x = y = 0) is { } c1 + ? x.ToString() + : y.ToString(); // 4 + } + + void M5(C? c) + { + int x, y; + _ = c?.M0(x = y = 0) is C c1 + ? x.ToString() + : y.ToString(); // 5 + } + + void M6(C? c) + { + int x, y; + _ = c?.M0(x = y = 0) is not null + ? x.ToString() + : y.ToString(); // 6 + } + + void M7(C? c) + { + int x, y; + _ = c?.M0(x = y = 0) is not C + ? x.ToString() // 7 + : y.ToString(); + } + + void M8(C? c) + { + int x, y; + _ = c?.M0(x = y = 0) is null + ? x.ToString() // 8 + : y.ToString(); + } +} +"; + CreateCompilation(source).VerifyDiagnostics( + // (12,15): error CS0165: Use of unassigned local variable 'x' + // ? x.ToString() // 1 + Diagnostic(ErrorCode.ERR_UseDefViolation, "x").WithArguments("x").WithLocation(12, 15), + // (20,15): error CS0165: Use of unassigned local variable 'x' + // ? x.ToString() // 2 + Diagnostic(ErrorCode.ERR_UseDefViolation, "x").WithArguments("x").WithLocation(20, 15), + // (29,15): error CS0165: Use of unassigned local variable 'y' + // : y.ToString(); // 3 + Diagnostic(ErrorCode.ERR_UseDefViolation, "y").WithArguments("y").WithLocation(29, 15), + // (37,15): error CS0165: Use of unassigned local variable 'y' + // : y.ToString(); // 4 + Diagnostic(ErrorCode.ERR_UseDefViolation, "y").WithArguments("y").WithLocation(37, 15), + // (45,15): error CS0165: Use of unassigned local variable 'y' + // : y.ToString(); // 5 + Diagnostic(ErrorCode.ERR_UseDefViolation, "y").WithArguments("y").WithLocation(45, 15), + // (53,15): error CS0165: Use of unassigned local variable 'y' + // : y.ToString(); // 6 + Diagnostic(ErrorCode.ERR_UseDefViolation, "y").WithArguments("y").WithLocation(53, 15), + // (60,15): error CS0165: Use of unassigned local variable 'x' + // ? x.ToString() // 7 + Diagnostic(ErrorCode.ERR_UseDefViolation, "x").WithArguments("x").WithLocation(60, 15), + // (68,15): error CS0165: Use of unassigned local variable 'x' + // ? x.ToString() // 8 + Diagnostic(ErrorCode.ERR_UseDefViolation, "x").WithArguments("x").WithLocation(68, 15) + ); + } + + [Fact] + public void IsCondAccess_03() + { + var source = @" +#nullable enable + +class C +{ + (C, C) M0(object obj) => (this, this); + + void M1(C? c) + { + int x, y; + _ = c?.M0(x = y = 0) is (_, _) + ? x.ToString() + : y.ToString(); // 1 + } + + void M2(C? c) + { + int x, y; + _ = c?.M0(x = y = 0) is not null + ? x.ToString() + : y.ToString(); // 2 + } + + void M3(C? c) + { + int x, y; + _ = c?.M0(x = y = 0) is (not null, null) + ? x.ToString() + : y.ToString(); // 3 + } + + void M4(C? c) + { + int x, y; + _ = c?.M0(x = y = 0) is null + ? x.ToString() // 4 + : y.ToString(); + } + + void M5(C? c) + { + int x, y; + _ = (c?.M0(x = 0), c?.M0(y = 0)) is (not null, not null) + ? x.ToString() // 5 + : y.ToString(); // 6 + } +} +"; + // note: "state when not null" is not tracked when pattern matching against tuples containing conditional accesses. + CreateCompilation(source).VerifyDiagnostics( + // (13,15): error CS0165: Use of unassigned local variable 'y' + // : y.ToString(); // 1 + Diagnostic(ErrorCode.ERR_UseDefViolation, "y").WithArguments("y").WithLocation(13, 15), + // (21,15): error CS0165: Use of unassigned local variable 'y' + // : y.ToString(); // 2 + Diagnostic(ErrorCode.ERR_UseDefViolation, "y").WithArguments("y").WithLocation(21, 15), + // (29,15): error CS0165: Use of unassigned local variable 'y' + // : y.ToString(); // 3 + Diagnostic(ErrorCode.ERR_UseDefViolation, "y").WithArguments("y").WithLocation(29, 15), + // (36,15): error CS0165: Use of unassigned local variable 'x' + // ? x.ToString() // 4 + Diagnostic(ErrorCode.ERR_UseDefViolation, "x").WithArguments("x").WithLocation(36, 15), + // (44,15): error CS0165: Use of unassigned local variable 'x' + // ? x.ToString() // 5 + Diagnostic(ErrorCode.ERR_UseDefViolation, "x").WithArguments("x").WithLocation(44, 15), + // (45,15): error CS0165: Use of unassigned local variable 'y' + // : y.ToString(); // 6 + Diagnostic(ErrorCode.ERR_UseDefViolation, "y").WithArguments("y").WithLocation(45, 15) + ); + } + + [Fact] + public void IsCondAccess_04() + { + var source = @" +#nullable enable +#pragma warning disable 8794 // An expression always matches the provided pattern + +class C +{ + C M0(object obj) => this; + + void M1(C? c) + { + int x, y; + _ = c?.M0(x = y = 0) is null or not null + ? x.ToString() // 1 + : y.ToString(); // unreachable + } + + void M2(C? c) + { + int x, y; + _ = c?.M0(x = y = 0) is C or null + ? x.ToString() // 2 + : y.ToString(); // unreachable + } + + void M3(C? c) + { + int x, y; + _ = c?.M0(x = y = 0) is not null and C + ? x.ToString() + : y.ToString(); // 3 + } + + void M4(C? c) + { + int x, y; + _ = c?.M0(x = y = 0) is null + ? x.ToString() // 4 + : y.ToString(); + } + + void M5(C? c) + { + int x, y; + _ = c?.M0(x = y = 0) is not (C or { }) + ? x.ToString() // 5 + : y.ToString(); + } + + void M6(C? c) + { + int x, y; + _ = c?.M0(x = y = 0) is _ and C + ? x.ToString() + : y.ToString(); // 6 + } + + void M7(C? c) + { + int x, y; + _ = c?.M0(x = y = 0) is C and _ + ? x.ToString() + : y.ToString(); // 7 + } +} +"; + CreateCompilation(source).VerifyDiagnostics( + // (13,15): error CS0165: Use of unassigned local variable 'x' + // ? x.ToString() // 1 + Diagnostic(ErrorCode.ERR_UseDefViolation, "x").WithArguments("x").WithLocation(13, 15), + // (21,15): error CS0165: Use of unassigned local variable 'x' + // ? x.ToString() // 2 + Diagnostic(ErrorCode.ERR_UseDefViolation, "x").WithArguments("x").WithLocation(21, 15), + // (30,15): error CS0165: Use of unassigned local variable 'y' + // : y.ToString(); // 3 + Diagnostic(ErrorCode.ERR_UseDefViolation, "y").WithArguments("y").WithLocation(30, 15), + // (37,15): error CS0165: Use of unassigned local variable 'x' + // ? x.ToString() // 4 + Diagnostic(ErrorCode.ERR_UseDefViolation, "x").WithArguments("x").WithLocation(37, 15), + // (45,15): error CS0165: Use of unassigned local variable 'x' + // ? x.ToString() // 5 + Diagnostic(ErrorCode.ERR_UseDefViolation, "x").WithArguments("x").WithLocation(45, 15), + // (54,15): error CS0165: Use of unassigned local variable 'y' + // : y.ToString(); // 6 + Diagnostic(ErrorCode.ERR_UseDefViolation, "y").WithArguments("y").WithLocation(54, 15), + // (62,15): error CS0165: Use of unassigned local variable 'y' + // : y.ToString(); // 7 + Diagnostic(ErrorCode.ERR_UseDefViolation, "y").WithArguments("y").WithLocation(62, 15) + ); + } + + [Theory] + [InlineData("int")] + [InlineData("int?")] + public void IsCondAccess_05(string returnType) + { + var source = @" +#nullable enable + +class C +{ + " + returnType + @" M0(object obj) => 1; + + void M1(C? c) + { + int x, y; + _ = c?.M0(x = y = 0) is 1 + ? x.ToString() + : y.ToString(); // 1 + } + + void M2(C? c) + { + int x, y; + _ = c?.M0(x = y = 0) is > 10 + ? x.ToString() + : y.ToString(); // 2 + } + + void M3(C? c) + { + int x, y; + _ = c?.M0(x = y = 0) is > 10 or < 0 + ? x.ToString() + : y.ToString(); // 3 + } + + void M4(C? c) + { + int x, y; + _ = c?.M0(x = y = 0) is 1 or 2 + ? x.ToString() + : y.ToString(); // 4 + } + + void M5(C? c) + { + int x, y; + _ = c?.M0(x = y = 0) is null + ? x.ToString() // 5 + : y.ToString(); + } + + void M6(C? c) + { + int x, y; + _ = c?.M0(x = y = 0) is not null + ? x.ToString() + : y.ToString(); // 6 + } +} +"; + CreateCompilation(source).VerifyDiagnostics( + // (13,15): error CS0165: Use of unassigned local variable 'y' + // : y.ToString(); // 1 + Diagnostic(ErrorCode.ERR_UseDefViolation, "y").WithArguments("y").WithLocation(13, 15), + // (21,15): error CS0165: Use of unassigned local variable 'y' + // : y.ToString(); // 2 + Diagnostic(ErrorCode.ERR_UseDefViolation, "y").WithArguments("y").WithLocation(21, 15), + // (29,15): error CS0165: Use of unassigned local variable 'y' + // : y.ToString(); // 3 + Diagnostic(ErrorCode.ERR_UseDefViolation, "y").WithArguments("y").WithLocation(29, 15), + // (37,15): error CS0165: Use of unassigned local variable 'y' + // : y.ToString(); // 4 + Diagnostic(ErrorCode.ERR_UseDefViolation, "y").WithArguments("y").WithLocation(37, 15), + // (44,15): error CS0165: Use of unassigned local variable 'x' + // ? x.ToString() // 5 + Diagnostic(ErrorCode.ERR_UseDefViolation, "x").WithArguments("x").WithLocation(44, 15), + // (53,15): error CS0165: Use of unassigned local variable 'y' + // : y.ToString(); // 6 + Diagnostic(ErrorCode.ERR_UseDefViolation, "y").WithArguments("y").WithLocation(53, 15) + ); + } + + [Fact] + public void IsCondAccess_06() + { + var source = @" +#nullable enable + +class C +{ + int M0(object obj) => 1; + + void M1(C? c) + { + int x, y; + _ = c?.M0(x = y = 0) is not null is true + ? x.ToString() + : y.ToString(); // 1 + } +} +"; + CreateCompilation(source).VerifyDiagnostics( + // (13,15): error CS0165: Use of unassigned local variable 'y' + // : y.ToString(); // 1 + Diagnostic(ErrorCode.ERR_UseDefViolation, "y").WithArguments("y").WithLocation(13, 15) + ); + } + [WorkItem(545352, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/545352")] [Fact] public void UseDefViolationInDelegateInSwitchWithGoto()