diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index afe7460a1811b6b2065256d58ce28cd66edcac4d..78d08ba14ad4ea737633a6251f7fbfa8191d2895 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -3637,6 +3637,13 @@ private void VisitMemberAccess(BoundExpression receiverOpt, Symbol member, bool } } + protected override void VisitForEachExpression(BoundForEachStatement node) + { + var expr = node.Expression; + VisitRvalue(expr); + CheckPossibleNullReceiver(expr); + } + public override void VisitForEachIterationVariables(BoundForEachStatement node) { // declare and assign all iteration variables diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs index cb05df44a796a73f18b82e567330264d9b663be1..da9766d5267f726b27e9da5e35f07c825019eda6 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs @@ -2204,7 +2204,7 @@ public override BoundNode VisitForStatement(BoundForStatement node) public override BoundNode VisitForEachStatement(BoundForEachStatement node) { // foreach ( var v in node.Expression ) { node.Body; node.ContinueLabel: } node.BreakLabel: - VisitRvalue(node.Expression); + VisitForEachExpression(node); var breakState = this.State.Clone(); LoopHead(node); VisitForEachIterationVariables(node); @@ -2215,6 +2215,11 @@ public override BoundNode VisitForEachStatement(BoundForEachStatement node) return null; } + protected virtual void VisitForEachExpression(BoundForEachStatement node) + { + VisitRvalue(node.Expression); + } + public virtual void VisitForEachIterationVariables(BoundForEachStatement node) { } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index 9c4679ae09ee7b94e7eb103363ea06cf088f2245..9b318a689cb99b738bdd67285f34c386e860804e 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -34338,6 +34338,278 @@ static void F(A[] c) Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "a2.F").WithLocation(15, 13)); } + [Fact] + public void ForEach_11() + { + var source = +@"using System.Collections.Generic; +class A +{ + public static implicit operator B?(A a) => null; +} +class B +{ +} +class C +{ + static void F(IEnumerable e) + { + foreach (var x in e) + x.ToString(); + foreach (B y in e) + y.ToString(); + foreach (B? z in e) + { + z.ToString(); + if (z != null) z.ToString(); + } + } +}"; + var comp = CreateCompilation(new[] { source, NonNullTypesTrue, NonNullTypesAttributesDefinition }); + // PROTOTYPE(NullableReferenceTypes): Location of WRN_NullabilityMismatchInAssignment should be `y` rather than `B`. + // PROTOTYPE(NullableReferenceTypes): Reword WRN_NullabilityMismatchInAssignment since there is not an explicit assignment. + comp.VerifyDiagnostics( + // (15,18): warning CS8601: Possible null reference assignment. + // foreach (B y in e) + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "B").WithLocation(15, 18), + // (16,13): warning CS8602: Possible dereference of a null reference. + // y.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "y").WithLocation(16, 13), + // (19,13): warning CS8602: Possible dereference of a null reference. + // z.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "z").WithLocation(19, 13)); + } + + [WorkItem(23493, "https://github.com/dotnet/roslyn/issues/23493")] + [Fact] + public void ForEach_12() + { + var source = +@"using System.Collections; +using System.Collections.Generic; +class C +{ + static void F() + { + foreach (var x in (IEnumerable?)null) // 1 + { + } + foreach (var y in (IEnumerable)default) // 2 + { + } + foreach (var z in default(IEnumerable)) // 3 + { + } + } +}"; + var comp = CreateCompilation(new[] { source, NonNullTypesTrue, NonNullTypesAttributesDefinition }); + comp.VerifyDiagnostics( + // (7,27): error CS0186: Use of null is not valid in this context + // foreach (var x in (IEnumerable?)null) // 1 + Diagnostic(ErrorCode.ERR_NullNotValid, "(IEnumerable?)null").WithLocation(7, 27), + // (10,27): error CS0186: Use of null is not valid in this context + // foreach (var y in (IEnumerable)default) // 2 + Diagnostic(ErrorCode.ERR_NullNotValid, "(IEnumerable)default").WithLocation(10, 27), + // (13,27): error CS0186: Use of null is not valid in this context + // foreach (var z in default(IEnumerable)) // 3 + Diagnostic(ErrorCode.ERR_NullNotValid, "default(IEnumerable)").WithLocation(13, 27), + // (7,27): warning CS8602: Possible dereference of a null reference. + // foreach (var x in (IEnumerable?)null) // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(IEnumerable?)null").WithLocation(7, 27), + // (10,27): warning CS8600: Converting null literal or possible null value to non-nullable type. + // foreach (var y in (IEnumerable)default) // 2 + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(IEnumerable)default").WithLocation(10, 27), + // (10,27): warning CS8602: Possible dereference of a null reference. + // foreach (var y in (IEnumerable)default) // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(IEnumerable)default").WithLocation(10, 27), + // (13,27): warning CS8602: Possible dereference of a null reference. + // foreach (var z in default(IEnumerable)) // 3 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "default(IEnumerable)").WithLocation(13, 27)); + } + + [WorkItem(23493, "https://github.com/dotnet/roslyn/issues/23493")] + [Fact] + public void ForEach_13() + { + var source = +@"using System.Collections; +using System.Collections.Generic; +class C +{ + static void F1(object[]? c1) + { + foreach (var x in c1) // 1 + { + } + foreach (var y in (IEnumerable)c1) // 2 + { + } + if (c1 == null) return; + foreach (var z in c1) + { + } + } + static void F2(IList? c2) + { + foreach (var x in c2) // 3 + { + } + foreach (var y in (IEnumerable?)c2) // 4 + { + } + } +}"; + var comp = CreateCompilation(new[] { source, NonNullTypesTrue, NonNullTypesAttributesDefinition }); + comp.VerifyDiagnostics( + // (7,27): warning CS8602: Possible dereference of a null reference. + // foreach (var x in c1) // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c1").WithLocation(7, 27), + // (10,27): warning CS8600: Converting null literal or possible null value to non-nullable type. + // foreach (var y in (IEnumerable)c1) // 2 + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(IEnumerable)c1").WithLocation(10, 27), + // (10,27): warning CS8602: Possible dereference of a null reference. + // foreach (var y in (IEnumerable)c1) // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(IEnumerable)c1").WithLocation(10, 27), + // (20,27): warning CS8602: Possible dereference of a null reference. + // foreach (var x in c2) // 3 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c2").WithLocation(20, 27), + // (23,27): warning CS8602: Possible dereference of a null reference. + // foreach (var y in (IEnumerable?)c2) // 4 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(IEnumerable?)c2").WithLocation(23, 27)); + } + + [WorkItem(23493, "https://github.com/dotnet/roslyn/issues/23493")] + [Fact] + public void ForEach_14() + { + var source = +@"using System.Collections; +using System.Collections.Generic; +class C +{ + static void F1(T t1) where T : class?, IEnumerable? + { + foreach (var x in t1) // 1 + { + } + foreach (var y in (IEnumerable?)t1) // 2 + { + } + foreach (var z in (IEnumerable)t1) // 3 + { + } + } + static void F2(T t2) where T : class? + { + foreach (var w in (IEnumerable?)t2) // 4 + { + } + foreach (var v in (IEnumerable)t2) // 5 + { + } + } +}"; + var comp = CreateCompilation(new[] { source, NonNullTypesTrue, NonNullTypesAttributesDefinition }); + comp.VerifyDiagnostics( + // (7,27): warning CS8602: Possible dereference of a null reference. + // foreach (var x in t1) // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "t1").WithLocation(7, 27), + // (10,27): warning CS8602: Possible dereference of a null reference. + // foreach (var y in (IEnumerable?)t1) // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(IEnumerable?)t1").WithLocation(10, 27), + // (13,27): warning CS8600: Converting null literal or possible null value to non-nullable type. + // foreach (var z in (IEnumerable)t1) // 3 + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(IEnumerable)t1").WithLocation(13, 27), + // (13,27): warning CS8602: Possible dereference of a null reference. + // foreach (var z in (IEnumerable)t1) // 3 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(IEnumerable)t1").WithLocation(13, 27), + // (19,27): warning CS8602: Possible dereference of a null reference. + // foreach (var w in (IEnumerable?)t2) // 4 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(IEnumerable?)t2").WithLocation(19, 27), + // (22,27): warning CS8600: Converting null literal or possible null value to non-nullable type. + // foreach (var v in (IEnumerable)t2) // 5 + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(IEnumerable)t2").WithLocation(22, 27), + // (22,27): warning CS8602: Possible dereference of a null reference. + // foreach (var v in (IEnumerable)t2) // 5 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(IEnumerable)t2").WithLocation(22, 27)); + } + + [WorkItem(23493, "https://github.com/dotnet/roslyn/issues/23493")] + [Fact] + public void ForEach_15() + { + var source = +@"using System.Collections; +using System.Collections.Generic; +class C +{ + static void F1(T t1) where T : IEnumerable? + { + foreach (var x in t1) // 1 + { + } + foreach (var w in (IEnumerable?)t1) // 2 + { + } + foreach (var v in (IEnumerable)t1) // 3 + { + } + } + static void F2(T t2) + { + foreach (var y in (IEnumerable?)t2) // 4 + { + } + foreach (var z in (IEnumerable)t2) // 5 + { + } + } +}"; + var comp = CreateCompilation(new[] { source, NonNullTypesTrue, NonNullTypesAttributesDefinition }); + comp.VerifyDiagnostics( + // (7,27): warning CS8602: Possible dereference of a null reference. + // foreach (var x in t1) // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "t1").WithLocation(7, 27), + // (10,27): warning CS8602: Possible dereference of a null reference. + // foreach (var w in (IEnumerable?)t1) // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(IEnumerable?)t1").WithLocation(10, 27), + // (13,27): warning CS8600: Converting null literal or possible null value to non-nullable type. + // foreach (var v in (IEnumerable)t1) // 3 + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(IEnumerable)t1").WithLocation(13, 27), + // (13,27): warning CS8602: Possible dereference of a null reference. + // foreach (var v in (IEnumerable)t1) // 3 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(IEnumerable)t1").WithLocation(13, 27)); + } + + [Fact] + public void ForEach_16() + { + var source = +@"using System.Collections; +class Enumerable : IEnumerable +{ + public IEnumerator? GetEnumerator() => null; +} +class C +{ + static void F(Enumerable e) + { + foreach (var x in e) // 1 + { + } + foreach (var y in (IEnumerable?)e) + { + } + foreach (var z in (IEnumerable)e) + { + } + } +}"; + var comp = CreateCompilation(new[] { source, NonNullTypesTrue, NonNullTypesAttributesDefinition }); + // PROTOTYPE(NullableReferenceTypes): Should report WRN_NullReferenceReceiver using Enumerable.GetEnumerator. + comp.VerifyDiagnostics(); + } + [Fact] public void TypeInference_01() {