diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index f478332e9a592..640aeb4b90aca 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -6009,14 +6009,46 @@ private FlowAnalysisAnnotations GetLValueAnnotations(BoundExpression expr) { BoundPropertyAccess property => getSetterAnnotations(property.PropertySymbol), BoundIndexerAccess indexer => getSetterAnnotations(indexer.Indexer), - BoundFieldAccess field => field.FieldSymbol.FlowAnalysisAnnotations, + BoundFieldAccess field => getFieldAnnotations(field.FieldSymbol), _ => FlowAnalysisAnnotations.None }; return annotations & (FlowAnalysisAnnotations.DisallowNull | FlowAnalysisAnnotations.AllowNull); + static FlowAnalysisAnnotations getFieldAnnotations(FieldSymbol field) + { + return field.AssociatedSymbol is PropertySymbol property ? + getSetterAnnotations(property) : + field.FlowAnalysisAnnotations; + } + static FlowAnalysisAnnotations getSetterAnnotations(PropertySymbol property) - => property.GetOwnOrInheritedSetMethod()?.Parameters.Last()?.FlowAnalysisAnnotations ?? FlowAnalysisAnnotations.None; + { + var accessor = property.GetOwnOrInheritedSetMethod(); + if (accessor is object) + { + return accessor.Parameters.Last().FlowAnalysisAnnotations; + } + if (property is SourcePropertySymbol sourceProperty) + { + return getPropertyAnnotations(sourceProperty); + } + return FlowAnalysisAnnotations.None; + } + + static FlowAnalysisAnnotations getPropertyAnnotations(SourcePropertySymbol property) + { + var annotations = FlowAnalysisAnnotations.None; + if (property.HasAllowNull) + { + annotations |= FlowAnalysisAnnotations.AllowNull; + } + if (property.HasDisallowNull) + { + annotations |= FlowAnalysisAnnotations.DisallowNull; + } + return annotations; + } } private static bool UseLegacyWarnings(BoundExpression expr, TypeWithAnnotations exprType) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index face5e1353c82..992912bcebffb 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -28059,15 +28059,8 @@ void M(C c) } void Deconstruct(out C? x, out C? y) => throw null!; }"; - // Note: we warn on the property initializer because it is a direct assignment to the backing field - // so the value is not getting normalized if the normalization is handled by the property setter - var comp = CreateNullableCompilation(new[] { source, AllowNullAttributeDefinition }); - comp.VerifyDiagnostics( - // (4,44): warning CS8625: Cannot convert null literal to non-nullable reference type. - // [AllowNull] public C P { get; set; } = null; - Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(4, 44) - ); + comp.VerifyDiagnostics(); } [Fact] @@ -29302,8 +29295,8 @@ static void M5(T? t5) where T : struct new CStruct().P5 = t5; } }"; - var expected = new[] - { + var comp = CreateNullableCompilation(source, references: new[] { lib.EmitToImageReference() }); + comp.VerifyDiagnostics( // (13,14): warning CS8600: Converting null literal or possible null value to non-nullable type. // t2 = null; // 1 Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithLocation(13, 14), @@ -29330,14 +29323,43 @@ static void M5(T? t5) where T : struct Diagnostic(ErrorCode.WRN_DisallowNullAttributeForbidsMaybeNullAssignment, "t5").WithLocation(35, 30), // (36,31): warning CS8607: A possible null value may not be assigned to a target marked with the [DisallowNull] attribute // new CStruct().P5 = t5; // 9 - Diagnostic(ErrorCode.WRN_DisallowNullAttributeForbidsMaybeNullAssignment, "t5").WithLocation(36, 31) - }; - - var comp = CreateNullableCompilation(source, references: new[] { lib.EmitToImageReference() }); - comp.VerifyDiagnostics(expected); + Diagnostic(ErrorCode.WRN_DisallowNullAttributeForbidsMaybeNullAssignment, "t5").WithLocation(36, 31)); var comp2 = CreateNullableCompilation(new[] { source, lib_cs, DisallowNullAttributeDefinition }); - comp2.VerifyDiagnostics(expected); + comp2.VerifyDiagnostics( + // (9,53): warning CS8625: Cannot convert null literal to non-nullable reference type. + // [DisallowNull]public TClass? P3 { get; set; } = null; + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(9, 53), + // (13,14): warning CS8600: Converting null literal or possible null value to non-nullable type. + // t2 = null; // 1 + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithLocation(13, 14), + // (14,5): warning CS8607: A possible null value may not be assigned to a target marked with the [DisallowNull] attribute + // [DisallowNull]public TStruct? P5 { get; set; } + Diagnostic(ErrorCode.WRN_DisallowNullAttributeForbidsMaybeNullAssignment, "[DisallowNull]public TStruct? P5 { get; set; }").WithLocation(14, 5), + // (14,29): warning CS8601: Possible null reference assignment. + // new COpen().P1 = t2; // 2 + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "t2").WithLocation(14, 29), + // (15,30): warning CS8601: Possible null reference assignment. + // new CClass().P2 = t2; // 3 + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "t2").WithLocation(15, 30), + // (16,30): warning CS8601: Possible null reference assignment. + // new CClass().P3 = t2; // 4, [DisallowNull] honored on TClass? + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "t2").WithLocation(16, 30), + // (20,29): warning CS8601: Possible null reference assignment. + // new COpen().P1 = t3; // 5 + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "t3").WithLocation(20, 29), + // (21,30): warning CS8601: Possible null reference assignment. + // new CClass().P2 = t3; // 6 + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "t3").WithLocation(21, 30), + // (22,30): warning CS8601: Possible null reference assignment. + // new CClass().P3 = t3; // 7, [DisallowNull] honored on TClass? + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "t3").WithLocation(22, 30), + // (35,30): warning CS8607: A possible null value may not be assigned to a target marked with the [DisallowNull] attribute + // new COpen().P1 = t5; // 8 + Diagnostic(ErrorCode.WRN_DisallowNullAttributeForbidsMaybeNullAssignment, "t5").WithLocation(35, 30), + // (36,31): warning CS8607: A possible null value may not be assigned to a target marked with the [DisallowNull] attribute + // new CStruct().P5 = t5; // 9 + Diagnostic(ErrorCode.WRN_DisallowNullAttributeForbidsMaybeNullAssignment, "t5").WithLocation(36, 31)); CompileAndVerify(comp2, sourceSymbolValidator: symbolValidator, symbolValidator: symbolValidator); @@ -29391,6 +29413,7 @@ void symbolValidator(ModuleSymbol m) } [Fact] + [WorkItem(39926, "https://github.com/dotnet/roslyn/issues/39926")] public void DisallowNull_Property_NullInitializer() { var source = @@ -29400,10 +29423,11 @@ public class C [DisallowNull]public string? P1 { get; set; } = null; } "; - - // Missing warning. Honor nullability attributes in method bodies https://github.com/dotnet/roslyn/issues/36039 var comp = CreateNullableCompilation(new[] { DisallowNullAttributeDefinition, source }); - comp.VerifyDiagnostics(); + comp.VerifyDiagnostics( + // (4,53): warning CS8625: Cannot convert null literal to non-nullable reference type. + // [DisallowNull]public string? P1 { get; set; } = null; + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(4, 53)); } [Fact] @@ -29432,6 +29456,12 @@ void M() var comp = CreateNullableCompilation(new[] { DisallowNullAttributeDefinition, source }); comp.VerifyDiagnostics( + // (4,5): warning CS8607: A possible null value may not be assigned to a target marked with the [DisallowNull] attribute + // [DisallowNull]public int? P1 { get; set; } = null; + Diagnostic(ErrorCode.WRN_DisallowNullAttributeForbidsMaybeNullAssignment, "[DisallowNull]public int? P1 { get; set; } = null;").WithLocation(4, 5), + // (4,50): warning CS8607: A possible null value may not be assigned to a target marked with the [DisallowNull] attribute + // [DisallowNull]public int? P1 { get; set; } = null; + Diagnostic(ErrorCode.WRN_DisallowNullAttributeForbidsMaybeNullAssignment, "null").WithLocation(4, 50), // (7,19): warning CS8607: A possible null value may not be assigned to a target marked with the [DisallowNull] attribute // (P1, _) = (null, 1); // 1 Diagnostic(ErrorCode.WRN_DisallowNullAttributeForbidsMaybeNullAssignment, "(null, 1)").WithLocation(7, 19), @@ -119197,5 +119227,110 @@ static T Get() // static T F6() => true ? default : Get(); Diagnostic(ErrorCode.WRN_NullReferenceReturn, "true ? default : Get()").WithLocation(13, 25)); } + + [Fact] + [WorkItem(39926, "https://github.com/dotnet/roslyn/issues/39926")] + public void MaybeNullT_24() + { + var source = +@"#nullable enable +using System.Diagnostics.CodeAnalysis; +class C +{ + [MaybeNull]T P1 { get; } = default; // 1 + [AllowNull]T P2 { get; } = default; + [MaybeNull, AllowNull]T P3 { get; } = default; + [MaybeNull]T P4 { get; set; } = default; // 2 + [AllowNull]T P5 { get; set; } = default; + [MaybeNull, AllowNull]T P6 { get; set; } = default; + C([AllowNull]T t) + { + P1 = t; // 3 + P2 = t; + P3 = t; + P4 = t; // 4 + P5 = t; + P6 = t; + } +}"; + var comp = CreateCompilation(new[] { source, AllowNullAttributeDefinition, MaybeNullAttributeDefinition }); + comp.VerifyDiagnostics( + // (5,32): warning CS8601: Possible null reference assignment. + // [MaybeNull]T P1 { get; } = default; // 1 + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "default").WithLocation(5, 32), + // (8,37): warning CS8601: Possible null reference assignment. + // [MaybeNull]T P4 { get; set; } = default; // 2 + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "default").WithLocation(8, 37), + // (13,14): warning CS8601: Possible null reference assignment. + // P1 = t; // 3 + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "t").WithLocation(13, 14), + // (16,14): warning CS8601: Possible null reference assignment. + // P4 = t; // 4 + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "t").WithLocation(16, 14)); + } + + [Fact] + public void MaybeNullT_25() + { + var source = +@"#nullable enable +using System.Diagnostics.CodeAnalysis; +class C +{ + [NotNull]T P1 { get; } = default; // 1 + [DisallowNull]T P2 { get; } = default; // 2 + [NotNull, DisallowNull]T P3 { get; } = default; // 3 + [NotNull]T P4 { get; set; } = default; // 4 + [DisallowNull]T P5 { get; set; } = default; // 5 + [NotNull, DisallowNull]T P6 { get; set; } = default; // 6 + C([AllowNull]T t) + { + P1 = t; // 7 + P2 = t; // 8 + P3 = t; // 9 + P4 = t; // 10 + P5 = t; // 11 + P6 = t; // 12 + } +}"; + var comp = CreateCompilation(new[] { source, AllowNullAttributeDefinition, DisallowNullAttributeDefinition, NotNullAttributeDefinition }); + comp.VerifyDiagnostics( + // (5,30): warning CS8601: Possible null reference assignment. + // [NotNull]T P1 { get; } = default; // 1 + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "default").WithLocation(5, 30), + // (6,35): warning CS8601: Possible null reference assignment. + // [DisallowNull]T P2 { get; } = default; // 2 + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "default").WithLocation(6, 35), + // (7,44): warning CS8601: Possible null reference assignment. + // [NotNull, DisallowNull]T P3 { get; } = default; // 3 + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "default").WithLocation(7, 44), + // (8,35): warning CS8601: Possible null reference assignment. + // [NotNull]T P4 { get; set; } = default; // 4 + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "default").WithLocation(8, 35), + // (9,40): warning CS8601: Possible null reference assignment. + // [DisallowNull]T P5 { get; set; } = default; // 5 + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "default").WithLocation(9, 40), + // (10,49): warning CS8601: Possible null reference assignment. + // [NotNull, DisallowNull]T P6 { get; set; } = default; // 6 + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "default").WithLocation(10, 49), + // (13,14): warning CS8601: Possible null reference assignment. + // P1 = t; // 7 + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "t").WithLocation(13, 14), + // (14,14): warning CS8601: Possible null reference assignment. + // P2 = t; // 8 + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "t").WithLocation(14, 14), + // (15,14): warning CS8601: Possible null reference assignment. + // P3 = t; // 9 + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "t").WithLocation(15, 14), + // (16,14): warning CS8601: Possible null reference assignment. + // P4 = t; // 10 + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "t").WithLocation(16, 14), + // (17,14): warning CS8601: Possible null reference assignment. + // P5 = t; // 11 + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "t").WithLocation(17, 14), + // (18,14): warning CS8601: Possible null reference assignment. + // P6 = t; // 12 + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "t").WithLocation(18, 14)); + } } }