未验证 提交 3b1a465f 编写于 作者: J Julien Couvreur 提交者: GitHub

Fix inference with ref parameter with maybe-null value (#47952)

上级 b7e71f04
......@@ -5797,9 +5797,8 @@ private ImmutableArray<BoundExpression> GetArgumentsForMethodTypeInference(Immut
{
var visitArgumentResult = argumentResults[i];
var lambdaState = visitArgumentResult.StateForLambda;
var argumentResult = visitArgumentResult.LValueType;
if (!argumentResult.HasType)
argumentResult = visitArgumentResult.RValueType.ToTypeWithAnnotations(compilation);
// Note: for `out` arguments, the argument result contains the declaration type (see `VisitArgumentEvaluate`)
var argumentResult = visitArgumentResult.RValueType.ToTypeWithAnnotations(compilation);
builder.Add(getArgumentForMethodTypeInference(arguments[i], argumentResult, lambdaState));
}
return builder.ToImmutableAndFree();
......
......@@ -30765,7 +30765,7 @@ public void M<T>() where T : new()
[Fact]
[WorkItem(37909, "https://github.com/dotnet/roslyn/issues/37909")]
public void RefParameter_TypeInferenceUsesLValueType()
public void RefParameter_TypeInferenceUsesRValueType()
{
var c = CreateNullableCompilation(@"
class C
......@@ -30774,26 +30774,226 @@ public void M(string? s)
{
if (s is null) return;
var t = M2(ref s);
s.ToString(); // 1
t.ToString(); // 2
s.ToString();
t.ToString();
}
T M2<T>(ref T t) => throw null!;
}
");
c.VerifyDiagnostics();
}
[Fact]
[WorkItem(37909, "https://github.com/dotnet/roslyn/issues/37909")]
public void OutParameter_TypeInferenceUsesLValueType()
{
var c = CreateNullableCompilation(@"
class C
{
void M()
{
string? s1;
var t1 = M2(out s1);
s1.ToString(); // 1
t1.ToString(); // 2
string? s2 = string.Empty;
var t2 = M2(out s2);
s2.ToString(); // 3
t2.ToString(); // 4
string? s3 = null;
var t3 = M2(out s3);
s3.ToString(); // 5
t3.ToString(); // 6
}
T M2<T>(out T t) => throw null!;
}
");
c.VerifyDiagnostics(
// (8,9): warning CS8602: Dereference of a possibly null reference.
// s.ToString(); // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(8, 9),
// s1.ToString(); // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s1").WithLocation(8, 9),
// (9,9): warning CS8602: Dereference of a possibly null reference.
// t.ToString(); // 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "t").WithLocation(9, 9)
// t1.ToString(); // 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "t1").WithLocation(9, 9),
// (13,9): warning CS8602: Dereference of a possibly null reference.
// s2.ToString(); // 3
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s2").WithLocation(13, 9),
// (14,9): warning CS8602: Dereference of a possibly null reference.
// t2.ToString(); // 4
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "t2").WithLocation(14, 9),
// (18,9): warning CS8602: Dereference of a possibly null reference.
// s3.ToString(); // 5
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s3").WithLocation(18, 9),
// (19,9): warning CS8602: Dereference of a possibly null reference.
// t3.ToString(); // 6
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "t3").WithLocation(19, 9)
);
}
[Fact]
[WorkItem(47663, "https://github.com/dotnet/roslyn/issues/47663")]
public void RefParameter_Issue_47663()
{
var source = @"
#nullable enable
using System.Diagnostics.CodeAnalysis;
class C
{
static void X1<T>([NotNullIfNotNull(""value"")] ref T location, T value) where T : class? { }
static void X2<T>(ref T location, T value) where T : class? { }
private readonly double[] f1;
private readonly double[] f2;
C()
{
double[] bar = new double[3];
X1(ref f1, bar);
X2(ref f2, bar); // 1, warn on outbound assignment
}
}
";
var comp = CreateCompilation(new[] { source, NotNullIfNotNullAttributeDefinition });
comp.VerifyEmitDiagnostics(
// (14,5): warning CS8618: Non-nullable field 'f2' must contain a non-null value when exiting constructor. Consider declaring the field as nullable.
// C()
Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C", isSuppressed: false).WithArguments("field", "f2").WithLocation(14, 5),
// (18,16): warning CS8601: Possible null reference assignment.
// X2(ref f2, bar); // 1, warn on outbound assignment
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "f2", isSuppressed: false).WithLocation(18, 16)
);
}
[Fact]
public void RefParameter_InterlockedExchange_ObliviousContext()
{
var source = @"
#nullable enable warnings
class C
{
object o;
void M()
{
InterlockedExchange(ref o, null);
}
#nullable enable
void InterlockedExchange<T>(ref T location, T value) { }
}
";
// This situation was encountered in VS codebases
var comp = CreateCompilation(source);
comp.VerifyEmitDiagnostics(
// (10,36): warning CS8625: Cannot convert null literal to non-nullable reference type.
// InterlockedExchange(ref o, null);
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null", isSuppressed: false).WithLocation(10, 36)
);
}
[Fact]
public void RefParameter_InterlockedExchange_NullableEnabledContext()
{
var source = @"
#nullable enable
class C
{
object? o;
void M()
{
InterlockedExchange(ref o, null);
}
void InterlockedExchange<T>(ref T location, T value) { }
}
";
// With proper annotation on the field, we have no warning
var comp = CreateCompilation(source);
comp.VerifyEmitDiagnostics();
}
[Fact]
public void RefParameter_MiscPermutations()
{
var source = @"
#nullable enable
class C
{
static T X<T>(ref T x, ref T y) => throw null!;
void M1(string? maybeNull)
{
X(ref maybeNull, ref maybeNull).ToString(); // 1
}
void M2(string? maybeNull, string notNull)
{
X(ref maybeNull, ref notNull).ToString(); // 2
}
void M3(string notNull)
{
X(ref notNull, ref notNull).ToString();
}
void M4(string? maybeNull, string notNull)
{
X(ref notNull, ref maybeNull).ToString(); // 3
}
}
";
var comp = CreateCompilation(source);
comp.VerifyEmitDiagnostics(
// (10,9): warning CS8602: Dereference of a possibly null reference.
// X(ref maybeNull, ref maybeNull).ToString(); // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "X(ref maybeNull, ref maybeNull)").WithLocation(10, 9),
// (15,15): warning CS8601: Possible null reference assignment.
// X(ref maybeNull, ref notNull).ToString(); // 2
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "maybeNull").WithLocation(15, 15),
// (25,28): warning CS8601: Possible null reference assignment.
// X(ref notNull, ref maybeNull).ToString(); // 3
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "maybeNull").WithLocation(25, 28)
);
}
[Fact]
[WorkItem(35534, "https://github.com/dotnet/roslyn/issues/35534")]
public void RefParameter_Issue_35534()
{
var source = @"
#nullable enable
public class C
{
void M2()
{
string? x = ""hello"";
var y = M(ref x);
y.ToString();
}
T M<T>(ref T t)
{
throw null!;
}
}";
var comp = CreateCompilation(source);
comp.VerifyEmitDiagnostics();
}
[Fact]
[WorkItem(37909, "https://github.com/dotnet/roslyn/issues/37909")]
public void RefParameter_TypeInferenceUsesLValueType_WithNullArgument()
public void RefParameter_TypeInferenceUsesRValueType_WithNullArgument()
{
var c = CreateNullableCompilation(@"
class C
......@@ -30801,21 +31001,30 @@ class C
public void M(string? s)
{
if (s is null) return;
var t = Copy(ref s, null);
s.ToString(); // 1
t.ToString(); // 2
var t = Copy(ref s, null); // 1
s.ToString();
t.ToString();
}
public void M2(string? s)
{
if (s is null) return;
var t = Copy2(s, null); // 2
s.ToString();
t.ToString();
}
T Copy<T>(ref T t, T t2) => throw null!;
T Copy2<T>(T t, T t2) => throw null!;
}
");
// known issue with null in method type inference: https://github.com/dotnet/roslyn/issues/43536
c.VerifyDiagnostics(
// (8,9): warning CS8602: Dereference of a possibly null reference.
// s.ToString(); // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(8, 9),
// (9,9): warning CS8602: Dereference of a possibly null reference.
// t.ToString(); // 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "t").WithLocation(9, 9)
// (7,29): warning CS8625: Cannot convert null literal to non-nullable reference type.
// var t = Copy(ref s, null); // 1
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(7, 29),
// (14,26): warning CS8625: Cannot convert null literal to non-nullable reference type.
// var t = Copy2(s, null); // 2
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(14, 26)
);
}
......@@ -50605,18 +50814,18 @@ void Test1(CL1 x1, CL1? z1)
{
var y1 = new { p1 = x1, p2 = z1 };
x1 = y1.p1 ?? x1;
x1 = y1.p2;
x1 = y1.p2; // 1
}
void Test2(CL1 x2, CL1? z2)
{
var u2 = new { p1 = x2, p2 = z2 };
var v2 = new { p1 = z2, p2 = x2 };
u2 = v2;
u2 = v2; // 2
x2 = u2.p2 ?? x2;
x2 = u2.p1;
x2 = v2.p2 ?? x2;
x2 = v2.p1;
x2 = u2.p1; // 3
x2 = v2.p2 ?? x2; // 4
x2 = v2.p1; // 5
}
void Test3(CL1 x3, CL1? z3)
......@@ -50624,18 +50833,18 @@ void Test3(CL1 x3, CL1? z3)
var u3 = new { p1 = x3, p2 = z3 };
var v3 = u3;
x3 = v3.p1 ?? x3;
x3 = v3.p2;
x3 = v3.p2; // 6
}
void Test4(CL1 x4, CL1? z4)
{
var u4 = new { p0 = new { p1 = x4, p2 = z4 } };
var v4 = new { p0 = new { p1 = z4, p2 = x4 } };
u4 = v4;
u4 = v4; // 7
x4 = u4.p0.p2 ?? x4;
x4 = u4.p0.p1;
x4 = v4.p0.p2 ?? x4;
x4 = v4.p0.p1;
x4 = u4.p0.p1; // 8
x4 = v4.p0.p2 ?? x4; // 9
x4 = v4.p0.p1; // 10
}
void Test5(CL1 x5, CL1? z5)
......@@ -50643,7 +50852,7 @@ void Test5(CL1 x5, CL1? z5)
var u5 = new { p0 = new { p1 = x5, p2 = z5 } };
var v5 = u5;
x5 = v5.p0.p1 ?? x5;
x5 = v5.p0.p2;
x5 = v5.p0.p2; // 11
}
void Test6(CL1 x6, CL1? z6)
......@@ -50651,7 +50860,7 @@ void Test6(CL1 x6, CL1? z6)
var u6 = new { p0 = new { p1 = x6, p2 = z6 } };
var v6 = u6.p0;
x6 = v6.p1 ?? x6;
x6 = v6.p2;
x6 = v6.p2; // 12
}
void Test7(CL1 x7, CL1? z7)
......@@ -50660,9 +50869,9 @@ void Test7(CL1 x7, CL1? z7)
var v7 = new { p0 = new S1() { p1 = z7, p2 = x7 } };
u7 = v7;
x7 = u7.p0.p2 ?? x7;
x7 = u7.p0.p1;
x7 = v7.p0.p2 ?? x7;
x7 = v7.p0.p1;
x7 = u7.p0.p1; // 13
x7 = v7.p0.p2 ?? x7; // 14
x7 = v7.p0.p1; // 15
}
void Test8(CL1 x8, CL1? z8)
......@@ -50670,7 +50879,7 @@ void Test8(CL1 x8, CL1? z8)
var u8 = new { p0 = new S1() { p1 = x8, p2 = z8 } };
var v8 = u8;
x8 = v8.p0.p1 ?? x8;
x8 = v8.p0.p2;
x8 = v8.p0.p2; // 16
}
void Test9(CL1 x9, CL1? z9)
......@@ -50678,7 +50887,7 @@ void Test9(CL1 x9, CL1? z9)
var u9 = new { p0 = new S1() { p1 = x9, p2 = z9 } };
var v9 = u9.p0;
x9 = v9.p1 ?? x9;
x9 = v9.p2;
x9 = v9.p2; // 17
}
void M1<T>(ref T x) {}
......@@ -50686,15 +50895,15 @@ void Test9(CL1 x9, CL1? z9)
void Test10(CL1 x10)
{
var u10 = new { a0 = x10, a1 = new { p1 = x10 }, a2 = new S1() { p2 = x10 } };
x10 = u10.a0; // 1
x10 = u10.a1.p1; // 2
x10 = u10.a2.p2; // 3
x10 = u10.a0;
x10 = u10.a1.p1;
x10 = u10.a2.p2;
M1(ref u10);
x10 = u10.a0; // 4
x10 = u10.a1.p1; // 5
x10 = u10.a2.p2; // 6
x10 = u10.a0;
x10 = u10.a1.p1;
x10 = u10.a2.p2; // 18
}
}
......@@ -50710,61 +50919,58 @@ struct S1
c.VerifyDiagnostics(
// (11,14): warning CS8600: Converting null literal or possible null value to non-nullable type.
// x1 = y1.p2;
// x1 = y1.p2; // 1
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "y1.p2").WithLocation(11, 14),
// (18,14): warning CS8619: Nullability of reference types in value of type '<anonymous type: CL1? p1, CL1 p2>' doesn't match target type '<anonymous type: CL1 p1, CL1? p2>'.
// u2 = v2;
// u2 = v2; // 2
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "v2").WithArguments("<anonymous type: CL1? p1, CL1 p2>", "<anonymous type: CL1 p1, CL1? p2>").WithLocation(18, 14),
// (20,14): warning CS8600: Converting null literal or possible null value to non-nullable type.
// x2 = u2.p1;
// x2 = u2.p1; // 3
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "u2.p1").WithLocation(20, 14),
// (21,14): warning CS8600: Converting null literal or possible null value to non-nullable type.
// x2 = v2.p2 ?? x2;
// x2 = v2.p2 ?? x2; // 4
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "v2.p2 ?? x2").WithLocation(21, 14),
// (22,14): warning CS8600: Converting null literal or possible null value to non-nullable type.
// x2 = v2.p1;
// x2 = v2.p1; // 5
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "v2.p1").WithLocation(22, 14),
// (30,14): warning CS8600: Converting null literal or possible null value to non-nullable type.
// x3 = v3.p2;
// x3 = v3.p2; // 6
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "v3.p2").WithLocation(30, 14),
// (37,14): warning CS8619: Nullability of reference types in value of type '<anonymous type: <anonymous type: CL1? p1, CL1 p2> p0>' doesn't match target type '<anonymous type: <anonymous type: CL1 p1, CL1? p2> p0>'.
// u4 = v4;
// u4 = v4; // 7
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "v4").WithArguments("<anonymous type: <anonymous type: CL1? p1, CL1 p2> p0>", "<anonymous type: <anonymous type: CL1 p1, CL1? p2> p0>").WithLocation(37, 14),
// (39,14): warning CS8600: Converting null literal or possible null value to non-nullable type.
// x4 = u4.p0.p1;
// x4 = u4.p0.p1; // 8
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "u4.p0.p1").WithLocation(39, 14),
// (40,14): warning CS8600: Converting null literal or possible null value to non-nullable type.
// x4 = v4.p0.p2 ?? x4;
// x4 = v4.p0.p2 ?? x4; // 9
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "v4.p0.p2 ?? x4").WithLocation(40, 14),
// (41,14): warning CS8600: Converting null literal or possible null value to non-nullable type.
// x4 = v4.p0.p1;
// x4 = v4.p0.p1; // 10
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "v4.p0.p1").WithLocation(41, 14),
// (49,14): warning CS8600: Converting null literal or possible null value to non-nullable type.
// x5 = v5.p0.p2;
// x5 = v5.p0.p2; // 11
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "v5.p0.p2").WithLocation(49, 14),
// (57,14): warning CS8600: Converting null literal or possible null value to non-nullable type.
// x6 = v6.p2;
// x6 = v6.p2; // 12
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "v6.p2").WithLocation(57, 14),
// (66,14): warning CS8600: Converting null literal or possible null value to non-nullable type.
// x7 = u7.p0.p1;
// x7 = u7.p0.p1; // 13
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "u7.p0.p1").WithLocation(66, 14),
// (67,14): warning CS8600: Converting null literal or possible null value to non-nullable type.
// x7 = v7.p0.p2 ?? x7;
// x7 = v7.p0.p2 ?? x7; // 14
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "v7.p0.p2 ?? x7").WithLocation(67, 14),
// (68,14): warning CS8600: Converting null literal or possible null value to non-nullable type.
// x7 = v7.p0.p1;
// x7 = v7.p0.p1; // 15
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "v7.p0.p1").WithLocation(68, 14),
// (76,14): warning CS8600: Converting null literal or possible null value to non-nullable type.
// x8 = v8.p0.p2;
// x8 = v8.p0.p2; // 16
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "v8.p0.p2").WithLocation(76, 14),
// (84,14): warning CS8600: Converting null literal or possible null value to non-nullable type.
// x9 = v9.p2;
// x9 = v9.p2; // 17
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "v9.p2").WithLocation(84, 14),
// (98,15): warning CS8602: Dereference of a possibly null reference.
// x10 = u10.a0; // 4
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "u10").WithLocation(98, 15),
// (100,15): warning CS8600: Converting null literal or possible null value to non-nullable type.
// x10 = u10.a2.p2; // 6
// x10 = u10.a2.p2; // 18
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "u10.a2.p2").WithLocation(100, 15));
}
......@@ -127847,13 +128053,10 @@ void M(object? o1)
// 2 states * 3 states * 3 states = 18 combinations
[InlineData("locationNonNull", "valueNonNull", "comparandNonNull", true)]
[InlineData("locationNonNull", "valueNonNull", "comparandMaybeNull", true)]
[InlineData("locationNonNull", "valueNonNull", "null", true)]
[InlineData("locationNonNull", "valueMaybeNull", "comparandNonNull", false)]
[InlineData("locationNonNull", "valueMaybeNull", "comparandMaybeNull", false)]
[InlineData("locationNonNull", "valueMaybeNull", "null", false)]
[InlineData("locationNonNull", "null", "comparandNonNull", false)]
[InlineData("locationNonNull", "null", "comparandMaybeNull", false)]
[InlineData("locationNonNull", "null", "null", false)]
[InlineData("locationMaybeNull", "valueNonNull", "comparandNonNull", false)]
[InlineData("locationMaybeNull", "valueNonNull", "comparandMaybeNull", false)]
[InlineData("locationMaybeNull", "valueNonNull", "null", true)]
......@@ -127869,14 +128072,209 @@ public void CompareExchange_LocationNullState(string location, string value, str
var source = $@"
#pragma warning disable 0436
using System.Threading;
using System.Diagnostics.CodeAnalysis;
namespace System.Threading
{{
public static class Interlocked
{{
public static object? CompareExchange([NotNullIfNotNull(""value"")] ref object? location, object? value, object? comparand) {{ return location; }}
}}
}}
class C
{{
// locationNonNull is annotated, but its flow state is non-null
void M(object? locationNonNull, object? locationMaybeNull, object comparandNonNull, object? comparandMaybeNull, object valueNonNull, object? valueMaybeNull)
{{
locationNonNull = new object();
Interlocked.CompareExchange(ref {location}, {value}, {comparand});
_ = {location}.ToString();
}}
}}
";
var comp = CreateCompilation(new[] { source, NotNullIfNotNullAttributeDefinition }, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(isLocationNonNullAfterCall
? Array.Empty<DiagnosticDescription>()
: new[]
{
// (19,13): warning CS8602: Dereference of a possibly null reference.
// _ = {location}.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, location).WithLocation(19, 13)
});
source = $@"
#pragma warning disable 0436
using System.Threading;
namespace System.Threading
{{
public static class Interlocked
{{
public static T CompareExchange<T>(ref T location, T value, T comparand) where T : class? {{ return location; }}
}}
}}
class C
{{
// locationNonNull is annotated, but its flow state is non-null
void M<T>(T? locationNonNull, T? locationMaybeNull, T comparandNonNull, T? comparandMaybeNull, T valueNonNull, T? valueMaybeNull) where T : class, new()
{{
locationNonNull = new T();
Interlocked.CompareExchange(ref {location}, {value}, {comparand});
_ = {location}.ToString();
}}
}}
";
comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(isLocationNonNullAfterCall
? Array.Empty<DiagnosticDescription>()
: new[]
{
// (19,13): warning CS8602: Dereference of a possibly null reference.
// _ = {location}.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, location).WithLocation(19, 13)
});
source = $@"
class C
{{
// locationNonNull is annotated, but its flow state is non-null
void M(object? locationNonNull, object? locationMaybeNull, object comparandNonNull, object? comparandMaybeNull, object valueNonNull, object? valueMaybeNull)
{{
locationNonNull = new object();
if ({location} == {comparand})
{{
{location} = {value};
}}
_ = {location}.ToString();
}}
}}
";
comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(isLocationNonNullAfterCall
? Array.Empty<DiagnosticDescription>()
: new[]
{
// (12,13): warning CS8602: Dereference of a possibly null reference.
// _ = {location}.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, location).WithLocation(12, 13)
});
}
// This test should be merged back into CompareExchange_LocationNullState when the issue with inference with null is resolved
// Currently differs by a diagnostic
[Theory]
[InlineData("locationNonNull", "null", "comparandNonNull", false)]
public void CompareExchange_LocationNullState_2(string location, string value, string comparand, bool isLocationNonNullAfterCall)
{
var source = $@"
#pragma warning disable 0436
using System.Threading;
using System.Diagnostics.CodeAnalysis;
namespace System.Threading
{{
public static class Interlocked
{{
public static object? CompareExchange([NotNullIfNotNull(""value"")] ref object? location, object? value, object? comparand) {{ return location; }}
}}
}}
class C
{{
// locationNonNull is annotated, but its flow state is non-null
void M(object? locationNonNull, object? locationMaybeNull, object comparandNonNull, object? comparandMaybeNull, object valueNonNull, object? valueMaybeNull)
{{
locationNonNull = new object();
Interlocked.CompareExchange(ref {location}, {value}, {comparand});
_ = {location}.ToString();
}}
}}
";
var comp = CreateCompilation(new[] { source, NotNullIfNotNullAttributeDefinition }, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(isLocationNonNullAfterCall
? Array.Empty<DiagnosticDescription>()
: new[]
{
// (19,13): warning CS8602: Dereference of a possibly null reference.
// _ = {location}.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, location).WithLocation(19, 13)
});
source = $@"
#pragma warning disable 0436
using System.Threading;
namespace System.Threading
{{
public static class Interlocked
{{
public static object? CompareExchange(ref object? location, object? value, object? comparand) {{ return location; }}
public static T CompareExchange<T>(ref T location, T value, T comparand) where T : class? {{ return location; }}
}}
}}
class C
{{
// locationNonNull is annotated, but its flow state is non-null
void M<T>(T? locationNonNull, T? locationMaybeNull, T comparandNonNull, T? comparandMaybeNull, T valueNonNull, T? valueMaybeNull) where T : class, new()
{{
locationNonNull = new T();
Interlocked.CompareExchange(ref {location}, {value}, {comparand});
_ = {location}.ToString();
}}
}}
";
comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(isLocationNonNullAfterCall
? Array.Empty<DiagnosticDescription>()
: new[]
{
// (18,58): warning CS8625: Cannot convert null literal to non-nullable reference type.
// Interlocked.CompareExchange(ref locationNonNull, null, comparandNonNull);
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(18, 58), // Unexpected warning, due to method type inference with null https://github.com/dotnet/roslyn/issues/43536
// (19,13): warning CS8602: Dereference of a possibly null reference.
// _ = locationNonNull.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "locationNonNull").WithLocation(19, 13)
});
source = $@"
class C
{{
// locationNonNull is annotated, but its flow state is non-null
void M(object? locationNonNull, object? locationMaybeNull, object comparandNonNull, object? comparandMaybeNull, object valueNonNull, object? valueMaybeNull)
{{
locationNonNull = new object();
if ({location} == {comparand})
{{
{location} = {value};
}}
_ = {location}.ToString();
}}
}}
";
comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(isLocationNonNullAfterCall
? Array.Empty<DiagnosticDescription>()
: new[]
{
// (12,13): warning CS8602: Dereference of a possibly null reference.
// _ = {location}.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, location).WithLocation(12, 13)
});
}
// This test should be merged back into CompareExchange_LocationNullState when the issue with inference with null is resolved
// Currently differs by a diagnostic
[Theory]
[InlineData("locationNonNull", "null", "null", false)]
public void CompareExchange_LocationNullState_3(string location, string value, string comparand, bool isLocationNonNullAfterCall)
{
var source = $@"
#pragma warning disable 0436
using System.Threading;
using System.Diagnostics.CodeAnalysis;
namespace System.Threading
{{
public static class Interlocked
{{
public static object? CompareExchange([NotNullIfNotNull(""value"")] ref object? location, object? value, object? comparand) {{ return location; }}
}}
}}
class C
{{
// locationNonNull is annotated, but its flow state is non-null
......@@ -127888,7 +128286,7 @@ void M(object? locationNonNull, object? locationMaybeNull, object comparandNonNu
}}
}}
";
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
var comp = CreateCompilation(new[] { source, NotNullIfNotNullAttributeDefinition }, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(isLocationNonNullAfterCall
? Array.Empty<DiagnosticDescription>()
: new[]
......@@ -127921,6 +128319,76 @@ void M<T>(T? locationNonNull, T? locationMaybeNull, T comparandNonNull, T? compa
}}
";
comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(isLocationNonNullAfterCall
? Array.Empty<DiagnosticDescription>()
: new[]
{
// (18,58): warning CS8625: Cannot convert null literal to non-nullable reference type.
// Interlocked.CompareExchange(ref locationNonNull, null, null);
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(18, 58), // Unexpected warning, due to method type inference with null https://github.com/dotnet/roslyn/issues/43536
// (18,64): warning CS8625: Cannot convert null literal to non-nullable reference type.
// Interlocked.CompareExchange(ref locationNonNull, null, null);
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(18, 64),
// (19,13): warning CS8602: Dereference of a possibly null reference.
// _ = locationNonNull.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "locationNonNull").WithLocation(19, 13)
});
source = $@"
class C
{{
// locationNonNull is annotated, but its flow state is non-null
void M(object? locationNonNull, object? locationMaybeNull, object comparandNonNull, object? comparandMaybeNull, object valueNonNull, object? valueMaybeNull)
{{
locationNonNull = new object();
if ({location} == {comparand})
{{
{location} = {value};
}}
_ = {location}.ToString();
}}
}}
";
comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(isLocationNonNullAfterCall
? Array.Empty<DiagnosticDescription>()
: new[]
{
// (12,13): warning CS8602: Dereference of a possibly null reference.
// _ = {location}.ToString();
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, location).WithLocation(12, 13)
});
}
// This test should be merged back into CompareExchange_LocationNullState when the issue with inference with null is resolved
// Currently differs by a diagnostic
[Theory]
[InlineData("locationNonNull", "valueNonNull", "null", true)]
public void CompareExchange_LocationNullState_4(string location, string value, string comparand, bool isLocationNonNullAfterCall)
{
var source = $@"
#pragma warning disable 0436
using System.Threading;
using System.Diagnostics.CodeAnalysis;
namespace System.Threading
{{
public static class Interlocked
{{
public static object? CompareExchange([NotNullIfNotNull(""value"")] ref object? location, object? value, object? comparand) {{ return location; }}
}}
}}
class C
{{
// locationNonNull is annotated, but its flow state is non-null
void M(object? locationNonNull, object? locationMaybeNull, object comparandNonNull, object? comparandMaybeNull, object valueNonNull, object? valueMaybeNull)
{{
locationNonNull = new object();
Interlocked.CompareExchange(ref {location}, {value}, {comparand});
_ = {location}.ToString();
}}
}}
";
var comp = CreateCompilation(new[] { source, NotNullIfNotNullAttributeDefinition }, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(isLocationNonNullAfterCall
? Array.Empty<DiagnosticDescription>()
: new[]
......@@ -127930,6 +128398,35 @@ void M<T>(T? locationNonNull, T? locationMaybeNull, T comparandNonNull, T? compa
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, location).WithLocation(19, 13)
});
source = $@"
#pragma warning disable 0436
using System.Threading;
namespace System.Threading
{{
public static class Interlocked
{{
public static T CompareExchange<T>(ref T location, T value, T comparand) where T : class? {{ return location; }}
}}
}}
class C
{{
// locationNonNull is annotated, but its flow state is non-null
void M<T>(T? locationNonNull, T? locationMaybeNull, T comparandNonNull, T? comparandMaybeNull, T valueNonNull, T? valueMaybeNull) where T : class, new()
{{
locationNonNull = new T();
Interlocked.CompareExchange(ref {location}, {value}, {comparand});
_ = {location}.ToString();
}}
}}
";
comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(
// (18,72): warning CS8625: Cannot convert null literal to non-nullable reference type.
// Interlocked.CompareExchange(ref locationNonNull, valueNonNull, null);
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(18, 72) // Unexpected warning, due to method type inference with null https://github.com/dotnet/roslyn/issues/43536
);
source = $@"
class C
{{
......@@ -128091,14 +128588,14 @@ public void CompareExchangeT_NonNullLocation_MaybeNullReturnValue()
var source = @"
#pragma warning disable 0436
using System.Threading;
using System.Diagnostics.CodeAnalysis;
namespace System.Threading
{
public static class Interlocked
{
public static T CompareExchange<T>(ref T location, T value, T comparand) where T : class? { return location; }
public static T CompareExchange<T>([NotNullIfNotNull(""value"")] ref T location, T value, T comparand) where T : class? { return location; }
}
}
class C
{
// location is annotated, but its flow state is non-null
......@@ -128107,6 +128604,11 @@ void M<T>(T? location, T value, T comparand) where T : class, new()
location = new T();
var result = Interlocked.CompareExchange(ref location, value, comparand);
_ = location.ToString();
_ = result.ToString();
location = null;
result = Interlocked.CompareExchange(ref location, value, null);
_ = location.ToString();
_ = result.ToString(); // 1
}
}
......@@ -128114,11 +128616,11 @@ void M<T>(T? location, T value, T comparand) where T : class, new()
// Note: the return value of CompareExchange is the value in `location` before the call.
// Therefore it might make sense to give the return value a flow state equal to the flow state of `location` before the call.
// Tracked by https://github.com/dotnet/roslyn/issues/36911
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
var comp = CreateCompilation(new[] { source, NotNullIfNotNullAttributeDefinition }, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(
// (20,13): warning CS8602: Dereference of a possibly null reference.
// (25,13): warning CS8602: Dereference of a possibly null reference.
// _ = result.ToString(); // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "result").WithLocation(20, 13));
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "result").WithLocation(25, 13));
}
[Fact]
......@@ -128198,12 +128700,13 @@ public void CompareExchangeT_UnrecognizedOverload()
var source = @"
#pragma warning disable 0436
using System.Threading;
using System.Diagnostics.CodeAnalysis;
namespace System.Threading
{
public static class Interlocked
{
public static T CompareExchange<T>(ref T location, T value) where T : class? { return location; }
public static T CompareExchange<T>(ref T location, T value, T comparand) where T : class? { return location; }
public static T CompareExchange<T>([NotNullIfNotNull(""value"")] ref T location, T value) where T : class? { return location; }
public static T CompareExchange<T>([NotNullIfNotNull(""value"")] ref T location, T value, T comparand) where T : class? { return location; }
}
}
......@@ -128213,17 +128716,26 @@ void M()
{
string? location = null;
Interlocked.CompareExchange(ref location, ""hello"");
_ = location.ToString(); // 1
_ = location.ToString();
location = null;
Interlocked.CompareExchange(ref location, ""hello"", null);
_ = location.ToString();
location = string.Empty;
Interlocked.CompareExchange(ref location, ""hello"", null); // 1
_ = location.ToString();
}
}
";
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
// We expect no warning at all, but in the last scenario the issue with `null` literals in method type inference becomes visible
// Tracked by https://github.com/dotnet/roslyn/issues/43536
var comp = CreateCompilation(new[] { source, NotNullIfNotNullAttributeDefinition }, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(
// (19,13): warning CS8602: Dereference of a possibly null reference.
// _ = location.ToString(); // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "location").WithLocation(19, 13));
// (27,60): warning CS8625: Cannot convert null literal to non-nullable reference type.
// Interlocked.CompareExchange(ref location, "hello", null); // 1
Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(27, 60)
);
}
[Fact]
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册