diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs index 8269db7e44210f68f1aa8ce84fd61a0aba4509ef..3b4ff85a1fa18c7b5266d92d86e343af510a4964 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs @@ -1177,6 +1177,11 @@ protected virtual void AssignImpl(BoundNode node, BoundExpression value, bool is ((BoundTupleExpression)node).VisitAllElements((x, self) => self.Assign(x, value: null, isRef: isRef), this); break; + case BoundKind.SuppressNullableWarningExpression: + // for example, assigning to `x!` in `M(out x!)` assigns to `x` + AssignImpl(((BoundSuppressNullableWarningExpression)node).Expression, value, isRef, written, read); + break; + default: // Other kinds of left-hand-sides either represent things not tracked (e.g. array elements) // or errors that have been reported earlier (e.g. assignment to a unary increment) @@ -1871,7 +1876,6 @@ protected void CheckAssigned(BoundExpression expr, SyntaxNode node) case BoundKind.BaseReference: CheckAssigned(MethodThisParameter, node); break; - //CheckAssigned(expr, } } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 7046dc9ff8766ce7f020c9652e3cbebc2d9b02c5..bbff66d6756710675658635a8be76c135448e08e 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -343,6 +343,9 @@ private static object GetTypeAsDiagnosticArgument(TypeSymbol typeOpt) return typeOpt ?? (object)""; } + /// + /// Reports top-level nullability problem in assignment. + /// private bool ReportNullReferenceAssignmentIfNecessary(BoundExpression value, TypeSymbolWithAnnotations targetType, TypeSymbolWithAnnotations valueType, bool useLegacyWarnings) { Debug.Assert(value != null); @@ -1427,6 +1430,7 @@ TypeSymbol getLeftResultType(TypeSymbol leftType, TypeSymbol rightType) { return rightType; } + GenerateConversionForConditionalOperator(node.RightOperand, rightType, leftType, reportMismatch: true); return leftType; } @@ -1971,34 +1975,49 @@ private void VisitArgumentEvaluate(ImmutableArray arguments, Im } break; case RefKind.Out: - if (argument is BoundLocal local && local.DeclarationKind == BoundLocalDeclarationKind.WithInferredType) { - _variableTypes[local.LocalSymbol] = parameterType; - resultType = parameterType; - } - if (!ReportNullReferenceAssignmentIfNecessary(argument, resultType, parameterType, useLegacyWarnings: UseLegacyWarnings(argument))) - { - HashSet useSiteDiagnostics = null; - if (!_conversions.HasIdentityOrImplicitReferenceConversion(parameterType.TypeSymbol, argumentType, ref useSiteDiagnostics)) + bool reportedWarning = false; + if (argument is BoundLocal local && local.DeclarationKind == BoundLocalDeclarationKind.WithInferredType) { - ReportNullabilityMismatchInArgument(argument, argumentType, parameter, parameterType.TypeSymbol); + _variableTypes[local.LocalSymbol] = parameterType; + resultType = parameterType; + } + if (argument.Kind != BoundKind.SuppressNullableWarningExpression) + { + reportedWarning = ReportNullReferenceAssignmentIfNecessary(argument, resultType, parameterType, useLegacyWarnings: UseLegacyWarnings(argument)); + } + if (!reportedWarning) + { + HashSet useSiteDiagnostics = null; + if (!_conversions.HasIdentityOrImplicitReferenceConversion(parameterType.TypeSymbol, argumentType, ref useSiteDiagnostics)) + { + ReportNullabilityMismatchInArgument(argument, argumentType, parameter, parameterType.TypeSymbol); + } } + // Set nullable state of argument to parameter type. + TrackNullableStateForAssignment(argument, resultType, result.Slot, parameterType); + break; } - // Set nullable state of argument to parameter type. - TrackNullableStateForAssignment(argument, resultType, result.Slot, parameterType); - break; case RefKind.Ref: - if (!ReportNullReferenceArgumentIfNecessary(argument, resultType, parameter, parameterType) && - !ReportNullReferenceAssignmentIfNecessary(argument, resultType, parameterType, useLegacyWarnings: UseLegacyWarnings(argument))) { - if ((object)argumentType != null && IsNullabilityMismatch(argumentType, parameterType.TypeSymbol)) + bool reportedWarning = false; + if (argument.Kind != BoundKind.SuppressNullableWarningExpression) { - ReportNullabilityMismatchInArgument(argument, argumentType, parameter, parameterType.TypeSymbol); + reportedWarning = ReportNullReferenceArgumentIfNecessary(argument, resultType, parameter, parameterType) || + ReportNullReferenceAssignmentIfNecessary(argument, resultType, parameterType, useLegacyWarnings: UseLegacyWarnings(argument)); } + if (!reportedWarning) + { + if ((object)argumentType != null && + IsNullabilityMismatch(argumentType, parameterType.TypeSymbol)) + { + ReportNullabilityMismatchInArgument(argument, argumentType, parameter, parameterType.TypeSymbol); + } + } + // Set nullable state of argument to parameter type. + TrackNullableStateForAssignment(argument, resultType, result.Slot, parameterType); + break; } - // Set nullable state of argument to parameter type. - TrackNullableStateForAssignment(argument, resultType, result.Slot, parameterType); - break; default: throw ExceptionUtilities.UnexpectedValue(refKind); } @@ -3414,14 +3433,14 @@ public override BoundNode VisitAsOperator(BoundAsOperator node) public override BoundNode VisitSuppressNullableWarningExpression(BoundSuppressNullableWarningExpression node) { - var result = base.VisitSuppressNullableWarningExpression(node); + base.VisitSuppressNullableWarningExpression(node); //if (this.State.Reachable) // PROTOTYPE(NullableReferenceTypes): Consider reachability? { - _result = _result.Type?.SetUnknownNullabilityForReferenceTypes(); + _result = _result.Type?.WithTopLevelNonNullability(); } - return result; + return null; } public override BoundNode VisitSizeOfOperator(BoundSizeOfOperator node) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs index 48767115ed5e1f4279f416b6d6f15c8e3a7e5f8d..0e0b7ef121ab5a8190303411dc37e7f4d75ed352 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/PreciseAbstractFlowPass.cs @@ -551,6 +551,10 @@ protected void VisitLvalue(BoundExpression node) ((BoundTupleExpression)node).VisitAllElements((x, self) => self.VisitLvalue(x), this); break; + case BoundKind.SuppressNullableWarningExpression: + VisitLvalue(((BoundSuppressNullableWarningExpression)node).Expression); + break; + default: VisitRvalue(node); break; diff --git a/src/Compilers/CSharp/Portable/Symbols/SymbolWithAnnotations.cs b/src/Compilers/CSharp/Portable/Symbols/SymbolWithAnnotations.cs index 5edb022b9be7cda54a162c6901b826834c0a4902..670811c9180ffe80dc75ce1a321d764d4fa4f751 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SymbolWithAnnotations.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SymbolWithAnnotations.cs @@ -580,6 +580,17 @@ public TypeSymbolWithAnnotations SetUnknownNullabilityForReferenceTypesIfNecessa this.SetUnknownNullabilityForReferenceTypes(); } + public TypeSymbolWithAnnotations WithTopLevelNonNullability() + { + var typeSymbol = TypeSymbol; + if (IsNullable == false || !typeSymbol.IsReferenceType) + { + return this; + } + + return new NonLazyType(typeSymbol, isNullable: false, CustomModifiers); + } + public TypeSymbolWithAnnotations SetUnknownNullabilityForReferenceTypes() { var typeSymbol = TypeSymbol; diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/StaticNullChecking.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/StaticNullChecking.cs index d7eb932e5c270216c398fd307723c9b0cce466fa..d84130c34ef867d665ca942cd5bfba7bff6ed7be 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/StaticNullChecking.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/StaticNullChecking.cs @@ -6012,14 +6012,14 @@ public class C public void Main(string? key) { Copy(key!, out var s); - s/*T:string*/.ToString(); + s/*T:string!*/.ToString(); } public static void Copy(T key, out T value) => throw null; } ", parseOptions: TestOptions.Regular8); VerifyOutVar(c, "string!"); - c.VerifyTypes(); // PROTOTOYPE(NullableReferenceTypes): Should this be string! + c.VerifyTypes(); c.VerifyDiagnostics(); } @@ -6032,14 +6032,14 @@ public class C public void Main(string? key) { var s = Copy(key!); - s/*T:string*/.ToString(); + s/*T:string!*/.ToString(); } public static T Copy(T key) => throw null; } ", parseOptions: TestOptions.Regular8); VerifyVarLocal(c, "string!"); - c.VerifyTypes(); // PROTOTOYPE(NullableReferenceTypes): Should this be string! + c.VerifyTypes(); c.VerifyDiagnostics(); } @@ -6557,6 +6557,7 @@ public void Main(string s) } ", parseOptions: TestOptions.Regular8); + // PROTOTYPE(NullableReferenceTypes): The suppression operator isn't producing a non-null result // PROTOTYPE(NullableReferenceTypes): I'd expect null! to return a non-null result VerifyVarLocal(c, "string!"); c.VerifyTypes(); @@ -6578,12 +6579,141 @@ public void Main(string s) } ", parseOptions: TestOptions.Regular8); + // PROTOTYPE(NullableReferenceTypes): The suppression operator isn't producing a non-null result // PROTOTYPE(NullableReferenceTypes): I'd expect null! to return a non-null result VerifyVarLocal(c, "string!"); c.VerifyTypes(); c.VerifyDiagnostics(); } + [Fact] + public void SuppressedObliviousValueGivesNonNullResult() + { + var libComp = CreateCompilation(@" +public static class Static +{ + public static string Oblivious = null; +} +", parseOptions: TestOptions.Regular7); + + var comp = CreateCompilation(@" +public class C +{ + public void Main(string s, string? ns) + { + s = Static.Oblivious!; + var s2 = s; + s2/*T:string!*/.ToString(); // ok + ns = Static.Oblivious!; + ns.ToString(); // ok + } +} +", references: new[] { libComp.EmitToImageReference() }, parseOptions: TestOptions.Regular8); + + VerifyVarLocal(comp, "string!"); + comp.VerifyTypes(); + comp.VerifyDiagnostics(); + } + + [Fact] + public void SuppressedValueGivesNonNullResult() + { + var comp = CreateCompilation(@" +public class C +{ + public void Main(string? ns, bool b) + { + var x1 = F(ns!); + x1 /*T:string!*/ .ToString(); + + var listNS = List.Create(ns); + listNS /*T:List*/ .ToString(); + var x2 = F2(listNS); + x2 /*T:string!*/ .ToString(); + } + public T F(T? x) where T : class => throw null; + public T F2(List x) where T : class => throw null; +} +public class List { public static List Create(T t) => throw null; } +public class List { } +", parseOptions: TestOptions.Regular8); + + comp.VerifyTypes(); + comp.VerifyDiagnostics(); + } + + [Fact] + public void NestedNullabilityMismatchIgnoresSuppression() + { + var obliviousComp = CreateCompilation(@" +public static class Static +{ + public static string Oblivious = null; +} +", parseOptions: TestOptions.Regular7); + + var comp = CreateCompilation(@" +public class C +{ + public void Main(string s, string? ns) + { + var o = Static.Oblivious; + + { + var listS = List.Create(s); + var listNS = List.Create(ns); + listS /*T:List*/ .ToString(); + listNS /*T:List*/ .ToString(); + listS = listNS!; // warn 1 + } + + { + var listS = List.Create(s); + var listO = List.Create(o); + listO /*T:List*/ .ToString(); + listS = listO; // ok + } + + { + var listNS = List.Create(ns); + var listS = List.Create(s); + listNS = listS!; // warn 2 + } + + { + var listNS = List.Create(ns); + var listO = List.Create(o); + listNS = listO; // ok + } + + { + var listO = List.Create(o); + var listNS = List.Create(ns); + listO = listNS; // ok + } + + { + var listO = List.Create(o); + var listS = List.Create(s); + listO = listS; // ok + } + } +} +public class List { public static List Create(T t) => throw null; } +public class List { } +", references: new[] { obliviousComp.EmitToImageReference() }, parseOptions: TestOptions.Regular8); + + comp.VerifyTypes(); + comp.VerifyDiagnostics( + // (13,21): warning CS8619: Nullability of reference types in value of type 'List' doesn't match target type 'List'. + // listS = listNS!; // warn 1 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "listNS!").WithArguments("List", "List").WithLocation(13, 21), + // (26,22): warning CS8619: Nullability of reference types in value of type 'List' doesn't match target type 'List'. + // listNS = listS!; // warn 2 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "listS!").WithArguments("List", "List").WithLocation(26, 22) + ); + } + [Fact] public void AssignNull() { @@ -10525,6 +10655,7 @@ struct S1 } ", parseOptions: TestOptions.Regular8); + // PROTOTYPE(NullableReferenceTypes): Why isn't u2 = v2 causing a warning? c.VerifyDiagnostics( // (10,14): hidden CS8607: Expression is probably never null. // x1 = y1.p1 ?? x1; @@ -19537,7 +19668,20 @@ static class E var comp = CreateCompilationWithMscorlib45( source, parseOptions: TestOptions.Regular8); - comp.VerifyDiagnostics(); + comp.VerifyDiagnostics( + // (6,17): warning CS8619: Nullability of reference types in value of type 'object?[]' doesn't match target type 'object[]'. + // other = o!; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "o!").WithArguments("object?[]", "object[]").WithLocation(6, 17), + // (8,9): warning CS8620: Nullability of reference types in argument of type 'object?[]' doesn't match target type 'object[]' for parameter 'o' in 'void E.F(object[] o)'. + // o!.F(); + Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "o!").WithArguments("object?[]", "object[]", "o", "void E.F(object[] o)").WithLocation(8, 9), + // (9,11): warning CS8620: Nullability of reference types in argument of type 'object?[]' doesn't match target type 'object[]' for parameter 'o' in 'void C.G(object[] o)'. + // G(o!); + Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "o!").WithArguments("object?[]", "object[]", "o", "void C.G(object[] o)").WithLocation(9, 11), + // (10,16): warning CS8619: Nullability of reference types in value of type 'object?[]' doesn't match target type 'object[]'. + // return o!; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "o!").WithArguments("object?[]", "object[]").WithLocation(10, 16) + ); } [Fact] @@ -19549,11 +19693,11 @@ public void SuppressNullableWarning_ConstructedType() static C F(C o) { C other; - other = o!; - o = other!; + other = o!; // 1 + o = other!; // 2 o!.F(); - G(o!); - return o!; + G(o!); // 3 + return o!; // 4 } static void G(C o) { } } @@ -19564,7 +19708,20 @@ class C var comp = CreateCompilation( source, parseOptions: TestOptions.Regular8); - comp.VerifyDiagnostics(); + comp.VerifyDiagnostics( + // (6,17): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'C'. + // other = o!; // 1 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "o!").WithArguments("C", "C").WithLocation(6, 17), + // (7,13): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'C'. + // o = other!; // 2 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "other!").WithArguments("C", "C").WithLocation(7, 13), + // (9,11): warning CS8620: Nullability of reference types in argument of type 'C' doesn't match target type 'C' for parameter 'o' in 'void C.G(C o)'. + // G(o!); // 3 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "o!").WithArguments("C", "C", "o", "void C.G(C o)").WithLocation(9, 11), + // (10,16): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'C'. + // return o!; // 4 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "o!").WithArguments("C", "C").WithLocation(10, 16) + ); } // PROTOTYPE(NullableReferenceTypes): Binder should report an error for `!!`. @@ -19627,16 +19784,16 @@ class C static void F(C? x, C y, bool c) { C a; - a = c ? x : y; - a = c ? y : x; - a = c ? x : y!; - a = c ? x! : y; - a = c ? x! : y!; + a = c ? x : y; // 1 and 2 + a = c ? y : x; // 3 and 4 + a = c ? x : y!; // 5 and 6 + a = c ? x! : y; // 7 + a = c ? x! : y!; // 8 C b; - b = c ? x : y; - b = c ? x : y!; - b = c ? x! : y; - b = c ? x! : y!; + b = c ? x : y; // 9 and 10 + b = c ? x : y!; // 11 and 12 + b = c ? x! : y; // 13 + b = c ? x! : y!; // 14 } }"; var comp = CreateCompilation( @@ -19644,32 +19801,294 @@ static void F(C? x, C y, bool c) parseOptions: TestOptions.Regular8); comp.VerifyDiagnostics( // (7,13): warning CS8626: No best nullability for operands of conditional expression 'C' and 'C'. - // a = c ? x : y; + // a = c ? x : y; // 1 and 2 Diagnostic(ErrorCode.WRN_NoBestNullabilityConditionalExpression, "c ? x : y").WithArguments("C", "C").WithLocation(7, 13), // (7,13): warning CS8600: Converting null literal or possible null value to non-nullable type. - // a = c ? x : y; + // a = c ? x : y; // 1 and 2 Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "c ? x : y").WithLocation(7, 13), // (8,13): warning CS8626: No best nullability for operands of conditional expression 'C' and 'C'. - // a = c ? y : x; + // a = c ? y : x; // 3 and 4 Diagnostic(ErrorCode.WRN_NoBestNullabilityConditionalExpression, "c ? y : x").WithArguments("C", "C").WithLocation(8, 13), // (8,13): warning CS8600: Converting null literal or possible null value to non-nullable type. - // a = c ? y : x; + // a = c ? y : x; // 3 and 4 Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "c ? y : x").WithLocation(8, 13), + // (9,13): warning CS8626: No best nullability for operands of conditional expression 'C' and 'C'. + // a = c ? x : y!; // 5 and 6 + Diagnostic(ErrorCode.WRN_NoBestNullabilityConditionalExpression, "c ? x : y!").WithArguments("C", "C").WithLocation(9, 13), // (9,13): warning CS8600: Converting null literal or possible null value to non-nullable type. - // a = c ? x : y!; + // a = c ? x : y!; // 5 and 6 Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "c ? x : y!").WithLocation(9, 13), + // (10,13): warning CS8626: No best nullability for operands of conditional expression 'C' and 'C'. + // a = c ? x! : y; // 7 + Diagnostic(ErrorCode.WRN_NoBestNullabilityConditionalExpression, "c ? x! : y").WithArguments("C", "C").WithLocation(10, 13), + // (11,13): warning CS8626: No best nullability for operands of conditional expression 'C' and 'C'. + // a = c ? x! : y!; // 8 + Diagnostic(ErrorCode.WRN_NoBestNullabilityConditionalExpression, "c ? x! : y!").WithArguments("C", "C").WithLocation(11, 13), // (13,13): warning CS8626: No best nullability for operands of conditional expression 'C' and 'C'. - // b = c ? x : y; + // b = c ? x : y; // 9 and 10 Diagnostic(ErrorCode.WRN_NoBestNullabilityConditionalExpression, "c ? x : y").WithArguments("C", "C").WithLocation(13, 13), // (13,13): warning CS8600: Converting null literal or possible null value to non-nullable type. - // b = c ? x : y; + // b = c ? x : y; // 9 and 10 Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "c ? x : y").WithLocation(13, 13), - // (14,13): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'C'. - // b = c ? x : y!; - Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "c ? x : y!").WithArguments("C", "C").WithLocation(14, 13), + // (14,13): warning CS8626: No best nullability for operands of conditional expression 'C' and 'C'. + // b = c ? x : y!; // 11 and 12 + Diagnostic(ErrorCode.WRN_NoBestNullabilityConditionalExpression, "c ? x : y!").WithArguments("C", "C").WithLocation(14, 13), // (14,13): warning CS8600: Converting null literal or possible null value to non-nullable type. - // b = c ? x : y!; - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "c ? x : y!").WithLocation(14, 13)); + // b = c ? x : y!; // 11 and 12 + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "c ? x : y!").WithLocation(14, 13), + // (15,13): warning CS8626: No best nullability for operands of conditional expression 'C' and 'C'. + // b = c ? x! : y; // 13 + Diagnostic(ErrorCode.WRN_NoBestNullabilityConditionalExpression, "c ? x! : y").WithArguments("C", "C").WithLocation(15, 13), + // (16,13): warning CS8626: No best nullability for operands of conditional expression 'C' and 'C'. + // b = c ? x! : y!; // 14 + Diagnostic(ErrorCode.WRN_NoBestNullabilityConditionalExpression, "c ? x! : y!").WithArguments("C", "C").WithLocation(16, 13) + ); + } + + [Fact] + public void SuppressNullableWarning_NullCoalescing() + { + var source = +@" +class C +{ + static void F(C? x, C y) + { + var t1 = x ?? y; // 1 + var t2 = y ?? x; // 2 and 3 + var t3 = x! ?? y; // 4 and 5 + var t4 = y! ?? x; // 6 and 7 + var t5 = x ?? y!; // 8 + var t6 = y ?? x!; // 9 and 10 + var t7 = x! ?? y!; // 11 and 12 + var t8 = y! ?? x!; // 13 and 14 + } +}"; + var comp = CreateCompilation( + source, + parseOptions: TestOptions.Regular8); + comp.VerifyDiagnostics( + // (6,23): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'C'. + // var t1 = x ?? y; // 1 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "y").WithArguments("C", "C").WithLocation(6, 23), + // (7,18): hidden CS8607: Expression is probably never null. + // var t2 = y ?? x; // 2 and 3 + Diagnostic(ErrorCode.HDN_ExpressionIsProbablyNeverNull, "y").WithLocation(7, 18), + // (7,23): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'C'. + // var t2 = y ?? x; // 2 and 3 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x").WithArguments("C", "C").WithLocation(7, 23), + // (8,18): hidden CS8607: Expression is probably never null. + // var t3 = x! ?? y; // 4 and 5 + Diagnostic(ErrorCode.HDN_ExpressionIsProbablyNeverNull, "x!").WithLocation(8, 18), + // (8,24): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'C'. + // var t3 = x! ?? y; // 4 and 5 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "y").WithArguments("C", "C").WithLocation(8, 24), + // (9,18): hidden CS8607: Expression is probably never null. + // var t4 = y! ?? x; // 6 and 7 + Diagnostic(ErrorCode.HDN_ExpressionIsProbablyNeverNull, "y!").WithLocation(9, 18), + // (9,24): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'C'. + // var t4 = y! ?? x; // 6 and 7 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x").WithArguments("C", "C").WithLocation(9, 24), + // (10,23): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'C'. + // var t5 = x ?? y!; // 8 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "y!").WithArguments("C", "C").WithLocation(10, 23), + // (11,18): hidden CS8607: Expression is probably never null. + // var t6 = y ?? x!; // 9 and 10 + Diagnostic(ErrorCode.HDN_ExpressionIsProbablyNeverNull, "y").WithLocation(11, 18), + // (11,23): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'C'. + // var t6 = y ?? x!; // 9 and 10 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x!").WithArguments("C", "C").WithLocation(11, 23), + // (12,18): hidden CS8607: Expression is probably never null. + // var t7 = x! ?? y!; // 11 and 12 + Diagnostic(ErrorCode.HDN_ExpressionIsProbablyNeverNull, "x!").WithLocation(12, 18), + // (12,24): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'C'. + // var t7 = x! ?? y!; // 11 and 12 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "y!").WithArguments("C", "C").WithLocation(12, 24), + // (13,18): hidden CS8607: Expression is probably never null. + // var t8 = y! ?? x!; // 13 and 14 + Diagnostic(ErrorCode.HDN_ExpressionIsProbablyNeverNull, "y!").WithLocation(13, 18), + // (13,24): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'C'. + // var t8 = y! ?? x!; // 13 and 14 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x!").WithArguments("C", "C").WithLocation(13, 24) + ); + } + + [Fact] + public void SuppressNullableWarning_ArrayInitializer() + { + var source = +@" +class C +{ + static void F(C? x, C y) + { + var a1 = new[] { x, y }; // 1 + var a2 = new[] { x!, y }; // 2 + var a3 = new[] { x, y! }; // 3 + var a4 = new[] { x!, y! }; + } +}"; + // PROTOTYPE(NullableReferenceTypes): There should be a ErrorCode.WRN_NoBestNullabilityArrayElements warning on (2) + // since InferBestType fails. + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8); + comp.VerifyDiagnostics( + // (6,29): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'C'. + // var a1 = new[] { x, y }; // 1 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "y").WithArguments("C", "C").WithLocation(6, 29), + // (8,29): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'C'. + // var a3 = new[] { x, y! }; // 3 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "y!").WithArguments("C", "C").WithLocation(8, 29) + ); + } + + [Fact] + public void SuppressNullableWarning_LocalDeclaration() + { + var source = +@" +class C +{ + static void F(C? x, C y) + { + C? c1 = y; // 1 + C c2 = x; // 2 and 3 + C? c3 = y!; // 4 + C c4 = x!; // 5 + } +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8); + comp.VerifyDiagnostics( + // (6,25): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'C'. + // C? c1 = y; // 1 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "y").WithArguments("C", "C").WithLocation(6, 25), + // (7,25): warning CS8600: Converting null literal or possible null value to non-nullable type. + // C c2 = x; // 2 and 3 + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "x").WithLocation(7, 25), + // (7,25): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'C'. + // C c2 = x; // 2 and 3 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x").WithArguments("C", "C").WithLocation(7, 25), + // (8,25): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'C'. + // C? c3 = y!; // 4 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "y!").WithArguments("C", "C").WithLocation(8, 25), + // (9,25): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'C'. + // C c4 = x!; // 5 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x!").WithArguments("C", "C").WithLocation(9, 25) + ); + } + + [Fact] + public void SuppressNullableWarning_Cast() + { + var source = +@" +class C +{ + static void F(C? x, C y) + { + var c1 = (C?)y; + var c2 = (C)x; // warn + var c3 = (C?)y!; + var c4 = (C)x!; + } +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8); + // PROTOTYPE(NullableReferenceTypes): Should there be a warning on c1 too? + comp.VerifyDiagnostics( + // (7,18): warning CS8600: Converting null literal or possible null value to non-nullable type. + // var c2 = (C)x; // warn + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(C)x").WithLocation(7, 18) + ); + } + + [Fact] + public void SuppressNullableWarning_ObjectInitializer() + { + var source = +@" +class C +{ + public C? X = null!; + public C Y = null!; + static void F(C? x, C y) + { + _ = new C() { X = y, Y = x }; + _ = new C() { X = y!, Y = x! }; + } +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8); + comp.VerifyDiagnostics( + // (8,32): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'C'. + // _ = new C() { X = y, Y = x }; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "y").WithArguments("C", "C").WithLocation(8, 32), + // (8,39): warning CS8601: Possible null reference assignment. + // _ = new C() { X = y, Y = x }; + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "x").WithLocation(8, 39), + // (8,39): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'C'. + // _ = new C() { X = y, Y = x }; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x").WithArguments("C", "C").WithLocation(8, 39), + // (9,32): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'C'. + // _ = new C() { X = y!, Y = x! }; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "y!").WithArguments("C", "C").WithLocation(9, 32), + // (9,40): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'C'. + // _ = new C() { X = y!, Y = x! }; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x!").WithArguments("C", "C").WithLocation(9, 40) + ); + } + + [Fact] + public void SuppressNullableWarning_CollectionInitializer() + { + var source = +@" +using System.Collections; +class C : IEnumerable +{ + IEnumerator IEnumerable.GetEnumerator() => throw null; + void Add(C key, params C[] value) => throw null; + static void F(C? x) + { + _ = new C() { x, x }; // warn 1 and 2 + _ = new C() { x!, x! }; // warn 3 and 4 + } +} +class D : IEnumerable +{ + IEnumerator IEnumerable.GetEnumerator() => throw null; + void Add(D? key, params D?[] value) => throw null; + static void F(D y) + { + _ = new D() { y, y }; // warn 5 and 6 + _ = new D() { y!, y! }; // warn 7 and 8 + } +}"; + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8); + comp.VerifyDiagnostics( + // (19,28): warning CS8620: Nullability of reference types in argument of type 'D' doesn't match target type 'D' for parameter 'key' in 'void D.Add(D? key, params D?[] value)'. + // _ = new D() { y, y }; // warn 5 and 6 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "y").WithArguments("D", "D", "key", "void D.Add(D? key, params D?[] value)").WithLocation(19, 28), + // (19,32): warning CS8620: Nullability of reference types in argument of type 'D' doesn't match target type 'D' for parameter 'key' in 'void D.Add(D? key, params D?[] value)'. + // _ = new D() { y, y }; // warn 5 and 6 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "y").WithArguments("D", "D", "key", "void D.Add(D? key, params D?[] value)").WithLocation(19, 32), + // (20,28): warning CS8620: Nullability of reference types in argument of type 'D' doesn't match target type 'D' for parameter 'key' in 'void D.Add(D? key, params D?[] value)'. + // _ = new D() { y!, y! }; // warn 7 and 8 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "y!").WithArguments("D", "D", "key", "void D.Add(D? key, params D?[] value)").WithLocation(20, 28), + // (20,33): warning CS8620: Nullability of reference types in argument of type 'D' doesn't match target type 'D' for parameter 'key' in 'void D.Add(D? key, params D?[] value)'. + // _ = new D() { y!, y! }; // warn 7 and 8 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "y!").WithArguments("D", "D", "key", "void D.Add(D? key, params D?[] value)").WithLocation(20, 33), + // (9,28): warning CS8604: Possible null reference argument for parameter 'key' in 'void C.Add(C key, params C[] value)'. + // _ = new C() { x, x }; // warn 1 and 2 + Diagnostic(ErrorCode.WRN_NullReferenceArgument, "x").WithArguments("key", "void C.Add(C key, params C[] value)").WithLocation(9, 28), + // (9,31): warning CS8604: Possible null reference argument for parameter 'key' in 'void C.Add(C key, params C[] value)'. + // _ = new C() { x, x }; // warn 1 and 2 + Diagnostic(ErrorCode.WRN_NullReferenceArgument, "x").WithArguments("key", "void C.Add(C key, params C[] value)").WithLocation(9, 31), + // (10,28): warning CS8620: Nullability of reference types in argument of type 'C' doesn't match target type 'C' for parameter 'key' in 'void C.Add(C key, params C[] value)'. + // _ = new C() { x!, x! }; // warn 3 and 4 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "x!").WithArguments("C", "C", "key", "void C.Add(C key, params C[] value)").WithLocation(10, 28), + // (10,32): warning CS8620: Nullability of reference types in argument of type 'C' doesn't match target type 'C' for parameter 'key' in 'void C.Add(C key, params C[] value)'. + // _ = new C() { x!, x! }; // warn 3 and 4 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "x!").WithArguments("C", "C", "key", "void C.Add(C key, params C[] value)").WithLocation(10, 32) + ); } [Fact] @@ -19682,23 +20101,31 @@ class C static void F(C x, C y) { C a; - a = x; - a = x!; + a = x; // 1 + a = x!; // 2 C b; - b = y; - b = y!; + b = y; // 3 + b = y!; // 4 } }"; var comp = CreateCompilation( source, parseOptions: TestOptions.Regular8); comp.VerifyDiagnostics( + // (7,13): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'C'. - // a = x; + // a = x; // 1 Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x").WithArguments("C", "C").WithLocation(7, 13), + // (8,13): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'C'. + // a = x!; // 2 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x!").WithArguments("C", "C").WithLocation(8, 13), // (10,13): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'C'. - // b = y; - Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "y").WithArguments("C", "C").WithLocation(10, 13)); + // b = y; // 3 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "y").WithArguments("C", "C").WithLocation(10, 13), + // (11,13): warning CS8619: Nullability of reference types in value of type 'C' doesn't match target type 'C'. + // b = y!; // 4 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "y!").WithArguments("C", "C").WithLocation(11, 13) + ); } // PROTOTYPE(NullableReferenceTypes): Should report WRN_NullabilityMismatch*. @@ -19810,11 +20237,11 @@ class C static void F(C x, C y) { I a; - a = (I)x; - a = ((I)x)!; + a = (I)x; // 1 + a = ((I)x)!; // 2 I b; - b = (I)y; - b = ((I)y)!; + b = (I)y; // 3 + b = ((I)y)!; // 4 } }"; var comp = CreateCompilation( @@ -19822,11 +20249,18 @@ static void F(C x, C y) parseOptions: TestOptions.Regular8); comp.VerifyDiagnostics( // (8,13): warning CS8619: Nullability of reference types in value of type 'I' doesn't match target type 'I'. - // a = (I)x; + // a = (I)x; // 1 Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(I)x").WithArguments("I", "I").WithLocation(8, 13), + // (9,13): warning CS8619: Nullability of reference types in value of type 'I' doesn't match target type 'I'. + // a = ((I)x)!; // 2 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "((I)x)!").WithArguments("I", "I").WithLocation(9, 13), // (11,13): warning CS8619: Nullability of reference types in value of type 'I' doesn't match target type 'I'. - // b = (I)y; - Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(I)y").WithArguments("I", "I").WithLocation(11, 13)); + // b = (I)y; // 3 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "(I)y").WithArguments("I", "I").WithLocation(11, 13), + // (12,13): warning CS8619: Nullability of reference types in value of type 'I' doesn't match target type 'I'. + // b = ((I)y)!; // 4 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "((I)y)!").WithArguments("I", "I").WithLocation(12, 13) + ); } [Fact] @@ -19859,6 +20293,163 @@ static void Main() Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "t").WithLocation(10, 22)); } + [Fact] + public void SuppressNullableWarning_Ref_WithNestedDifferences() + { + var source = +@" +class List { } +class C +{ + static void F(ref List a, ref List? b, ref List c, ref List? d) + { + } + static void F1(ref List a, ref List? b, ref List c, ref List? d) + { + F(ref b, ref c, ref d, ref a); // warn 1, 2, 3 and 4 + } + static void F2(ref List a, ref List? b, ref List c, ref List? d) + { + F(ref b!, ref c!, ref d!, ref a!); // warn 5 and 6 + } +}"; + var comp = CreateCompilation( + source, + parseOptions: TestOptions.Regular8); + comp.VerifyDiagnostics( + // (10,15): warning CS8604: Possible null reference argument for parameter 'a' in 'void C.F(ref List a, ref List? b, ref List c, ref List? d)'. + // F(ref b, ref c, ref d, ref a); // warn 1, 2, 3 and 4 + Diagnostic(ErrorCode.WRN_NullReferenceArgument, "b").WithArguments("a", "void C.F(ref List a, ref List? b, ref List c, ref List? d)").WithLocation(10, 15), + // (10,22): warning CS8600: Converting null literal or possible null value to non-nullable type. + // F(ref b, ref c, ref d, ref a); // warn 1, 2, 3 and 4 + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "c").WithLocation(10, 22), + // (10,29): warning CS8604: Possible null reference argument for parameter 'c' in 'void C.F(ref List a, ref List? b, ref List c, ref List? d)'. + // F(ref b, ref c, ref d, ref a); // warn 1, 2, 3 and 4 + Diagnostic(ErrorCode.WRN_NullReferenceArgument, "d").WithArguments("c", "void C.F(ref List a, ref List? b, ref List c, ref List? d)").WithLocation(10, 29), + // (10,36): warning CS8600: Converting null literal or possible null value to non-nullable type. + // F(ref b, ref c, ref d, ref a); // warn 1, 2, 3 and 4 + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "a").WithLocation(10, 36), + // (14,23): warning CS8620: Nullability of reference types in argument of type 'List' doesn't match target type 'List' for parameter 'b' in 'void C.F(ref List a, ref List? b, ref List c, ref List? d)'. + // F(ref b!, ref c!, ref d!, ref a!); // warn 5 and 6 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "c!").WithArguments("List", "List", "b", "void C.F(ref List a, ref List? b, ref List c, ref List? d)").WithLocation(14, 23), + // (14,39): warning CS8620: Nullability of reference types in argument of type 'List' doesn't match target type 'List' for parameter 'd' in 'void C.F(ref List a, ref List? b, ref List c, ref List? d)'. + // F(ref b!, ref c!, ref d!, ref a!); // warn 5 and 6 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "a!").WithArguments("List", "List", "d", "void C.F(ref List a, ref List? b, ref List c, ref List? d)").WithLocation(14, 39) + ); + } + + [Fact] + public void SuppressNullableWarning_Ref_WithUnassignedLocals() + { + var source = +@" +class List { } +class C +{ + static void F(ref List a, ref List? b, ref List c, ref List? d) + { + throw null; + } + static void F1() + { + List a; + List? b; + List c; + List? d; + F(ref b, ref c, ref d, ref a); + } + static void F2() + { + List a; + List? b; + List c; + List? d; + F(ref b!, ref c!, ref d!, ref a!); + } +}"; + var comp = CreateCompilation( + source, + parseOptions: TestOptions.Regular8); + comp.VerifyDiagnostics( + // (15,15): error CS0165: Use of unassigned local variable 'b' + // F(ref b, ref c, ref d, ref a); + Diagnostic(ErrorCode.ERR_UseDefViolation, "b").WithArguments("b").WithLocation(15, 15), + // (15,22): error CS0165: Use of unassigned local variable 'c' + // F(ref b, ref c, ref d, ref a); + Diagnostic(ErrorCode.ERR_UseDefViolation, "c").WithArguments("c").WithLocation(15, 22), + // (15,29): error CS0165: Use of unassigned local variable 'd' + // F(ref b, ref c, ref d, ref a); + Diagnostic(ErrorCode.ERR_UseDefViolation, "d").WithArguments("d").WithLocation(15, 29), + // (15,36): error CS0165: Use of unassigned local variable 'a' + // F(ref b, ref c, ref d, ref a); + Diagnostic(ErrorCode.ERR_UseDefViolation, "a").WithArguments("a").WithLocation(15, 36), + // (15,22): warning CS8620: Nullability of reference types in argument of type 'List' doesn't match target type 'List' for parameter 'b' in 'void C.F(ref List a, ref List? b, ref List c, ref List? d)'. + // F(ref b, ref c, ref d, ref a); + Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "c").WithArguments("List", "List", "b", "void C.F(ref List a, ref List? b, ref List c, ref List? d)").WithLocation(15, 22), + // (15,36): warning CS8620: Nullability of reference types in argument of type 'List' doesn't match target type 'List' for parameter 'd' in 'void C.F(ref List a, ref List? b, ref List c, ref List? d)'. + // F(ref b, ref c, ref d, ref a); + Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "a").WithArguments("List", "List", "d", "void C.F(ref List a, ref List? b, ref List c, ref List? d)").WithLocation(15, 36), + // (23,15): error CS0165: Use of unassigned local variable 'b' + // F(ref b!, ref c!, ref d!, ref a!); + Diagnostic(ErrorCode.ERR_UseDefViolation, "b").WithArguments("b").WithLocation(23, 15), + // (23,23): error CS0165: Use of unassigned local variable 'c' + // F(ref b!, ref c!, ref d!, ref a!); + Diagnostic(ErrorCode.ERR_UseDefViolation, "c").WithArguments("c").WithLocation(23, 23), + // (23,31): error CS0165: Use of unassigned local variable 'd' + // F(ref b!, ref c!, ref d!, ref a!); + Diagnostic(ErrorCode.ERR_UseDefViolation, "d").WithArguments("d").WithLocation(23, 31), + // (23,39): error CS0165: Use of unassigned local variable 'a' + // F(ref b!, ref c!, ref d!, ref a!); + Diagnostic(ErrorCode.ERR_UseDefViolation, "a").WithArguments("a").WithLocation(23, 39), + // (23,23): warning CS8620: Nullability of reference types in argument of type 'List' doesn't match target type 'List' for parameter 'b' in 'void C.F(ref List a, ref List? b, ref List c, ref List? d)'. + // F(ref b!, ref c!, ref d!, ref a!); + Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "c!").WithArguments("List", "List", "b", "void C.F(ref List a, ref List? b, ref List c, ref List? d)").WithLocation(23, 23), + // (23,39): warning CS8620: Nullability of reference types in argument of type 'List' doesn't match target type 'List' for parameter 'd' in 'void C.F(ref List a, ref List? b, ref List c, ref List? d)'. + // F(ref b!, ref c!, ref d!, ref a!); + Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "a!").WithArguments("List", "List", "d", "void C.F(ref List a, ref List? b, ref List c, ref List? d)").WithLocation(23, 39) + ); + } + + [Fact] + public void SuppressNullableWarning_Out_WithNestedDifferences() + { + var source = +@" +class List { } +class C +{ + static void F(out List a, out List? b, out List c, out List? d) + { + throw null; + } + static void F1(out List a, out List? b, out List c, out List? d) + { + F(out b, out c, out d, out a); // warn on `c` and `a` + } + static void F2(out List a, out List? b, out List c, out List? d) + { + F(out b!, out c!, out d!, out a!); // warn on `c!` and `a!` + } +}"; + var comp = CreateCompilation( + source, + parseOptions: TestOptions.Regular8); + comp.VerifyDiagnostics( + // (11,22): warning CS8600: Converting null literal or possible null value to non-nullable type. + // F(out b, out c, out d, out a); // warn on `c` and `a` + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "c").WithLocation(11, 22), + // (11,36): warning CS8600: Converting null literal or possible null value to non-nullable type. + // F(out b, out c, out d, out a); // warn on `c` and `a` + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "a").WithLocation(11, 36), + // (15,23): warning CS8620: Nullability of reference types in argument of type 'List' doesn't match target type 'List' for parameter 'b' in 'void C.F(out List a, out List? b, out List c, out List? d)'. + // F(out b!, out c!, out d!, out a!); // warn on `c!` and `a!` + Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "c!").WithArguments("List", "List", "b", "void C.F(out List a, out List? b, out List c, out List? d)").WithLocation(15, 23), + // (15,39): warning CS8620: Nullability of reference types in argument of type 'List' doesn't match target type 'List' for parameter 'd' in 'void C.F(out List a, out List? b, out List c, out List? d)'. + // F(out b!, out c!, out d!, out a!); // warn on `c!` and `a!` + Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "a!").WithArguments("List", "List", "d", "void C.F(out List a, out List? b, out List c, out List? d)").WithLocation(15, 39) + ); + } + [Fact] public void SuppressNullableWarning_Out() { @@ -19874,8 +20465,10 @@ static void Main() { string? s; string t; - F(out s, out t); - F(out s!, out t!); + F(out s, out t); // warn + F(out s!, out t!); // ok + F(out (s!), out (t!)); // errors + F(out (s)!, out (t)!); // errors } }"; var comp = CreateCompilation( @@ -19883,9 +20476,22 @@ static void Main() parseOptions: TestOptions.Regular8, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( + // (15,19): error CS1525: Invalid expression term ',' + // F(out (s)!, out (t)!); // errors + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ",").WithArguments(",").WithLocation(15, 19), + // (15,29): error CS1525: Invalid expression term ')' + // F(out (s)!, out (t)!); // errors + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(15, 29), + // (15,16): error CS0118: 's' is a variable but is used like a type + // F(out (s)!, out (t)!); // errors + Diagnostic(ErrorCode.ERR_BadSKknown, "s").WithArguments("s", "variable", "type").WithLocation(15, 16), + // (15,26): error CS0118: 't' is a variable but is used like a type + // F(out (s)!, out (t)!); // errors + Diagnostic(ErrorCode.ERR_BadSKknown, "t").WithArguments("t", "variable", "type").WithLocation(15, 26), // (12,22): warning CS8600: Converting null literal or possible null value to non-nullable type. - // F(out s, out t); - Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "t").WithLocation(12, 22)); + // F(out s, out t); // warn + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "t").WithLocation(12, 22) + ); } // PROTOTYPE(NullableReferenceTypes): 't! = s' should be an error. @@ -19908,7 +20514,14 @@ static void Main() source, parseOptions: TestOptions.Regular8, options: TestOptions.ReleaseExe); - comp.VerifyDiagnostics(/* ... */); + comp.VerifyDiagnostics( + // (7,14): warning CS8601: Possible null reference assignment. + // t! = s; + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "s").WithLocation(7, 14), + // (9,16): warning CS8601: Possible null reference assignment. + // (t!) = s; + Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "s").WithLocation(9, 16) + ); } [Fact] @@ -20035,12 +20648,16 @@ public void SuppressNullableWarning_ValueType_02() var source = @"struct S where T : class { - static S F(S s) => s!; + static S F(S s) => s!/*T:S*/; }"; var comp = CreateCompilation( source, parseOptions: TestOptions.Regular8); - comp.VerifyDiagnostics(); + comp.VerifyDiagnostics( + // (3,41): warning CS8619: Nullability of reference types in value of type 'S' doesn't match target type 'S'. + // static S F(S s) => s!/*T:S*/; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "s!").WithArguments("S", "S").WithLocation(3, 41)); + comp.VerifyTypes(); } // PROTOTYPE(NullableReferenceTypes): Should not warn for `default(T1)!.ToString()`. diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/StaticNullChecking_FlowAnalysis.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/StaticNullChecking_FlowAnalysis.cs index e43b6393300a930d50a77fb83eb9c0d4553b4d51..7164c0eb0d64efb0b71cf341e1d249f2ba38644c 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/StaticNullChecking_FlowAnalysis.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/StaticNullChecking_FlowAnalysis.cs @@ -49,10 +49,15 @@ static void F(object x, object? y) a[0].ToString(); var b = new[] { y }; b[0].ToString(); + } + static void F(object[] a, object?[] b) + { var c = new[] { a, b }; c[0][0].ToString(); var d = new[] { a, b! }; d[0][0].ToString(); + var e = new[] { b!, a }; + e[0][0].ToString(); } }"; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8); @@ -60,9 +65,15 @@ static void F(object x, object? y) // (8,9): warning CS8602: Possible dereference of a null reference. // b[0].ToString(); Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "b[0]").WithLocation(8, 9), - // (10,9): warning CS8602: Possible dereference of a null reference. + // (13,9): warning CS8602: Possible dereference of a null reference. // c[0][0].ToString(); - Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c[0][0]").WithLocation(10, 9)); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c[0][0]").WithLocation(13, 9), + // (15,9): warning CS8602: Possible dereference of a null reference. + // d[0][0].ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "d[0][0]").WithLocation(15, 9), + // (17,9): warning CS8602: Possible dereference of a null reference. + // e[0][0].ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "e[0][0]").WithLocation(17, 9)); } [Fact] @@ -2742,8 +2753,7 @@ static void F(IOut x, IOut y, IOut? z, IOut? w } // PROTOTYPE(NullableReferenceTypes): Update this method to use types from unannotated assemblies - // rather than `x!`, particularly because `x!` should result in IsNullable=false rather than IsNullable=null. - // PROTOTYPE(NullableReferenceTypes): Should report the same warnings (or no warnings) for { x, x! } and { x!, x }. + // rather than `x!`, particularly because `x!` results in IsNullable=false rather than IsNullable=null. [Fact] public void IdentityConversion_ArrayInitializer_IsNullableNull() { @@ -2760,6 +2770,7 @@ static void F(object? x, object y) { (new[] { x, x! })[0].ToString(); (new[] { x!, x })[0].ToString(); + (new[] { x!, x! })[0].ToString(); (new[] { y, y! })[0].ToString(); (new[] { y!, y })[0].ToString(); } @@ -2767,6 +2778,7 @@ static void F(A z, A w) { (new[] { z, z! })[0].F.ToString(); (new[] { z!, z })[0].F.ToString(); + (new[] { z!, z! })[0].F.ToString(); (new[] { w, w! })[0].F.ToString(); (new[] { w!, w })[0].F.ToString(); } @@ -2776,14 +2788,23 @@ static void F(A z, A w) // (11,9): warning CS8602: Possible dereference of a null reference. // (new[] { x, x! })[0].ToString(); Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(new[] { x, x! })[0]").WithLocation(11, 9), - // (18,9): warning CS8602: Possible dereference of a null reference. + // (12,9): warning CS8602: Possible dereference of a null reference. + // (new[] { x!, x })[0].ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(new[] { x!, x })[0]").WithLocation(12, 9), + // (19,9): warning CS8602: Possible dereference of a null reference. // (new[] { z, z! })[0].F.ToString(); - Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(new[] { z, z! })[0].F").WithLocation(18, 9)); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(new[] { z, z! })[0].F").WithLocation(19, 9), + // (20,9): warning CS8602: Possible dereference of a null reference. + // (new[] { z!, z })[0].F.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(new[] { z!, z })[0].F").WithLocation(20, 9), + // (21,9): warning CS8602: Possible dereference of a null reference. + // (new[] { z!, z! })[0].F.ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "(new[] { z!, z! })[0].F").WithLocation(21, 9) + ); } // PROTOTYPE(NullableReferenceTypes): Update this method to use types from unannotated assemblies - // rather than `x!`, particularly because `x!` should result in IsNullable=false rather than IsNullable=null. - // PROTOTYPE(NullableReferenceTypes): Should report the same warnings (or no warnings) for (x, x!) and (x!, x). + // rather than `x!`, particularly because `x!` results in IsNullable=false rather than IsNullable=null. [Fact] public void IdentityConversion_TypeInference_IsNullableNull() { @@ -2821,9 +2842,15 @@ static void G(A z, A w) // (12,9): warning CS8602: Possible dereference of a null reference. // F1(x, x!).ToString(); Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "F1(x, x!)").WithLocation(12, 9), + // (13,9): warning CS8602: Possible dereference of a null reference. + // F1(x!, x).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "F1(x!, x)").WithLocation(13, 9), // (23,9): warning CS8602: Possible dereference of a null reference. // F2(z, z!).ToString(); - Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "F2(z, z!)").WithLocation(23, 9)); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "F2(z, z!)").WithLocation(23, 9), + // (24,9): warning CS8602: Possible dereference of a null reference. + // F2(z!, z).ToString(); + Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "F2(z!, z)").WithLocation(24, 9)); } [Fact] @@ -3518,12 +3545,18 @@ static void F3(B? x3) // (8,14): warning CS8619: Nullability of reference types in value of type 'B' doesn't match target type 'A'. // y1 = x1; Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x1").WithArguments("B", "A").WithLocation(8, 14), + // (9,14): warning CS8619: Nullability of reference types in value of type 'B' doesn't match target type 'A'. + // y1 = x1!; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x1!").WithArguments("B", "A").WithLocation(9, 14), // (13,24): warning CS8619: Nullability of reference types in value of type 'B' doesn't match target type 'A'. // A y2 = x2; Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x2").WithArguments("B", "A").WithLocation(13, 24), // (14,14): warning CS8619: Nullability of reference types in value of type 'B' doesn't match target type 'A'. // y2 = x2; Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x2").WithArguments("B", "A").WithLocation(14, 14), + // (15,14): warning CS8619: Nullability of reference types in value of type 'B' doesn't match target type 'A'. + // y2 = x2!; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x2!").WithArguments("B", "A").WithLocation(15, 14), // (19,25): warning CS8600: Converting null literal or possible null value to non-nullable type. // A y3 = x3; Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "x3").WithLocation(19, 25), @@ -3535,7 +3568,11 @@ static void F3(B? x3) Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "x3").WithLocation(20, 14), // (20,14): warning CS8619: Nullability of reference types in value of type 'B' doesn't match target type 'A'. // y3 = x3; - Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x3").WithArguments("B", "A").WithLocation(20, 14)); + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x3").WithArguments("B", "A").WithLocation(20, 14), + // (21,14): warning CS8619: Nullability of reference types in value of type 'B' doesn't match target type 'A'. + // y3 = x3!; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x3!").WithArguments("B", "A").WithLocation(21, 14) + ); } [Fact] @@ -3573,12 +3610,18 @@ static void F3(IB? x3) // (8,14): warning CS8619: Nullability of reference types in value of type 'IB' doesn't match target type 'IA'. // y1 = x1; Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x1").WithArguments("IB", "IA").WithLocation(8, 14), + // (9,14): warning CS8619: Nullability of reference types in value of type 'IB' doesn't match target type 'IA'. + // y1 = x1!; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x1!").WithArguments("IB", "IA").WithLocation(9, 14), // (13,25): warning CS8619: Nullability of reference types in value of type 'IB' doesn't match target type 'IA'. // IA y2 = x2; Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x2").WithArguments("IB", "IA").WithLocation(13, 25), // (14,14): warning CS8619: Nullability of reference types in value of type 'IB' doesn't match target type 'IA'. // y2 = x2; Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x2").WithArguments("IB", "IA").WithLocation(14, 14), + // (15,14): warning CS8619: Nullability of reference types in value of type 'IB' doesn't match target type 'IA'. + // y2 = x2!; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x2!").WithArguments("IB", "IA").WithLocation(15, 14), // (19,26): warning CS8600: Converting null literal or possible null value to non-nullable type. // IA y3 = x3; Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "x3").WithLocation(19, 26), @@ -3590,7 +3633,10 @@ static void F3(IB? x3) Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "x3").WithLocation(20, 14), // (20,14): warning CS8619: Nullability of reference types in value of type 'IB' doesn't match target type 'IA'. // y3 = x3; - Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x3").WithArguments("IB", "IA").WithLocation(20, 14)); + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x3").WithArguments("IB", "IA").WithLocation(20, 14), + // (21,14): warning CS8619: Nullability of reference types in value of type 'IB' doesn't match target type 'IA'. + // y3 = x3!; + Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "x3!").WithArguments("IB", "IA").WithLocation(21, 14)); } [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/StaticNullChecking_TypeInference.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/StaticNullChecking_TypeInference.cs index f96ad8f492d473a7dcd9bb54cf005157f61a69dc..7addfbf1a635dbec462b31cd45d9a6367f835819 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/StaticNullChecking_TypeInference.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/StaticNullChecking_TypeInference.cs @@ -595,13 +595,17 @@ public void LocalVar_FlowAnalysis_08() static void F(string? s) { var t = s!; - t.ToString(); + t/*T:string!*/.ToString(); t = null; } }"; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular8); - comp.VerifyDiagnostics(); + comp.VerifyDiagnostics( + // (7,13): warning CS8600: Converting null literal or possible null value to non-nullable type. + // t = null; + Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithLocation(7, 13)); + comp.VerifyTypes(); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree);