未验证 提交 06f9495f 编写于 作者: R Rikki Gibson 提交者: GitHub

Fix nullable suppression in conditional access (#47712)

上级 b8cd43a6
......@@ -10580,6 +10580,18 @@ internal ExpressionSyntax ParseConsequenceSyntax()
while (true)
{
tk = this.CurrentToken.Kind;
// Nullable suppression operators should only be consumed by a conditional access
// if there are further conditional operations performed after the suppression
if (isOptionalExclamationsFollowedByConditionalOperation())
{
while (tk == SyntaxKind.ExclamationToken)
{
expr = _syntaxFactory.PostfixUnaryExpression(SyntaxKind.SuppressNullableWarningExpression, expr, EatToken());
expr = CheckFeatureAvailability(expr, MessageID.IDS_FeatureNullableReferenceTypes);
tk = this.CurrentToken.Kind;
}
}
switch (tk)
{
case SyntaxKind.OpenParenToken:
......@@ -10607,6 +10619,20 @@ internal ExpressionSyntax ParseConsequenceSyntax()
return expr;
}
}
bool isOptionalExclamationsFollowedByConditionalOperation()
{
var tk = this.CurrentToken.Kind;
for (int i = 1; tk == SyntaxKind.ExclamationToken; i++)
{
tk = this.PeekToken(i).Kind;
}
return tk
is SyntaxKind.OpenParenToken
or SyntaxKind.OpenBracketToken
or SyntaxKind.DotToken
or SyntaxKind.QuestionToken;
}
}
internal ArgumentListSyntax ParseParenthesizedArgumentList()
......
......@@ -7893,15 +7893,73 @@ public void SuppressionOnNullableReferenceType_AppliedOnField2()
public class C
{
public string? field;
void M(C? c)
void M1(C? c)
{
c?.field!.ToString();
c.ToString(); // We learned from suppressed dereference that `c` isn't null
c.ToString(); // 1
}
void M2(C? c)
{
c!?.field!.ToString();
c.ToString(); // 2
}
void M3(C? c)
{
_ = c?.field!.ToString()!;
c.ToString(); // 3
}
void M4(C? c)
{
(c?.field!.ToString()!).ToString();
c.ToString(); // no warning because 'c' was part of a call receiver in previous line
}
}
";
var c = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue());
c.VerifyDiagnostics();
c.VerifyDiagnostics(
// (8,9): warning CS8602: Dereference of a possibly null reference.
// c.ToString(); // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c", isSuppressed: false).WithLocation(8, 9),
// (14,9): warning CS8602: Dereference of a possibly null reference.
// c.ToString(); // 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c", isSuppressed: false).WithLocation(14, 9),
// (20,9): warning CS8602: Dereference of a possibly null reference.
// c.ToString(); // 3
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c", isSuppressed: false).WithLocation(20, 9));
}
[Fact]
[WorkItem(43659, "https://github.com/dotnet/roslyn/issues/43659")]
public void SuppressionOnNullableReferenceType_AppliedOnField3()
{
var source = @"
public class C
{
public string[]? F1;
public System.Func<object>? F2;
static void M1(C? c)
{
c?.F1![0].ToString();
c.ToString(); // 1
}
static void M2(C? c)
{
c?.F2!().ToString();
c.ToString(); // 2
}
}
";
var c = CreateNullableCompilation(source);
c.VerifyDiagnostics(
// (9,9): warning CS8602: Dereference of a possibly null reference.
// c.ToString(); // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c", isSuppressed: false).WithLocation(9, 9),
// (14,9): warning CS8602: Dereference of a possibly null reference.
// c.ToString(); // 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c", isSuppressed: false).WithLocation(14, 9));
}
[Fact]
......@@ -63580,6 +63638,142 @@ static void G(string s)
);
}
[Fact]
[WorkItem(43659, "https://github.com/dotnet/roslyn/issues/43659")]
public void SuppressNullableWarning_ConditionalAccess_01()
{
var source = @"
#nullable enable
using System;
using System.Linq;
class Program
{
static void Main()
{
int[]? a = null;
string? s1 = a?.First().ToString();
Console.Write(s1 == null);
string? s2 = a?.First()!.ToString();
Console.Write(s2 == null);
string s3 = a?.First().ToString()!;
Console.Write(s3 == null);
string? s4 = (a?.First()).ToString();
Console.Write(s4 == null);
string? s5 = (a?.First())!.ToString();
Console.Write(s5 == null);
}
}";
var comp = CreateCompilation(source, options: TestOptions.DebugExe);
comp.VerifyDiagnostics();
CompileAndVerify(comp, expectedOutput: "TrueTrueTrueFalseFalse");
}
[Fact]
[WorkItem(43659, "https://github.com/dotnet/roslyn/issues/43659")]
public void SuppressNullableWarning_ConditionalAccess_02()
{
var source = @"
#nullable enable
using System.Linq;
class Program
{
static void Main()
{
int[]? a = null;
string? s1 = a?.First()!!.ToString(); // 1
string? s2 = a?.First()!!!!.ToString(); // 2
}
}";
var comp = CreateCompilation(source, options: TestOptions.DebugExe);
comp.VerifyDiagnostics(
// (12,24): error CS8715: Duplicate null suppression operator ('!')
// string? s1 = a?.First()!!.ToString(); // 1
Diagnostic(ErrorCode.ERR_DuplicateNullSuppression, ".First()", isSuppressed: false).WithLocation(12, 24),
// (13,24): error CS8715: Duplicate null suppression operator ('!')
// string? s2 = a?.First()!!!!.ToString(); // 2
Diagnostic(ErrorCode.ERR_DuplicateNullSuppression, ".First()", isSuppressed: false).WithLocation(13, 24),
// (13,24): error CS8715: Duplicate null suppression operator ('!')
// string? s2 = a?.First()!!!!.ToString(); // 2
Diagnostic(ErrorCode.ERR_DuplicateNullSuppression, ".First()", isSuppressed: false).WithLocation(13, 24),
// (13,24): error CS8715: Duplicate null suppression operator ('!')
// string? s2 = a?.First()!!!!.ToString(); // 2
Diagnostic(ErrorCode.ERR_DuplicateNullSuppression, ".First()", isSuppressed: false).WithLocation(13, 24));
}
[Fact]
[WorkItem(43659, "https://github.com/dotnet/roslyn/issues/43659")]
public void SuppressNullableWarning_ConditionalAccess_03()
{
var source = @"
#nullable enable
using System;
public class D { }
public class C { public D d = null!; }
public class B { public C? c; }
public class A { public B? b; }
class Program
{
static void Main()
{
M(new A());
}
static void M(A a)
{
var str = a.b?.c!.d.ToString();
Console.Write(str == null);
}
}";
var comp = CreateCompilation(source, options: TestOptions.DebugExe);
comp.VerifyDiagnostics();
CompileAndVerify(comp, expectedOutput: "True");
}
[Fact]
[WorkItem(43659, "https://github.com/dotnet/roslyn/issues/43659")]
public void SuppressNullableWarning_ConditionalAccess_04()
{
var source = @"
#nullable enable
public class D { }
public class C { public D? d; }
public class B { public C? c; }
public class A { public B? b; }
class Program
{
static void M(A a)
{
string str1 = a.b?.c!.d.ToString(); // 1, 2
string str2 = a.b?.c!.d!.ToString(); // 3
string str3 = a.b?.c!.d!.ToString()!;
}
}";
var comp = CreateCompilation(source);
comp.VerifyDiagnostics(
// (13,23): warning CS8600: Converting null literal or possible null value to non-nullable type.
// string str1 = a.b?.c!.d.ToString(); // 1, 2
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "a.b?.c!.d.ToString()", isSuppressed: false).WithLocation(13, 23),
// (13,27): warning CS8602: Dereference of a possibly null reference.
// string str1 = a.b?.c!.d.ToString(); // 1, 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, ".c!.d", isSuppressed: false).WithLocation(13, 27),
// (14,23): warning CS8600: Converting null literal or possible null value to non-nullable type.
// string str2 = a.b?.c!.d!.ToString(); // 3
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "a.b?.c!.d!.ToString()", isSuppressed: false).WithLocation(14, 23));
}
[Fact]
public void SuppressNullableWarning_Nested()
{
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册