diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index b0f799d6ae6c3..607f7b90e2f1b 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -3581,7 +3581,7 @@ private BoundNode BindSimpleProgramCompilationUnit(CompilationUnitSyntax compila private BoundNode BindPrimaryConstructorBody(TypeDeclarationSyntax typeDecl, BindingDiagnosticBag diagnostics) { Debug.Assert(typeDecl.ParameterList is object); - Debug.Assert(typeDecl.Kind() is SyntaxKind.RecordDeclaration or SyntaxKind.ClassDeclaration); + Debug.Assert(typeDecl.Kind() is SyntaxKind.RecordDeclaration or SyntaxKind.ClassDeclaration or SyntaxKind.RecordStructDeclaration or SyntaxKind.StructDeclaration); BoundExpressionStatement initializer; ImmutableArray constructorLocals; diff --git a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs index 7940f2df63ec4..8c82999bbee73 100644 --- a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs +++ b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs @@ -1735,12 +1735,7 @@ private static void GetStateMachineSlotDebugInfo( initializersBody ??= GetSynthesizedEmptyBody(method); - if (method is SynthesizedPrimaryConstructor primaryCtor && method.ContainingType.IsStructType()) - { - body = BoundBlock.SynthesizedNoLocals(primaryCtor.GetSyntax()); - nullableInitialState = getInitializerState(body); - } - else if (method is SourceMemberMethodSymbol sourceMethod) + if (method is SourceMemberMethodSymbol sourceMethod) { CSharpSyntaxNode syntaxNode = sourceMethod.SyntaxNode; diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 16c7828f6f7af..7009b911ec2b3 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -639,8 +639,7 @@ void enforceMemberNotNull(SyntaxNode? syntaxOpt, LocalState state) Debug.Assert(thisParameter is object); thisSlot = GetOrCreateSlot(thisParameter); } - // https://github.com/dotnet/roslyn/issues/46718: give diagnostics on return points, not constructor signature - var exitLocation = method.DeclaringSyntaxReferences.IsEmpty ? null : method.TryGetFirstLocation(); + var exitLocation = method is SynthesizedPrimaryConstructor || method.DeclaringSyntaxReferences.IsEmpty ? null : method.TryGetFirstLocation(); bool constructorEnforcesRequiredMembers = method.ShouldCheckRequiredMembers(); // Required properties can be attributed MemberNotNull, indicating that if the property is set, the field will be set as well. diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/PrimaryConstructorTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/PrimaryConstructorTests.cs index 6405e7fa42eed..9f994c27677c1 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/PrimaryConstructorTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/PrimaryConstructorTests.cs @@ -22122,5 +22122,95 @@ public event EventHandler OnClosed var comp = CreateCompilation(source, options: TestOptions.ReleaseDll); comp.VerifyEmitDiagnostics(); } + + [Theory] + [InlineData("class")] + [InlineData("struct")] + [WorkItem("https://github.com/dotnet/roslyn/issues/74726")] + public void PrimaryCtorNullableFieldWarning(string typeKind) + { + var source = $$""" + #nullable enable + + public {{typeKind}} C() + { + public string Text { get; set; } // 1 + public C(bool ignored) : this() { } + } + + public {{typeKind}} C2 + { + public string Text { get; set; } + public C2() { } // 2 + } + + public {{typeKind}} C3() + { + public string Text { get; set; } = "a"; + public C3(bool ignored) : this() { } + } + + public {{typeKind}} C4 + { + public string Text { get; set; } + public C4() { Text = "a"; } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (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. + // public string Text { get; set; } // 1 + Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Text").WithArguments("property", "Text").WithLocation(5, 19), + // (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. + // public C2() { } // 2 + Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C2").WithArguments("property", "Text").WithLocation(12, 12) + ); + } + + [Theory] + [InlineData("class")] + [InlineData("struct")] + [WorkItem("https://github.com/dotnet/roslyn/issues/74726")] + public void RecordPrimaryCtorNullableFieldWarning(string typeKind) + { + var source = $$""" + #nullable enable + + public record {{typeKind}} C() + { + public string Text { get; set; } // 1 + public C(bool ignored) : this() { } + } + + public record {{typeKind}} C2 + { + public string Text { get; set; } + public C2() { } // 2 + } + + public record {{typeKind}} C3() + { + public string Text { get; set; } = "a"; + public C3(bool ignored) : this() { } + } + + public record {{typeKind}} C4 + { + public string Text { get; set; } + public C4() { Text = "a"; } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (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. + // public string Text { get; set; } // 1 + Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "Text").WithArguments("property", "Text").WithLocation(5, 19), + // (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. + // public C2() { } // 2 + Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C2").WithArguments("property", "Text").WithLocation(12, 12) + ); + } } }