diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index 50624951cd3bfef9953ed41ef3b287fb9fd4ddc4..fc78aabe6784da22eac3699ebe62e49cc1ed3a5c 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -505,8 +505,6 @@ private BoundExpression CreateMethodGroupConversion(SyntaxNode syntax, BoundExpr _ => throw ExceptionUtilities.UnexpectedValue(source), }; BoundMethodGroup group = FixMethodGroupWithTypeOrValue(originalGroup, conversion, diagnostics); - BoundExpression? receiverOpt = group.ReceiverOpt; - MethodSymbol? method = conversion.Method; bool hasErrors = false; if (MethodGroupConversionHasErrors(syntax, conversion, group.ReceiverOpt, conversion.IsExtensionMethod, destination, diagnostics)) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/BestTypeInferrer.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/BestTypeInferrer.cs index d098036806eb296cc919e1b27eac5fa637f507e9..04691cde5334b2a4aa82954a294e26c1a9ed033c 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/BestTypeInferrer.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/BestTypeInferrer.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; +using System.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.PooledObjects; @@ -14,11 +15,17 @@ internal static class BestTypeInferrer { public static NullableAnnotation GetNullableAnnotation(ArrayBuilder types) { +#if DEBUG + var example = types.FirstOrDefault(t => t.HasType); +#endif + var result = NullableAnnotation.NotAnnotated; foreach (var type in types) { - Debug.Assert(type.HasType); - Debug.Assert(type.Equals(types[0], TypeCompareKind.AllIgnoreOptions)); +#if DEBUG + Debug.Assert(!type.HasType || type.Equals(example, TypeCompareKind.AllIgnoreOptions)); +#endif + // This uses the covariant merging rules. result = result.Join(type.NullableAnnotation); } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index b1d65aac65646418a050efa86da349e2c0691ac9..e26d61f87b98030c84f2cb8a4b344e0376a72ae1 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -2842,11 +2842,9 @@ protected override void VisitStatement(BoundStatement statement) public override BoundNode? VisitUnconvertedObjectCreationExpression(BoundUnconvertedObjectCreationExpression node) { - var discardedDiagnostics = new DiagnosticBag(); - var expr = _binder!.BindObjectCreationForErrorRecovery(node, discardedDiagnostics); - discardedDiagnostics.Free(); - Visit(expr); - SetResultType(node, TypeWithState.Create(expr.Type, NullableFlowState.NotNull)); + // This method is only involved in method inference with unbound lambdas. + // The diagnostics on arguments are reported by VisitObjectCreationExpression. + SetResultType(node, TypeWithState.Create(null, NullableFlowState.NotNull)); return null; } @@ -4108,8 +4106,6 @@ private static TypeWithState GetNullCoalescingResultType(TypeWithState rightResu BoundExpression originalConsequence, BoundExpression originalAlternative) { - Debug.Assert(node.Type is object); - VisitCondition(condition); var consequenceState = this.StateWhenTrue; var alternativeState = this.StateWhenFalse; @@ -4127,7 +4123,7 @@ private static TypeWithState GetNullCoalescingResultType(TypeWithState rightResu (alternativeLValue, alternativeRValue) = visitConditionalRefOperand(alternativeState, originalAlternative); Join(ref this.State, ref consequenceState); - TypeSymbol refResultType = node.Type.SetUnknownNullabilityForReferenceTypes(); + TypeSymbol? refResultType = node.Type?.SetUnknownNullabilityForReferenceTypes(); if (IsNullabilityMismatch(consequenceLValue, alternativeLValue)) { // l-value types must match @@ -4204,8 +4200,8 @@ private static TypeWithState GetNullCoalescingResultType(TypeWithState rightResu NullableFlowState resultState; if (resultType is null) { - resultType = node.Type.SetUnknownNullabilityForReferenceTypes(); - resultState = NullableFlowState.NotNull; + resultType = node.Type?.SetUnknownNullabilityForReferenceTypes(); + resultState = consequenceRValue.State.Join(alternativeRValue.State); var resultTypeWithState = TypeWithState.Create(resultType, resultState); @@ -6541,9 +6537,7 @@ private bool HasTopLevelNullabilityConversion(TypeWithAnnotations source, TypeWi case ConversionKind.ObjectCreation: case ConversionKind.SwitchExpression: case ConversionKind.ConditionalExpression: - // These are not represented as a separate conversion in the bound tree. - // Instead they are folded into the operand. - throw ExceptionUtilities.UnexpectedValue(conversion.Kind); + return operandType; case ConversionKind.ExplicitUserDefined: case ConversionKind.ImplicitUserDefined: @@ -8795,9 +8789,9 @@ private TypeWithState InferResultNullabilityOfBinaryLogicalOperator(BoundExpress public override BoundNode? VisitDefaultLiteral(BoundDefaultLiteral node) { - // Can occur in error scenarios + // Can occur in error scenarios and lambda scenarios var result = base.VisitDefaultLiteral(node); - SetUnknownResultNullability(node); + SetResultType(node, TypeWithState.Create(node.Type, NullableFlowState.MaybeDefault)); return result; } diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs index ef8b8344d45bba55231f60e00531f57be31771ab..ee778886b097e37f6d7b19dce508b4e55ae8a626 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker_Patterns.cs @@ -566,6 +566,7 @@ public override BoundNode VisitConvertedSwitchExpression(BoundConvertedSwitchExp public override BoundNode VisitUnconvertedSwitchExpression(BoundUnconvertedSwitchExpression node) { + // This method is only involved in method inference with unbound lambdas. VisitSwitchExpressionCore(node, inferType: true); return null; } @@ -632,34 +633,40 @@ private void VisitSwitchExpressionCore(BoundSwitchExpression node, bool inferTyp TypeSymbol inferredType = (inferType ? BestTypeInferrer.InferBestType(placeholders, _conversions, ref useSiteDiagnostics) : null) - ?? node.Type?.SetUnknownNullabilityForReferenceTypes() - ?? new ExtendedErrorTypeSymbol(this.compilation, "", arity: 0, errorInfo: null, unreported: false); + ?? node.Type?.SetUnknownNullabilityForReferenceTypes(); var inferredTypeWithAnnotations = TypeWithAnnotations.Create(inferredType); // Convert elements to best type to determine element top-level nullability and to report nested nullability warnings - for (int i = 0; i < numSwitchArms; i++) + if (inferredType is not null) { - var expression = expressions[i]; - resultTypes[i] = VisitConversion(conversionOpt: null, expression, conversions[i], inferredTypeWithAnnotations, resultTypes[i], checkConversion: true, - fromExplicitCast: false, useLegacyWarnings: false, AssignmentKind.Assignment, reportRemainingWarnings: true, reportTopLevelWarnings: false); + for (int i = 0; i < numSwitchArms; i++) + { + var expression = expressions[i]; + resultTypes[i] = VisitConversion(conversionOpt: null, expression, conversions[i], inferredTypeWithAnnotations, resultTypes[i], checkConversion: true, + fromExplicitCast: false, useLegacyWarnings: false, AssignmentKind.Assignment, reportRemainingWarnings: true, reportTopLevelWarnings: false); + } } var inferredState = BestTypeInferrer.GetNullableState(resultTypes); var resultType = TypeWithState.Create(inferredType, inferredState); - inferredTypeWithAnnotations = resultType.ToTypeWithAnnotations(compilation); - if (resultType.State == NullableFlowState.MaybeDefault) - { - inferredTypeWithAnnotations = inferredTypeWithAnnotations.AsAnnotated(); - } - for (int i = 0; i < numSwitchArms; i++) + if (inferredType is not null) { - var nodeForSyntax = expressions[i]; - var conversionOpt = node.SwitchArms[i].Value switch { BoundConversion c when c != nodeForSyntax => c, _ => null }; - // Report top-level warnings - _ = VisitConversion(conversionOpt, conversionOperand: nodeForSyntax, conversions[i], targetTypeWithNullability: inferredTypeWithAnnotations, operandType: resultTypes[i], - checkConversion: true, fromExplicitCast: false, useLegacyWarnings: false, AssignmentKind.Assignment, reportRemainingWarnings: false, reportTopLevelWarnings: true); + inferredTypeWithAnnotations = resultType.ToTypeWithAnnotations(compilation); + if (resultType.State == NullableFlowState.MaybeDefault) + { + inferredTypeWithAnnotations = inferredTypeWithAnnotations.AsAnnotated(); + } + + for (int i = 0; i < numSwitchArms; i++) + { + var nodeForSyntax = expressions[i]; + var conversionOpt = node.SwitchArms[i].Value switch { BoundConversion c when c != nodeForSyntax => c, _ => null }; + // Report top-level warnings + _ = VisitConversion(conversionOpt, conversionOperand: nodeForSyntax, conversions[i], targetTypeWithNullability: inferredTypeWithAnnotations, operandType: resultTypes[i], + checkConversion: true, fromExplicitCast: false, useLegacyWarnings: false, AssignmentKind.Assignment, reportRemainingWarnings: false, reportTopLevelWarnings: true); + } } conversions.Free(); diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeWithAnnotations.cs b/src/Compilers/CSharp/Portable/Symbols/TypeWithAnnotations.cs index 9b150fd195ff1c8704d3759a7e7ed3364c08ed45..c9ad96daa10a35df6bb93c76522b347120c4820f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeWithAnnotations.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeWithAnnotations.cs @@ -782,7 +782,7 @@ static NullableFlowState getFlowState(TypeSymbol type, NullableAnnotation annota { if (type is null) { - return annotation.IsAnnotated() ? NullableFlowState.MaybeNull : NullableFlowState.NotNull; + return annotation.IsAnnotated() ? NullableFlowState.MaybeDefault : NullableFlowState.NotNull; } if (type.IsPossiblyNullableReferenceTypeTypeParameter()) { diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index a35f607b263abaad245a45fe93828847396505e5..07622267871d48418bf0693746735cdca2327503 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -44288,6 +44288,504 @@ static void F(bool x, bool y, bool z, bool? w) Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "x ? y && z : w").WithLocation(6, 13)); } + [Fact, WorkItem(46954, "https://github.com/dotnet/roslyn/issues/46954")] + public void ReturningValues_ConditionalOperator_WithoutType_WithNullLiterals() + { + var source = +@"class Program +{ + static void M(System.Func t) { } + static void F(bool b) + { + M(() => + { + if (b) + return b ? null : null; + else + return new object(); + }); + } +}"; + var comp = CreateCompilation(source, options: WithNonNullTypesTrue()); + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); + var nullNode = tree.GetRoot().DescendantNodes().OfType().First(); + Assert.Equal("null", nullNode.ToString()); + Assert.Null(model.GetTypeInfo(nullNode).Type); + Assert.Equal("System.Object?", model.GetTypeInfo(nullNode).ConvertedType.ToTestDisplayString()); + Assert.Equal(CodeAnalysis.NullableFlowState.MaybeNull, model.GetTypeInfo(nullNode).ConvertedNullability.FlowState); + + var invocationNode = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal("void Program.M(System.Func t)", model.GetSymbolInfo(invocationNode).Symbol.ToTestDisplayString()); + + comp.VerifyDiagnostics(); + } + + [Fact, WorkItem(46954, "https://github.com/dotnet/roslyn/issues/46954")] + public void ArrayTypeInference_ConditionalOperator_WithoutType_WithNullLiterals() + { + var source = +@"class Program +{ + static void F(bool b) + { + var x = new[] { b ? null : null, new object() }; + } +}"; + var comp = CreateCompilation(source, options: WithNonNullTypesTrue()); + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); + var nullNode = tree.GetRoot().DescendantNodes().OfType().First(); + Assert.Equal("null", nullNode.ToString()); + Assert.Null(model.GetTypeInfo(nullNode).Type); + Assert.Equal("System.Object?", model.GetTypeInfo(nullNode).ConvertedType.ToTestDisplayString()); + Assert.Equal(CodeAnalysis.NullableFlowState.MaybeNull, model.GetTypeInfo(nullNode).ConvertedNullability.FlowState); + + var invocationNode = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal("System.Object?[]", model.GetTypeInfo(invocationNode).Type.ToTestDisplayString()); + + comp.VerifyDiagnostics(); + } + + [Fact, WorkItem(46954, "https://github.com/dotnet/roslyn/issues/46954")] + public void ReturningValues_ConditionalOperator_WithoutType_WithDefaultLiterals() + { + var source = +@"class Program +{ + static void M(System.Func t) { } + static void F(bool b) where U : new() + { + M(() => + { + if (b) + return b ? default : default; + else + return new U(); + }); + } +}"; + var comp = CreateCompilation(source, options: WithNonNullTypesTrue()); + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); + var defaultNode = tree.GetRoot().DescendantNodes().OfType().First(); + Assert.Equal("default", defaultNode.ToString()); + Assert.Equal("U?", model.GetTypeInfo(defaultNode).Type.ToTestDisplayString()); + Assert.Equal("U?", model.GetTypeInfo(defaultNode).ConvertedType.ToTestDisplayString()); + Assert.Equal(CodeAnalysis.NullableFlowState.MaybeNull, model.GetTypeInfo(defaultNode).ConvertedNullability.FlowState); + + var invocationNode = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal("void Program.M(System.Func t)", model.GetSymbolInfo(invocationNode).Symbol.ToTestDisplayString()); + + comp.VerifyDiagnostics(); + } + + [Fact, WorkItem(46954, "https://github.com/dotnet/roslyn/issues/46954")] + public void ReturningValues_ConditionalOperator_WithoutType_WithDefaultLiterals_StructType() + { + var source = +@"class Program +{ + static void M(System.Func t) { } + static void F(bool b) where U : struct + { + M(() => + { + if (b) + return b ? default : default; + else + return new U(); + }); + } +}"; + var comp = CreateCompilation(source, options: WithNonNullTypesTrue()); + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); + var defaultNode = tree.GetRoot().DescendantNodes().OfType().First(); + Assert.Equal("default", defaultNode.ToString()); + Assert.Equal("U", model.GetTypeInfo(defaultNode).Type.ToTestDisplayString()); + Assert.Equal("U", model.GetTypeInfo(defaultNode).ConvertedType.ToTestDisplayString()); + Assert.Equal(CodeAnalysis.NullableFlowState.NotNull, model.GetTypeInfo(defaultNode).ConvertedNullability.FlowState); + + var invocationNode = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal("void Program.M(System.Func t)", model.GetSymbolInfo(invocationNode).Symbol.ToTestDisplayString()); + + comp.VerifyDiagnostics(); + } + + [Fact, WorkItem(46954, "https://github.com/dotnet/roslyn/issues/46954")] + public void ReturningValues_ConditionalOperator_WithoutType_WithMaybeNullVariables() + { + var source = +@"class Program +{ + static void M(System.Func t) { } + static void F(bool b) + { + M(() => + { + Program? x = null; + string? y = null; + if (b) + return b ? x : y; + else + return new object(); + }); + } +}"; + var comp = CreateCompilation(source, options: WithNonNullTypesTrue()); + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); + + var invocationNode = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal("void Program.M(System.Func t)", model.GetSymbolInfo(invocationNode).Symbol.ToTestDisplayString()); + + comp.VerifyDiagnostics(); + } + + [Fact, WorkItem(46954, "https://github.com/dotnet/roslyn/issues/46954")] + public void ReturningValues_ConditionalOperator_WithoutType_WithNotNullVariables() + { + var source = +@"class Program +{ + static void M(System.Func t) { } + static void F(bool b) + { + M(() => + { + Program? x = new(); + string? y = string.Empty; + if (b) + return b ? x : y; + else + return new object(); + }); + } +}"; + var comp = CreateCompilation(source, options: WithNonNullTypesTrue()); + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); + + var invocationNode = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal("void Program.M(System.Func t)", model.GetSymbolInfo(invocationNode).Symbol.ToTestDisplayString()); + + comp.VerifyDiagnostics(); + } + + [Fact, WorkItem(46954, "https://github.com/dotnet/roslyn/issues/46954")] + public void ReturningValues_SwitchExpression_WithoutType_NullLiteral() + { + var source = +@"class Program +{ + static void M(System.Func t) { } + static void F(bool b) + { + M(() => + { + if (b) + return b switch { _ => null }; + else + return new object(); + }); + } +}"; + var comp = CreateCompilation(source, options: WithNonNullTypesTrue()); + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); + var nullNode = tree.GetRoot().DescendantNodes().OfType().First(); + Assert.Equal("null", nullNode.ToString()); + Assert.Null(model.GetTypeInfo(nullNode).Type); + Assert.Equal("System.Object?", model.GetTypeInfo(nullNode).ConvertedType.ToTestDisplayString()); + Assert.Equal(CodeAnalysis.NullableFlowState.MaybeNull, model.GetTypeInfo(nullNode).ConvertedNullability.FlowState); + + var invocationNode = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal("void Program.M(System.Func t)", model.GetSymbolInfo(invocationNode).Symbol.ToTestDisplayString()); + + comp.VerifyDiagnostics(); + } + + [Fact, WorkItem(46954, "https://github.com/dotnet/roslyn/issues/46954")] + public void ReturningValues_SwitchExpression_WithoutType_WithMaybeNullVariables() + { + var source = +@"class Program +{ + static void M(System.Func t) { } + static void F(bool b) + { + M(() => + { + Program? x = null; + string? y = null; + if (b) + return b switch { true => x, _ => y }; + else + return new object(); + }); + } +}"; + var comp = CreateCompilation(source, options: WithNonNullTypesTrue()); + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); + + var invocationNode = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal("void Program.M(System.Func t)", model.GetSymbolInfo(invocationNode).Symbol.ToTestDisplayString()); + + comp.VerifyDiagnostics(); + } + + [Fact, WorkItem(46954, "https://github.com/dotnet/roslyn/issues/46954")] + public void ReturningValues_SwitchExpression_WithoutType_WithNotNullVariables() + { + var source = +@"class Program +{ + static void M(System.Func t) { } + static void F(bool b) + { + M(() => + { + Program? x = new(); + string? y = string.Empty; + if (b) + return b switch { true => x, _ => y }; + else + return new object(); + }); + } +}"; + var comp = CreateCompilation(source, options: WithNonNullTypesTrue()); + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); + + var invocationNode = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal("void Program.M(System.Func t)", model.GetSymbolInfo(invocationNode).Symbol.ToTestDisplayString()); + + comp.VerifyDiagnostics(); + } + + [Fact, WorkItem(46954, "https://github.com/dotnet/roslyn/issues/46954")] + public void ReturningValues_WithoutType_New() + { + var source = +@"class Program +{ + static void M(System.Func t) { } + + static void F(bool b) + { + M(() => + { + if (b) + return new(); + else + return new object(); + }); + } +}"; + var comp = CreateCompilation(source, options: WithNonNullTypesTrue()); + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); + var newNode = tree.GetRoot().DescendantNodes().OfType().First(); + Assert.Equal("new()", newNode.ToString()); + Assert.Equal("System.Object", model.GetTypeInfo(newNode).Type.ToTestDisplayString()); + Assert.Equal("System.Object", model.GetTypeInfo(newNode).ConvertedType.ToTestDisplayString()); + + var invocationNode = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal("void Program.M(System.Func t)", model.GetSymbolInfo(invocationNode).Symbol.ToTestDisplayString()); + + comp.VerifyDiagnostics(); + } + + [Fact, WorkItem(46954, "https://github.com/dotnet/roslyn/issues/46954")] + public void ReturningValues_WithoutType_NewWithArguments() + { + var source = +@"class Program +{ + static void M(System.Func t) { } + + static void F(bool b) + { + M(() => + { + if (b) + return new(null); + else + return new Program(string.Empty); + }); + } + + Program(string s) { } +}"; + var comp = CreateCompilation(source, options: WithNonNullTypesTrue()); + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); + var newNode = tree.GetRoot().DescendantNodes().OfType().First(); + Assert.Equal("new(null)", newNode.ToString()); + Assert.Equal("Program", model.GetTypeInfo(newNode).Type.ToTestDisplayString()); + Assert.Equal("Program", model.GetTypeInfo(newNode).ConvertedType.ToTestDisplayString()); + + var invocationNode = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal("void Program.M(System.Func t)", model.GetSymbolInfo(invocationNode).Symbol.ToTestDisplayString()); + + comp.VerifyDiagnostics( + // (10,28): warning CS8625: Cannot convert null literal to non-nullable reference type. + // return new(null); + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(10, 28) + ); + } + + [Fact, WorkItem(46954, "https://github.com/dotnet/roslyn/issues/46954")] + public void ReturningValues_WithoutType() + { + var source = +@"class Program +{ + static void M(System.Func t) { } + static void F(bool b) + { + M(() => + { + if (b) + return null; + else + return new object(); + }); + } +}"; + var comp = CreateCompilation(source, options: WithNonNullTypesTrue()); + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); + var nullNode = tree.GetRoot().DescendantNodes().OfType().First(); + Assert.Equal("null", nullNode.ToString()); + Assert.Null(model.GetTypeInfo(nullNode).Type); + Assert.Equal("System.Object?", model.GetTypeInfo(nullNode).ConvertedType.ToTestDisplayString()); + + var invocationNode = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal("void Program.M(System.Func t)", model.GetSymbolInfo(invocationNode).Symbol.ToTestDisplayString()); + + comp.VerifyDiagnostics(); + } + + [Fact, WorkItem(44339, "https://github.com/dotnet/roslyn/issues/44339")] + public void TypeInference_LambdasWithNullsAndDefaults() + { + var source = @" +#nullable enable + +public class C +{ + public void M1(bool b) + { + var map = new C(); + + map.GetOrAdd("""", _ => default); // 1 + map.GetOrAdd("""", _ => null); // 2 + + map.GetOrAdd("""", _ => { if (b) return default; return """"; }); // 3 + map.GetOrAdd("""", _ => { if (b) return null; return """"; }); // 4 + map.GetOrAdd("""", _ => { if (b) return """"; return null; }); // 5 + } +} + +public static class Extensions +{ + public static V GetOrAdd(this C dictionary, K key, System.Func function) + => throw null!; +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (10,31): warning CS8603: Possible null reference return. + // map.GetOrAdd("", _ => default); // 1 + Diagnostic(ErrorCode.WRN_NullReferenceReturn, "default").WithLocation(10, 31), + // (11,31): warning CS8603: Possible null reference return. + // map.GetOrAdd("", _ => null); // 2 + Diagnostic(ErrorCode.WRN_NullReferenceReturn, "null").WithLocation(11, 31), + // (13,9): warning CS8620: Argument of type 'C' cannot be used for parameter 'dictionary' of type 'C' in 'string? Extensions.GetOrAdd(C dictionary, string key, Func function)' due to differences in the nullability of reference types. + // map.GetOrAdd("", _ => { if (b) return default; return ""; }); // 3 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "map").WithArguments("C", "C", "dictionary", "string? Extensions.GetOrAdd(C dictionary, string key, Func function)").WithLocation(13, 9), + // (14,9): warning CS8620: Argument of type 'C' cannot be used for parameter 'dictionary' of type 'C' in 'string? Extensions.GetOrAdd(C dictionary, string key, Func function)' due to differences in the nullability of reference types. + // map.GetOrAdd("", _ => { if (b) return null; return ""; }); // 4 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "map").WithArguments("C", "C", "dictionary", "string? Extensions.GetOrAdd(C dictionary, string key, Func function)").WithLocation(14, 9), + // (15,9): warning CS8620: Argument of type 'C' cannot be used for parameter 'dictionary' of type 'C' in 'string? Extensions.GetOrAdd(C dictionary, string key, Func function)' due to differences in the nullability of reference types. + // map.GetOrAdd("", _ => { if (b) return ""; return null; }); // 5 + Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "map").WithArguments("C", "C", "dictionary", "string? Extensions.GetOrAdd(C dictionary, string key, Func function)").WithLocation(15, 9) + ); + } + + [Fact, WorkItem(43536, "https://github.com/dotnet/roslyn/issues/43536")] + public void TypeInference_StringAndNullOrDefault() + { + var source = @" +#nullable enable + +class C +{ + void M(string s) + { + Infer(s, default); + Infer(s, null); + } + + T Infer(T t1, T t2) => t1 ?? t2; +} +"; + // We're expecting the same inference and warnings for both invocations + // Tracked by issue https://github.com/dotnet/roslyn/issues/43536 + + var comp = CreateCompilation(source); + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); + + var invocationNode = tree.GetRoot().DescendantNodes().OfType().First(); + Assert.Equal("System.String? C.Infer(System.String? t1, System.String? t2)", model.GetSymbolInfo(invocationNode).Symbol.ToTestDisplayString()); + + var invocationNode2 = tree.GetRoot().DescendantNodes().OfType().Last(); + Assert.Equal("System.String C.Infer(System.String t1, System.String t2)", model.GetSymbolInfo(invocationNode2).Symbol.ToTestDisplayString()); + + comp.VerifyDiagnostics( + // (9,18): warning CS8625: Cannot convert null literal to non-nullable reference type. + // Infer(s, null); // Infer, CS8625 cannot convert null literal to non-nullable refernece type + Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(9, 18) + ); + } + + [Fact] + public void ConditionalOperator_WithoutType_Lambda() + { + var source = +@"class Program +{ + static void M(System.Func t) { } + static void F(bool b, System.Action a) + { + M(() => + { + if (b) + return () => { }; + else + return a; + }); + } +}"; + var comp = CreateCompilation(source, options: WithNonNullTypesTrue()); + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); + var lambdaNode = tree.GetRoot().DescendantNodes().OfType().Last(); + Assert.Equal("() => { }", lambdaNode.ToString()); + Assert.Null(model.GetTypeInfo(lambdaNode).Type); + Assert.Equal("System.Action", model.GetTypeInfo(lambdaNode).ConvertedType.ToTestDisplayString()); + + var invocationNode = tree.GetRoot().DescendantNodes().OfType().Single(); + Assert.Equal("void Program.M(System.Func t)", model.GetSymbolInfo(invocationNode).Symbol.ToTestDisplayString()); + + comp.VerifyDiagnostics(); + } + [Fact] public void ConditionalOperator_TopLevelNullability() { @@ -71606,6 +72104,8 @@ static void F(object? x, bool b) _ = (default, default)/*T:!*/.Item1.ToString(); _ = (x, default)/*T:!*/.Item2.ToString(); (b switch { _ => null }).ToString(); + (b ? null : null).ToString(); + (new()).ToString(); } }"; var comp = CreateCompilation(source, options: WithNonNullTypesTrue()); @@ -71621,7 +72121,13 @@ static void F(object? x, bool b) Diagnostic(ErrorCode.ERR_DefaultLiteralNoTargetType, "default").WithLocation(6, 17), // (7,12): error CS8506: No best type was found for the switch expression. // (b switch { _ => null }).ToString(); - Diagnostic(ErrorCode.ERR_SwitchExpressionNoBestType, "switch").WithLocation(7, 12) + Diagnostic(ErrorCode.ERR_SwitchExpressionNoBestType, "switch").WithLocation(7, 12), + // (8,10): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between '' and '' + // (b ? null : null).ToString(); + Diagnostic(ErrorCode.ERR_InvalidQM, "b ? null : null").WithArguments("", "").WithLocation(8, 10), + // (9,10): error CS8754: There is no target type for 'new()' + // (new()).ToString(); + Diagnostic(ErrorCode.ERR_TypelessNewNoTargetType, "new()").WithArguments("new()").WithLocation(9, 10) ); comp.VerifyTypes(); } @@ -137155,7 +137661,7 @@ static void F(Func f) { F(() => { - if (b) return default; // 1 + if (b) return default; return t; }); } @@ -137163,19 +137669,23 @@ static void F(Func f) { F(() => { - if (b) return default; // 2 + if (b) return default; return t; }); } }"; var comp = CreateCompilation(source); - comp.VerifyEmitDiagnostics( - // (12,27): warning CS8603: Possible null reference return. - // if (b) return default; // 1 - Diagnostic(ErrorCode.WRN_NullReferenceReturn, "default").WithLocation(12, 27), - // (20,27): warning CS8603: Possible null reference return. - // if (b) return default; // 2 - Diagnostic(ErrorCode.WRN_NullReferenceReturn, "default").WithLocation(20, 27)); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree, ignoreAccessibility: false); + + var invocationNode = tree.GetRoot().DescendantNodes().OfType().First(); + Assert.Equal("void Program.F(System.Func f)", model.GetSymbolInfo(invocationNode).Symbol.ToTestDisplayString()); + + var invocationNode2 = tree.GetRoot().DescendantNodes().OfType().Last(); + Assert.Equal("void Program.F(System.Func f)", model.GetSymbolInfo(invocationNode2).Symbol.ToTestDisplayString()); + + comp.VerifyEmitDiagnostics(); } [Fact] diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/NullablePublicAPITests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/NullablePublicAPITests.cs index 8b885069b1785ca5793d5a3551adcdcea4d8c3f4..3fb33bdd57d61ccf859c5fcda9f78e5944b418b1 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/NullablePublicAPITests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/NullablePublicAPITests.cs @@ -1268,7 +1268,7 @@ void M() Assert.Equal(notNull, leftInfo.Nullability); Assert.Equal(notNull, leftInfo.ConvertedNullability); Assert.Equal(@null, rightInfo.Nullability); - Assert.Equal(notNull, rightInfo.ConvertedNullability); + Assert.Equal(@null, rightInfo.ConvertedNullability); } [Fact]