未验证 提交 87f4c9df 编写于 作者: J Julien Couvreur 提交者: GitHub

Typeless expressions should contribute nullability to lambda return (#47581)

上级 1d69a1dc
......@@ -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))
......
......@@ -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<TypeWithAnnotations> 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);
}
......
......@@ -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;
}
......
......@@ -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();
......
......@@ -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())
{
......
......@@ -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<T>(System.Func<T> 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<LiteralExpressionSyntax>().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<InvocationExpressionSyntax>().Single();
Assert.Equal("void Program.M<System.Object?>(System.Func<System.Object?> 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<LiteralExpressionSyntax>().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<ImplicitArrayCreationExpressionSyntax>().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<T>(System.Func<T> t) { }
static void F<U>(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<LiteralExpressionSyntax>().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<InvocationExpressionSyntax>().Single();
Assert.Equal("void Program.M<U?>(System.Func<U?> 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<T>(System.Func<T> t) { }
static void F<U>(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<LiteralExpressionSyntax>().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<InvocationExpressionSyntax>().Single();
Assert.Equal("void Program.M<U>(System.Func<U> 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<T>(System.Func<T> 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<InvocationExpressionSyntax>().Single();
Assert.Equal("void Program.M<System.Object?>(System.Func<System.Object?> 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<T>(System.Func<T> 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<InvocationExpressionSyntax>().Single();
Assert.Equal("void Program.M<System.Object>(System.Func<System.Object> 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<T>(System.Func<T> 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<LiteralExpressionSyntax>().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<InvocationExpressionSyntax>().Single();
Assert.Equal("void Program.M<System.Object?>(System.Func<System.Object?> 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<T>(System.Func<T> 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<InvocationExpressionSyntax>().Single();
Assert.Equal("void Program.M<System.Object?>(System.Func<System.Object?> 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<T>(System.Func<T> 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<InvocationExpressionSyntax>().Single();
Assert.Equal("void Program.M<System.Object>(System.Func<System.Object> 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<T>(System.Func<T> 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<ImplicitObjectCreationExpressionSyntax>().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<InvocationExpressionSyntax>().Single();
Assert.Equal("void Program.M<System.Object>(System.Func<System.Object> 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<T>(System.Func<T> 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<ImplicitObjectCreationExpressionSyntax>().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<InvocationExpressionSyntax>().Single();
Assert.Equal("void Program.M<Program>(System.Func<Program> 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<T>(System.Func<T> 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<LiteralExpressionSyntax>().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<InvocationExpressionSyntax>().Single();
Assert.Equal("void Program.M<System.Object?>(System.Func<System.Object?> 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<T1, T2>
{
public void M1(bool b)
{
var map = new C<string, string>();
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<K, V>(this C<K, V> dictionary, K key, System.Func<K, V> 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<string, string>' cannot be used for parameter 'dictionary' of type 'C<string, string?>' in 'string? Extensions.GetOrAdd<string, string?>(C<string, string?> dictionary, string key, Func<string, string?> 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<string, string>", "C<string, string?>", "dictionary", "string? Extensions.GetOrAdd<string, string?>(C<string, string?> dictionary, string key, Func<string, string?> function)").WithLocation(13, 9),
// (14,9): warning CS8620: Argument of type 'C<string, string>' cannot be used for parameter 'dictionary' of type 'C<string, string?>' in 'string? Extensions.GetOrAdd<string, string?>(C<string, string?> dictionary, string key, Func<string, string?> 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<string, string>", "C<string, string?>", "dictionary", "string? Extensions.GetOrAdd<string, string?>(C<string, string?> dictionary, string key, Func<string, string?> function)").WithLocation(14, 9),
// (15,9): warning CS8620: Argument of type 'C<string, string>' cannot be used for parameter 'dictionary' of type 'C<string, string?>' in 'string? Extensions.GetOrAdd<string, string?>(C<string, string?> dictionary, string key, Func<string, string?> 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<string, string>", "C<string, string?>", "dictionary", "string? Extensions.GetOrAdd<string, string?>(C<string, string?> dictionary, string key, Func<string, string?> 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>(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<InvocationExpressionSyntax>().First();
Assert.Equal("System.String? C.Infer<System.String?>(System.String? t1, System.String? t2)", model.GetSymbolInfo(invocationNode).Symbol.ToTestDisplayString());
var invocationNode2 = tree.GetRoot().DescendantNodes().OfType<InvocationExpressionSyntax>().Last();
Assert.Equal("System.String C.Infer<System.String>(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<string>, 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<T>(System.Func<T> 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<LambdaExpressionSyntax>().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<InvocationExpressionSyntax>().Single();
Assert.Equal("void Program.M<System.Action>(System.Func<System.Action> 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:<null>!*/.Item1.ToString();
_ = (x, default)/*T:<null>!*/.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 '<null>' and '<null>'
// (b ? null : null).ToString();
Diagnostic(ErrorCode.ERR_InvalidQM, "b ? null : null").WithArguments("<null>", "<null>").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<T>(Func<T> f)
{
F(() =>
{
if (b) return default; // 1
if (b) return default;
return t;
});
}
......@@ -137163,19 +137669,23 @@ static void F<T>(Func<T> 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<InvocationExpressionSyntax>().First();
Assert.Equal("void Program.F<T?>(System.Func<T?> f)", model.GetSymbolInfo(invocationNode).Symbol.ToTestDisplayString());
var invocationNode2 = tree.GetRoot().DescendantNodes().OfType<InvocationExpressionSyntax>().Last();
Assert.Equal("void Program.F<T?>(System.Func<T?> f)", model.GetSymbolInfo(invocationNode2).Symbol.ToTestDisplayString());
comp.VerifyEmitDiagnostics();
}
[Fact]
......@@ -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]
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册