From 18c645ccd46f29ab26e65ab1a905d68a6440ccdb Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Thu, 10 Dec 2020 13:01:52 -0800 Subject: [PATCH] Fix constructor exit warnings for generic NotNull (#49841) --- .../Portable/FlowAnalysis/NullableWalker.cs | 6 +- .../Semantics/NullableReferenceTypesTests.cs | 82 +++++++++++++++++++ 2 files changed, 86 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 4b3b0e92f59ef..e7e422e9dd5c4 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -644,8 +644,10 @@ void checkMemberStateOnConstructorExit(MethodSymbol constructor, Symbol member, } var memberState = state[slot]; - var badState = fieldType.Type.IsPossiblyNullableReferenceTypeTypeParameter() ? NullableFlowState.MaybeDefault : NullableFlowState.MaybeNull; - if (memberState == badState) + var badState = fieldType.Type.IsPossiblyNullableReferenceTypeTypeParameter() && (annotations & FlowAnalysisAnnotations.NotNull) == 0 + ? NullableFlowState.MaybeDefault + : NullableFlowState.MaybeNull; + if (memberState >= badState) // is 'memberState' as bad as or worse than 'badState'? { Diagnostics.Add(ErrorCode.WRN_UninitializedNonNullableField, exitLocation ?? symbol.Locations.FirstOrNone(), symbol.Kind.Localize(), symbol.Name); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index de14d902216c2..91cb565eb6c71 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -28846,6 +28846,88 @@ public C([NotNull] out string? pOut) Diagnostic(ErrorCode.WRN_ParameterDisallowsNull, "}").WithArguments("pOut").WithLocation(8, 5)); } + [Theory, WorkItem(48996, "https://github.com/dotnet/roslyn/issues/48996")] + [InlineData("")] + [InlineData("where T : class?")] + public void NotNullProperty_Constructor(string constraints) + { + var source = @" +#nullable enable +using System.Diagnostics.CodeAnalysis; + +class C " + constraints + @" +{ + [NotNull] public T Prop { get; set; } + + public C() { } // 1 + public C(T t) { Prop = t; } // 2 + public C(T t, int x) { Prop = t; Prop.ToString(); } // 3 + public C([DisallowNull] T t, int x, int y) { Prop = t; } + + [MemberNotNull(nameof(Prop))] + public void Init(T t) + { + Prop = t; + } // 4 +} +"; + var comp = CreateNullableCompilation(new[] { source, NotNullAttributeDefinition, MemberNotNullAttributeDefinition, DisallowNullAttributeDefinition }); + comp.VerifyDiagnostics( + // (9,12): warning CS8618: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // public C() { } // 1 + Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C").WithArguments("property", "Prop").WithLocation(9, 12), + // (10,12): warning CS8618: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. + // public C(T t) { Prop = t; } // 2 + Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C").WithArguments("property", "Prop").WithLocation(10, 12), + // (11,38): warning CS8602: Dereference of a possibly null reference. + // public C(T t, int x) { Prop = t; Prop.ToString(); } // 3 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "Prop").WithLocation(11, 38), + // (18,5): warning CS8774: Member 'Prop' must have a non-null value when exiting. + // } // 4 + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("Prop").WithLocation(18, 5)); + } + + [Theory, WorkItem(48996, "https://github.com/dotnet/roslyn/issues/48996")] + [InlineData("")] + [InlineData("where T : class?")] + public void NotNullField_Constructor(string constraints) + { + var source = @" +#nullable enable +using System.Diagnostics.CodeAnalysis; + +class C " + constraints + @" +{ + [NotNull] public T field; + + public C() { } // 1 + public C(T t) { field = t; } // 2 + public C(T t, int x) { field = t; field.ToString(); } // 3 + public C([DisallowNull] T t, int x, int y) { field = t; } + + [MemberNotNull(nameof(field))] + public void Init(T t) + { + field = t; + } // 4 +} +"; + var comp = CreateNullableCompilation(new[] { source, NotNullAttributeDefinition, MemberNotNullAttributeDefinition, DisallowNullAttributeDefinition }); + comp.VerifyDiagnostics( + // (9,12): warning CS8618: Non-nullable field 'field' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // public C() { } // 1 + Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C").WithArguments("field", "field").WithLocation(9, 12), + // (10,12): warning CS8618: Non-nullable field 'field' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. + // public C(T t) { field = t; } // 2 + Diagnostic(ErrorCode.WRN_UninitializedNonNullableField, "C").WithArguments("field", "field").WithLocation(10, 12), + // (11,39): warning CS8602: Dereference of a possibly null reference. + // public C(T t, int x) { field = t; field.ToString(); } // 3 + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "field").WithLocation(11, 39), + // (18,5): warning CS8774: Member 'field' must have a non-null value when exiting. + // } // 4 + Diagnostic(ErrorCode.WRN_MemberNotNull, "}").WithArguments("field").WithLocation(18, 5)); + } + [Fact] public void NotNullIfNotNull_Return_NullableInt() {