Skip to content

Commit 1562f12

Browse files
authored
Report nullable ctor warnings for struct primary constructors (#74844)
1 parent 0d54341 commit 1562f12

File tree

4 files changed

+93
-9
lines changed

4 files changed

+93
-9
lines changed

src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3581,7 +3581,7 @@ private BoundNode BindSimpleProgramCompilationUnit(CompilationUnitSyntax compila
35813581
private BoundNode BindPrimaryConstructorBody(TypeDeclarationSyntax typeDecl, BindingDiagnosticBag diagnostics)
35823582
{
35833583
Debug.Assert(typeDecl.ParameterList is object);
3584-
Debug.Assert(typeDecl.Kind() is SyntaxKind.RecordDeclaration or SyntaxKind.ClassDeclaration);
3584+
Debug.Assert(typeDecl.Kind() is SyntaxKind.RecordDeclaration or SyntaxKind.ClassDeclaration or SyntaxKind.RecordStructDeclaration or SyntaxKind.StructDeclaration);
35853585

35863586
BoundExpressionStatement initializer;
35873587
ImmutableArray<LocalSymbol> constructorLocals;

src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs

+1-6
Original file line numberDiff line numberDiff line change
@@ -1735,12 +1735,7 @@ private static void GetStateMachineSlotDebugInfo(
17351735

17361736
initializersBody ??= GetSynthesizedEmptyBody(method);
17371737

1738-
if (method is SynthesizedPrimaryConstructor primaryCtor && method.ContainingType.IsStructType())
1739-
{
1740-
body = BoundBlock.SynthesizedNoLocals(primaryCtor.GetSyntax());
1741-
nullableInitialState = getInitializerState(body);
1742-
}
1743-
else if (method is SourceMemberMethodSymbol sourceMethod)
1738+
if (method is SourceMemberMethodSymbol sourceMethod)
17441739
{
17451740
CSharpSyntaxNode syntaxNode = sourceMethod.SyntaxNode;
17461741

src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -639,8 +639,7 @@ void enforceMemberNotNull(SyntaxNode? syntaxOpt, LocalState state)
639639
Debug.Assert(thisParameter is object);
640640
thisSlot = GetOrCreateSlot(thisParameter);
641641
}
642-
// https://github.com/dotnet/roslyn/issues/46718: give diagnostics on return points, not constructor signature
643-
var exitLocation = method.DeclaringSyntaxReferences.IsEmpty ? null : method.TryGetFirstLocation();
642+
var exitLocation = method is SynthesizedPrimaryConstructor || method.DeclaringSyntaxReferences.IsEmpty ? null : method.TryGetFirstLocation();
644643
bool constructorEnforcesRequiredMembers = method.ShouldCheckRequiredMembers();
645644

646645
// Required properties can be attributed MemberNotNull, indicating that if the property is set, the field will be set as well.

src/Compilers/CSharp/Test/Emit2/Semantics/PrimaryConstructorTests.cs

+90
Original file line numberDiff line numberDiff line change
@@ -22122,5 +22122,95 @@ public event EventHandler OnClosed
2212222122
var comp = CreateCompilation(source, options: TestOptions.ReleaseDll);
2212322123
comp.VerifyEmitDiagnostics();
2212422124
}
22125+
22126+
[Theory]
22127+
[InlineData("class")]
22128+
[InlineData("struct")]
22129+
[WorkItem("https://github.com/dotnet/roslyn/issues/74726")]
22130+
public void PrimaryCtorNullableFieldWarning(string typeKind)
22131+
{
22132+
var source = $$"""
22133+
#nullable enable
22134+
22135+
public {{typeKind}} C()
22136+
{
22137+
public string Text { get; set; } // 1
22138+
public C(bool ignored) : this() { }
22139+
}
22140+
22141+
public {{typeKind}} C2
22142+
{
22143+
public string Text { get; set; }
22144+
public C2() { } // 2
22145+
}
22146+
22147+
public {{typeKind}} C3()
22148+
{
22149+
public string Text { get; set; } = "a";
22150+
public C3(bool ignored) : this() { }
22151+
}
22152+
22153+
public {{typeKind}} C4
22154+
{
22155+
public string Text { get; set; }
22156+
public C4() { Text = "a"; }
22157+
}
22158+
""";
22159+
22160+
var comp = CreateCompilation(source);
22161+
comp.VerifyEmitDiagnostics(
22162+
// (5,19): warning CS8618: Non-nullable property 'Text' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.
22163+
// public string Text { get; set; } // 1
22164+
Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Text").WithArguments("property", "Text").WithLocation(5, 19),
22165+
// (12,12): warning CS8618: Non-nullable property 'Text' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.
22166+
// public C2() { } // 2
22167+
Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C2").WithArguments("property", "Text").WithLocation(12, 12)
22168+
);
22169+
}
22170+
22171+
[Theory]
22172+
[InlineData("class")]
22173+
[InlineData("struct")]
22174+
[WorkItem("https://github.com/dotnet/roslyn/issues/74726")]
22175+
public void RecordPrimaryCtorNullableFieldWarning(string typeKind)
22176+
{
22177+
var source = $$"""
22178+
#nullable enable
22179+
22180+
public record {{typeKind}} C()
22181+
{
22182+
public string Text { get; set; } // 1
22183+
public C(bool ignored) : this() { }
22184+
}
22185+
22186+
public record {{typeKind}} C2
22187+
{
22188+
public string Text { get; set; }
22189+
public C2() { } // 2
22190+
}
22191+
22192+
public record {{typeKind}} C3()
22193+
{
22194+
public string Text { get; set; } = "a";
22195+
public C3(bool ignored) : this() { }
22196+
}
22197+
22198+
public record {{typeKind}} C4
22199+
{
22200+
public string Text { get; set; }
22201+
public C4() { Text = "a"; }
22202+
}
22203+
""";
22204+
22205+
var comp = CreateCompilation(source);
22206+
comp.VerifyEmitDiagnostics(
22207+
// (5,19): warning CS8618: Non-nullable property 'Text' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.
22208+
// public string Text { get; set; } // 1
22209+
Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Text").WithArguments("property", "Text").WithLocation(5, 19),
22210+
// (12,12): warning CS8618: Non-nullable property 'Text' must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring the property as nullable.
22211+
// public C2() { } // 2
22212+
Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C2").WithArguments("property", "Text").WithLocation(12, 12)
22213+
);
22214+
}
2212522215
}
2212622216
}

0 commit comments

Comments
 (0)