未验证 提交 1a258ac1 编写于 作者: R Rastislav Novotný 提交者: GitHub

Support NotNullIfNotNull in user-defined binary operators (#48490)

上级 c4932a97
......@@ -3434,9 +3434,19 @@ private TypeWithState InferResultNullability(BinaryOperatorKind operatorKind, Me
{
if (methodOpt?.ParameterCount == 2)
{
return operatorKind.IsLifted() && !operatorKind.IsComparison()
? GetLiftedReturnType(methodOpt.ReturnTypeWithAnnotations, leftType.State.Join(rightType.State))
: GetReturnTypeWithState(methodOpt);
if (operatorKind.IsLifted() && !operatorKind.IsComparison())
{
return GetLiftedReturnType(methodOpt.ReturnTypeWithAnnotations, leftType.State.Join(rightType.State));
}
var resultTypeWithState = GetReturnTypeWithState(methodOpt);
if ((leftType.IsNotNull && methodOpt.ReturnNotNullIfParameterNotNull.Contains(methodOpt.Parameters[0].Name)) ||
(rightType.IsNotNull && methodOpt.ReturnNotNullIfParameterNotNull.Contains(methodOpt.Parameters[1].Name)))
{
resultTypeWithState = resultTypeWithState.WithNotNullState();
}
return resultTypeWithState;
}
}
else if (!operatorKind.IsDynamic() && !resultType.IsValueType)
......
......@@ -29114,6 +29114,246 @@ static void M(C c, string? p1, string? p2)
CompileAndVerify(comp2);
}
[Fact, WorkItem(48489, "https://github.com/dotnet/roslyn/issues/48489")]
public void NotNullIfNotNull_Return_BinaryOperator()
{
var lib_cs =
@"using System.Diagnostics.CodeAnalysis;
public class C
{
[return: NotNullIfNotNull(""x""), NotNullIfNotNull(""y"")] public static C? operator +(C? x, C? y) => null;
public static C? operator *(C? x, C? y) => null;
[return: NotNullIfNotNull(""x"")] public static C? operator -(C? x, C? y) => null;
[return: NotNullIfNotNull(""y"")] public static C? operator /(C? x, C? y) => null;
}";
var source = @"
class D
{
static void M(C c1, C c2, C? cn1, C? cn2)
{
(c1 + c2).ToString(); // no warn
(c1 + cn2).ToString(); // no warn
(cn1 + c2).ToString(); // no warn
(cn1 + cn2).ToString(); // warn
(c1 * c2).ToString(); // warn
(c1 * cn2).ToString(); // warn
(cn1 * c2).ToString(); // warn
(cn1 * cn2).ToString(); // warn
(c1 - c2).ToString(); // no warn
(c1 - cn2).ToString(); // no warn
(cn1 - c2).ToString(); // warn
(cn1 - cn2).ToString(); // warn
(c1 / c2).ToString(); // no warn
(c1 / cn2).ToString(); // warn
(cn1 / c2).ToString(); // no warn
(cn1 / cn2).ToString(); // warn
}
}";
var expected = new[]
{
// (9,10): warning CS8602: Dereference of a possibly null reference.
// (cn1 + cn2).ToString(); // warn
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "cn1 + cn2").WithLocation(9, 10),
// (11,10): warning CS8602: Dereference of a possibly null reference.
// (c1 * c2).ToString(); // warn
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c1 * c2").WithLocation(11, 10),
// (12,10): warning CS8602: Dereference of a possibly null reference.
// (c1 * cn2).ToString(); // warn
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c1 * cn2").WithLocation(12, 10),
// (13,10): warning CS8602: Dereference of a possibly null reference.
// (cn1 * c2).ToString(); // warn
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "cn1 * c2").WithLocation(13, 10),
// (14,10): warning CS8602: Dereference of a possibly null reference.
// (cn1 * cn2).ToString(); // warn
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "cn1 * cn2").WithLocation(14, 10),
// (18,10): warning CS8602: Dereference of a possibly null reference.
// (cn1 - c2).ToString(); // warn
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "cn1 - c2").WithLocation(18, 10),
// (19,10): warning CS8602: Dereference of a possibly null reference.
// (cn1 - cn2).ToString(); // warn
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "cn1 - cn2").WithLocation(19, 10),
// (22,10): warning CS8602: Dereference of a possibly null reference.
// (c1 / cn2).ToString(); // warn
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c1 / cn2").WithLocation(22, 10),
// (24,10): warning CS8602: Dereference of a possibly null reference.
// (cn1 / cn2).ToString(); // warn
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "cn1 / cn2").WithLocation(24, 10)
};
var lib = CreateNullableCompilation(new[] { NotNullIfNotNullAttributeDefinition, lib_cs });
var comp = CreateNullableCompilation(source, references: new[] { lib.EmitToImageReference() });
comp.VerifyDiagnostics(expected);
var comp2 = CreateNullableCompilation(new[] { source, lib_cs, NotNullIfNotNullAttributeDefinition });
comp2.VerifyDiagnostics(expected);
CompileAndVerify(comp2);
}
[Fact, WorkItem(48489, "https://github.com/dotnet/roslyn/issues/48489")]
public void NotNullIfNotNull_Return_BinaryOperatorWithStruct()
{
var lib_cs =
@"using System.Diagnostics.CodeAnalysis;
public struct S
{
public int Value;
}
public class C
{
[return: NotNullIfNotNull(""y"")] public static C? operator +(C? x, S? y) => null;
#pragma warning disable CS8825
[return: NotNullIfNotNull(""y"")] public static C? operator -(C? x, S y) => null;
#pragma warning restore CS8825
}";
var source = @"
class D
{
static void M(C c, C? cn, S s, S? sn)
{
(c + s).ToString(); // no warn
(cn + s).ToString(); // no warn
(c + sn).ToString(); // warn
(cn + sn).ToString(); // warn
(c - s).ToString(); // no warn
(cn - s).ToString(); // no warn
}
}";
var expected = new[]
{
// (8,10): warning CS8602: Dereference of a possibly null reference.
// (c + sn).ToString(); // warn
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c + sn").WithLocation(8, 10),
// (9,10): warning CS8602: Dereference of a possibly null reference.
// (cn + sn).ToString(); // warn
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "cn + sn").WithLocation(9, 10)
};
var lib = CreateNullableCompilation(new[] { NotNullIfNotNullAttributeDefinition, lib_cs });
var comp = CreateNullableCompilation(source, references: new[] { lib.EmitToImageReference() });
comp.VerifyDiagnostics(expected);
var comp2 = CreateNullableCompilation(new[] { source, lib_cs, NotNullIfNotNullAttributeDefinition });
comp2.VerifyDiagnostics(expected);
CompileAndVerify(comp2);
}
[Fact, WorkItem(48489, "https://github.com/dotnet/roslyn/issues/48489")]
public void NotNullIfNotNull_Return_BinaryOperatorInCompoundAssignment()
{
var lib_cs =
@"using System.Diagnostics.CodeAnalysis;
public class C
{
[return: NotNullIfNotNull(""x""), NotNullIfNotNull(""y"")] public static C? operator +(C? x, C? y) => null;
public static C? operator *(C? x, C? y) => null;
[return: NotNullIfNotNull(""x"")] public static C? operator -(C? x, C? y) => null;
[return: NotNullIfNotNull(""y"")] public static C? operator /(C? x, C? y) => null;
}";
var source = @"
class E
{
static void A(C ac, C? acn, C ar1, C ar2, C? arn1, C? arn2)
{
ar1 += ac;
ar1.ToString();
ar2 += acn;
ar2.ToString();
arn1 += ac;
arn1.ToString();
arn2 += acn;
arn2.ToString(); // warn reference
}
static void M(C mc, C? mcn, C mr1, C mr2, C? mrn1, C? mrn2)
{
mr1 *= mc; // warn assignment
mr1.ToString(); // warn reference
mr2 *= mcn; // warn assignment
mr2.ToString(); // warn reference
mrn1 *= mc;
mrn1.ToString(); // warn reference
mrn2 *= mcn;
mrn2.ToString(); // warn reference
}
static void S(C sc, C? scn, C sr1, C sr2, C? srn1, C? srn2)
{
sr1 -= sc;
sr1.ToString();
sr2 -= scn;
sr2.ToString();
srn1 -= sc;
srn1.ToString(); // warn reference
srn2 -= scn;
srn2.ToString(); // warn reference
}
static void D(C dc, C? dcn, C dr1, C dr2, C? drn1, C? drn2)
{
dr1 /= dc;
dr1.ToString();
dr2 /= dcn; // warn assignment
dr2.ToString(); // warn reference
drn1 /= dc;
drn1.ToString();
drn2 /= dcn;
drn2.ToString(); // warn reference
}
}";
var expected = new[]
{
// (13,9): warning CS8602: Dereference of a possibly null reference.
// arn2.ToString(); // warn reference
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "arn2").WithLocation(13, 9),
// (18,9): warning CS8601: Possible null reference assignment.
// mr1 *= mc; // warn assignment
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "mr1 *= mc").WithLocation(18, 9),
// (19,9): warning CS8602: Dereference of a possibly null reference.
// mr1.ToString(); // warn reference
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "mr1").WithLocation(19, 9),
// (20,9): warning CS8601: Possible null reference assignment.
// mr2 *= mcn; // warn assignment
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "mr2 *= mcn").WithLocation(20, 9),
// (21,9): warning CS8602: Dereference of a possibly null reference.
// mr2.ToString(); // warn reference
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "mr2").WithLocation(21, 9),
// (23,9): warning CS8602: Dereference of a possibly null reference.
// mrn1.ToString(); // warn reference
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "mrn1").WithLocation(23, 9),
// (25,9): warning CS8602: Dereference of a possibly null reference.
// mrn2.ToString(); // warn reference
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "mrn2").WithLocation(25, 9),
// (35,9): warning CS8602: Dereference of a possibly null reference.
// srn1.ToString(); // warn reference
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "srn1").WithLocation(35, 9),
// (37,9): warning CS8602: Dereference of a possibly null reference.
// srn2.ToString(); // warn reference
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "srn2").WithLocation(37, 9),
// (44,9): warning CS8601: Possible null reference assignment.
// dr2 *= dcn; // warn assignment
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "dr2 /= dcn").WithLocation(44, 9),
// (45,9): warning CS8602: Dereference of a possibly null reference.
// dr2.ToString(); // warn reference
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "dr2").WithLocation(45, 9),
// (49,9): warning CS8602: Dereference of a possibly null reference.
// drn2.ToString(); // warn reference
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "drn2").WithLocation(49, 9)
};
var lib = CreateNullableCompilation(new[] { NotNullIfNotNullAttributeDefinition, lib_cs });
var comp = CreateNullableCompilation(source, references: new[] { lib.EmitToImageReference() });
comp.VerifyDiagnostics(expected);
var comp2 = CreateNullableCompilation(new[] { source, lib_cs, NotNullIfNotNullAttributeDefinition });
comp2.VerifyDiagnostics(expected);
CompileAndVerify(comp2);
}
[Fact]
public void MethodWithOutNullableParameter_AfterNotNullWhenTrue()
{
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册