Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,10 @@ internal static SyntaxToken ExtractAnonymousTypeMemberName(this ExpressionSyntax

continue;

case SyntaxKind.SuppressNullableWarningExpression:
input = ((PostfixUnaryExpressionSyntax)input).Operand;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

input = ((PostfixUnaryExpressionSyntax)input).Operand;

It looks like this potentially can introduce a breaking change for an existing code. Let's add test(s) demonstrating that. And also let's document the breaking change. Once that is done, we can start an internal process for approving the breaking change.

continue;

default:
return default(SyntaxToken);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80646,9 +80646,9 @@ void M(C<string?> c)
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "t4.Item1").WithLocation(25, 9),

// line 6
// (34,52): warning CS8619: Nullability of reference types in value of type '(C<string?>, string)' doesn't match target type '(C<string>, string)'.
// (34,52): warning CS8619: Nullability of reference types in value of type '(C<string?> c, string)' doesn't match target type '(C<string>, string)'.
// (string, (C<string>, string)) t6 = (null!, (c!, null!)); // warn
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(c!, null!)").WithArguments("(C<string?>, string)", "(C<string>, string)").WithLocation(34, 52),
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(c!, null!)").WithArguments("(C<string?> c, string)", "(C<string>, string)").WithLocation(34, 52),

// line 8
// (44,51): warning CS8619: Nullability of reference types in value of type '(C<string?> c, string?)' doesn't match target type '(C<string>, string)'.
Expand Down Expand Up @@ -85853,7 +85853,7 @@ static void F(string? x, string? y)
{
(object a, long b) t = (x, 0)/*T:(string? x, int)*/; // 1
(object a, (long b, object c)) t2 = (x, (0, y)/*T:(int, string? y)*/)/*T:(string? x, (int, string? y))*/; // 2, 3
(object a, (long b, object c)) t3 = (x!, (0, y!)/*T:(int, string!)*/)/*T:(string!, (int, string!))*/;
(object a, (long b, object c)) t3 = (x!, (0, y!)/*T:(int, string! y)*/)/*T:(string! x, (int, string! y))*/;
(int b, string? c)? t4 = null;
(object? a, (long b, object? c)?) t5 = (x, t4)/*T:(string? x, (int b, string? c)? t4)*/;

Expand Down
62 changes: 62 additions & 0 deletions src/Compilers/CSharp/Test/Semantic/Semantics/ValueTupleTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -843,5 +843,67 @@ void M()
Assert.Equal("C", secondTupleArgInfo.Type.ToTestDisplayString());
Assert.Equal("C", secondTupleArgInfo.ConvertedType.ToTestDisplayString());
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80752")]
public void ElementNameInference_FromLocal_NullSuppression()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ElementNameInference_FromLocal_NullSuppression

It would be good to add a test with a motivating scenario for adding the suppression. I.e. a scenario that benefits from adding !.

{
var source = """
#nullable enable

class C
{
string M()
{
string? str = null;
C? c = null;
var t = (str!, c!);
return t.str ?? "";
}
}
""";

var comp = CreateCompilation(source);
comp.VerifyDiagnostics();

var tree = comp.SyntaxTrees.First();
var model = comp.GetSemanticModel(tree);

var tupleExpression = tree.GetCompilationUnitRoot().DescendantNodes().OfType<TupleExpressionSyntax>().Single();
var tupleTypeInfo = model.GetTypeInfo(tupleExpression);
Assert.Equal("(System.String str, C c)", tupleTypeInfo.Type.ToTestDisplayString());
Assert.Equal("(System.String str, C c)", tupleTypeInfo.ConvertedType.ToTestDisplayString());
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80752")]
public void ElementNameInference_FromPropertyName_NullSuppression()
{
var source = """
#nullable enable

class C
{
public string? Prop1 { get; set; }

public string? Prop2 { get; set; }

string M(C c)
{
var t = (c.Prop1!, c.Prop2!);
return t.Prop1 ?? "";
}
}
""";

var comp = CreateCompilation(source);
comp.VerifyDiagnostics();

var tree = comp.SyntaxTrees.First();
var model = comp.GetSemanticModel(tree);

var tupleExpression = tree.GetCompilationUnitRoot().DescendantNodes().OfType<TupleExpressionSyntax>().Single();
var tupleTypeInfo = model.GetTypeInfo(tupleExpression);
Assert.Equal("(System.String Prop1, System.String Prop2)", tupleTypeInfo.Type.ToTestDisplayString());
Assert.Equal("(System.String Prop1, System.String Prop2)", tupleTypeInfo.ConvertedType.ToTestDisplayString());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1927,5 +1927,79 @@ .maxstack 2

this.CompileAndVerify(compilation).VerifyIL("C.Main", expectedIL);
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80752")]
public void ElementNameInference_FromLocal_NullSuppression()
{
var source = """
#nullable enable

class C
{
string M()
{
string? str = null;
C? c = null;
var a = new { str!, c! };
return a.str ?? "";
}
}
""";

var comp = CreateCompilation(source);
comp.VerifyDiagnostics(
// (9,23): error CS0746: Invalid anonymous type member declarator. Anonymous type members must be declared with a member assignment, simple name or member access.
// var a = new { str!, c! };
Diagnostic(ErrorCode.ERR_InvalidAnonymousTypeMemberDeclarator, "str!").WithLocation(9, 23),
// (9,29): error CS0746: Invalid anonymous type member declarator. Anonymous type members must be declared with a member assignment, simple name or member access.
// var a = new { str!, c! };
Diagnostic(ErrorCode.ERR_InvalidAnonymousTypeMemberDeclarator, "c!").WithLocation(9, 29));

var tree = comp.SyntaxTrees.First();
var model = comp.GetSemanticModel(tree);

var tupleExpression = tree.GetCompilationUnitRoot().DescendantNodes().OfType<AnonymousObjectCreationExpressionSyntax>().Single();
var tupleTypeInfo = model.GetTypeInfo(tupleExpression);
Assert.Equal("<anonymous type: System.String str, C c>", tupleTypeInfo.Type.ToTestDisplayString());
Assert.Equal("<anonymous type: System.String str, C c>", tupleTypeInfo.ConvertedType.ToTestDisplayString());
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80752")]
public void ElementNameInference_FromPropertyName_NullSuppression()
{
var source = """
#nullable enable

class C
{
public string? Prop1 { get; set; }

public string? Prop2 { get; set; }

string M(C c)
{
var a = new { c.Prop1!, c.Prop2! };
return a.Prop1 ?? "";
}
}
""";

var comp = CreateCompilation(source);
comp.VerifyDiagnostics(
// (11,23): error CS0746: Invalid anonymous type member declarator. Anonymous type members must be declared with a member assignment, simple name or member access.
// var a = new { c.Prop1!, c.Prop2! };
Diagnostic(ErrorCode.ERR_InvalidAnonymousTypeMemberDeclarator, "c.Prop1!").WithLocation(11, 23),
// (11,33): error CS0746: Invalid anonymous type member declarator. Anonymous type members must be declared with a member assignment, simple name or member access.
// var a = new { c.Prop1!, c.Prop2! };
Diagnostic(ErrorCode.ERR_InvalidAnonymousTypeMemberDeclarator, "c.Prop2!").WithLocation(11, 33));

var tree = comp.SyntaxTrees.First();
var model = comp.GetSemanticModel(tree);

var tupleExpression = tree.GetCompilationUnitRoot().DescendantNodes().OfType<AnonymousObjectCreationExpressionSyntax>().Single();
var tupleTypeInfo = model.GetTypeInfo(tupleExpression);
Assert.Equal("<anonymous type: System.String Prop1, System.String Prop2>", tupleTypeInfo.Type.ToTestDisplayString());
Assert.Equal("<anonymous type: System.String Prop1, System.String Prop2>", tupleTypeInfo.ConvertedType.ToTestDisplayString());
}
}
}