From fc15d5ee8ac4eb66196f65477509c62e875b8c87 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Fri, 8 Jun 2018 19:15:49 -0700 Subject: [PATCH] Suppression operator produces non-null result (#27317) --- .../Portable/FlowAnalysis/DataFlowPass.cs | 6 +- .../Portable/FlowAnalysis/NullableWalker.cs | 63 +- .../FlowAnalysis/PreciseAbstractFlowPass.cs | 4 + .../Portable/Symbols/SymbolWithAnnotations.cs | 11 + .../Semantic/Semantics/StaticNullChecking.cs | 721 ++++++++++++++++-- .../StaticNullChecking_FlowAnalysis.cs | 68 +- .../StaticNullChecking_TypeInference.cs | 8 +- 7 files changed, 793 insertions(+), 88 deletions(-) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs index 8269db7e442..3b4ff85a1fa 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 7046dc9ff87..bbff66d6756 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 48767115ed5..0e0b7ef121a 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 5edb022b9be..670811c9180 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 d7eb932e5c2..d84130c34ef 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 e43b6393300..7164c0eb0d6 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 f96ad8f492d..7addfbf1a63 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); -- GitLab