Refactor ApplyConversion into VisitConversion

Refactors out ApplyConversion into a more aptly named VisitConversion. Adjusts the parameters to take the node being converted, and optionally the original boundconversion. When the boundconversion is passed it, the calculated nullability is now tracked through nested levels of conversions between the top-level boundconversion and the underlying converted node. Removed TrackInferredTypesThroughConversions, as this is now being handled by VisitConversion.

This PR additionally fixes a conversion issue where user-defined conversions that added nullability were not then marking the result type of that conversion as nullable.
上级 fd70a2aa
......@@ -191,7 +191,7 @@ private BoundExpression CreateUserDefinedConversion(SyntaxNode syntax, BoundExpr
conversion: conversion.UserDefinedFromConversion,
isCast: false,
conversionGroup,
wasCompilerGenerated: true,
wasCompilerGenerated: false,
destination: conversion.BestUserDefinedConversionAnalysis.FromType,
diagnostics: diagnostics);
......
......@@ -498,7 +498,6 @@ public override BoundNode VisitSwitchExpression(BoundSwitchExpression node)
conversions.Add(conversion);
var armType = VisitRvalueWithState(expression);
resultTypes.Add(armType);
TrackInferredTypesThroughConversions(arm.Value, expression, _visitResult);
Join(ref endState, ref this.State);
// Build placeholders for inference in order to preserve annotations.
......@@ -516,7 +515,7 @@ public override BoundNode VisitSwitchExpression(BoundSwitchExpression node)
for (int i = 0; i < numSwitchArms; i++)
{
var expression = expressions[i];
resultTypes[i] = ApplyConversion(expression, expression, conversions[i], inferredTypeWithAnnotations, resultTypes[i], checkConversion: true,
resultTypes[i] = VisitConversion(conversionOpt: null, expression, conversions[i], inferredTypeWithAnnotations, resultTypes[i], checkConversion: true,
fromExplicitCast: false, useLegacyWarnings: false, AssignmentKind.Assignment, reportRemainingWarnings: true, reportTopLevelWarnings: false);
}
......@@ -527,8 +526,9 @@ public override BoundNode VisitSwitchExpression(BoundSwitchExpression node)
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
_ = ApplyConversion(nodeForSyntax, operandOpt: nodeForSyntax, conversions[i], targetTypeWithNullability: inferredTypeWithAnnotations, operandType: resultTypes[i],
_ = VisitConversion(conversionOpt, convertedNode: nodeForSyntax, conversions[i], targetTypeWithNullability: inferredTypeWithAnnotations, operandType: resultTypes[i],
checkConversion: true, fromExplicitCast: false, useLegacyWarnings: false, AssignmentKind.Assignment, reportRemainingWarnings: false, reportTopLevelWarnings: true);
}
......
......@@ -49186,6 +49186,31 @@ static void F4(int? x4)
Diagnostic(ErrorCode.WRN_NullableValueTypeMayBeNull, "(long)x3").WithLocation(14, 19));
}
[Fact]
[WorkItem(35334, "https://github.com/dotnet/roslyn/issues/35334")]
public void Conversions_ExplicitNullable_UserDefinedIntroducingNullablility()
{
var source =
@"
class A { public static explicit operator B(A a) => throw null!; }
class B
{
void M(A a)
{
var b = ((B?)a)/*T:B?*/;
b.ToString(); // 1
}
}
";
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyTypes();
comp.VerifyDiagnostics(
// (8,9): warning CS8602: Dereference of a possibly null reference.
// b.ToString(); // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "b").WithLocation(8, 9));
}
[Fact]
[WorkItem(32531, "https://github.com/dotnet/roslyn/issues/32531")]
public void Tuple_Conversions_ImplicitNullable_01()
......@@ -51369,6 +51394,7 @@ static void F6(IOut<object> x6, IOut<object?> y6)
}
[Fact]
[WorkItem(35334, "https://github.com/dotnet/roslyn/issues/35334")]
public void ExplicitCast_UserDefined_01()
{
var source =
......@@ -51411,17 +51437,17 @@ static void F3(A3? x3, A3 y3)
{
B? b;
b = ((B)x3)/*T:B!*/;
b = ((B?)x3)/*T:B!*/;
b = ((B?)x3)/*T:B?*/;
b = ((B)y3)/*T:B!*/;
b = ((B?)y3)/*T:B!*/;
b = ((B?)y3)/*T:B?*/;
}
static void F4(A4? x4, A4 y4)
{
B? b;
b = ((B)x4)/*T:B!*/;
b = ((B?)x4)/*T:B!*/;
b = ((B?)x4)/*T:B?*/;
b = ((B)y4)/*T:B!*/;
b = ((B?)y4)/*T:B!*/;
b = ((B?)y4)/*T:B?*/;
}
}";
var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue());
......@@ -56728,8 +56754,8 @@ static void F1((int, int) t1)
static void F2((int, int) t2)
{
var u2 = ((B?, A))t2; // 3
u2.Item1.ToString();
u2.Item2.ToString(); // 4
u2.Item1.ToString(); // 4
u2.Item2.ToString(); // 5
}
}";
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
......@@ -56738,14 +56764,17 @@ static void F2((int, int) t2)
// (13,22): warning CS8601: Possible null reference assignment.
// (A, B?) u1 = t1; // 1
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "t1").WithLocation(13, 22),
// (14,9): warning CS8602: Possible dereference of a null reference.
// (14,9): warning CS8602: Dereference of a possibly null reference.
// u1.Item1.ToString(); // 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "u1.Item1").WithLocation(14, 9),
// (19,18): warning CS8601: Possible null reference assignment.
// var u2 = ((B?, A))t2; // 3
Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "((B?, A))t2").WithLocation(19, 18),
// (21,9): warning CS8602: Possible dereference of a null reference.
// u2.Item2.ToString(); // 4
// (20,9): warning CS8602: Dereference of a possibly null reference.
// u2.Item1.ToString(); // 4
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "u2.Item1").WithLocation(20, 9),
// (21,9): warning CS8602: Dereference of a possibly null reference.
// u2.Item2.ToString(); // 5
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "u2.Item2").WithLocation(21, 9));
}
......@@ -86245,6 +86274,7 @@ static void F2(S? ns)
}
[Fact]
[WorkItem(35334, "https://github.com/dotnet/roslyn/issues/35334")]
public void NullableT_StructToClass()
{
var source =
......@@ -86271,26 +86301,29 @@ static void F2(S? ns)
if (ns.HasValue)
{
var c1 = (C?)ns;
_ = c1.ToString();
_ = c1.ToString(); // 1
C? c2 = ns;
_ = c2.ToString();
}
else
{
var c3 = (C?)ns;
_ = c3.ToString(); // 1
_ = c3.ToString(); // 2
C? c4 = ns;
_ = c4.ToString(); // 2
_ = c4.ToString(); // 3
}
}
}";
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(
// (24,17): warning CS8602: Dereference of a possibly null reference.
// _ = c1.ToString(); // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c1").WithLocation(24, 17),
// (31,17): warning CS8602: Dereference of a possibly null reference.
// _ = c3.ToString(); // 1
// _ = c3.ToString(); // 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c3").WithLocation(31, 17),
// (33,17): warning CS8602: Dereference of a possibly null reference.
// _ = c4.ToString(); // 2
// _ = c4.ToString(); // 3
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c4").WithLocation(33, 17));
}
......@@ -86357,6 +86390,7 @@ static void F2(S? ns)
}
[Fact]
[WorkItem(35334, "https://github.com/dotnet/roslyn/issues/35334")]
public void NullableT_NullableStructToClass()
{
var source =
......@@ -86383,21 +86417,27 @@ static void F2(S? ns)
if (ns.HasValue)
{
var c1 = (C?)ns;
_ = c1.ToString();
_ = c1.ToString(); // 1
C? c2 = ns;
_ = c2.ToString();
}
else
{
var c3 = (C?)ns;
_ = c3.ToString();
_ = c3.ToString(); // 2
C? c4 = ns;
_ = c4.ToString();
}
}
}";
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics();
comp.VerifyDiagnostics(
// (24,17): warning CS8602: Dereference of a possibly null reference.
// _ = c1.ToString(); // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c1").WithLocation(24, 17),
// (31,17): warning CS8602: Dereference of a possibly null reference.
// _ = c3.ToString(); // 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "c3").WithLocation(31, 17));
}
[Fact]
......@@ -86790,6 +86830,7 @@ static void F2(S<T>? ns)
}
[Fact]
[WorkItem(35334, "https://github.com/dotnet/roslyn/issues/35334")]
public void NullableT_StructToTypeParameterClassConstraint()
{
var source =
......@@ -86813,30 +86854,34 @@ static void F2(S<T>? ns)
if (ns.HasValue)
{
var t1 = (T?)ns;
_ = t1.ToString();
_ = t1.ToString(); // 1
T? t2 = ns;
_ = t2.ToString();
}
else
{
var t3 = (T?)ns;
_ = t3.ToString(); // 1
_ = t3.ToString(); // 2
T? t4 = ns;
_ = t4.ToString(); // 2
_ = t4.ToString(); // 3
}
}
}";
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(
// (21,17): warning CS8602: Dereference of a possibly null reference.
// _ = t1.ToString(); // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "t1").WithLocation(21, 17),
// (28,17): warning CS8602: Dereference of a possibly null reference.
// _ = t3.ToString(); // 1
// _ = t3.ToString(); // 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "t3").WithLocation(28, 17),
// (30,17): warning CS8602: Dereference of a possibly null reference.
// _ = t4.ToString(); // 2
// _ = t4.ToString(); // 3
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "t4").WithLocation(30, 17));
}
[Fact]
[WorkItem(35334, "https://github.com/dotnet/roslyn/issues/35334")]
public void NullableT_NullableStructToTypeParameterClassConstraint()
{
var source =
......@@ -86860,21 +86905,27 @@ static void F2(S<T>? ns)
if (ns.HasValue)
{
var t1 = (T?)ns;
_ = t1.ToString();
_ = t1.ToString(); // 1
T? t2 = ns;
_ = t2.ToString();
}
else
{
var t3 = (T?)ns;
_ = t3.ToString();
_ = t3.ToString(); // 2
T? t4 = ns;
_ = t4.ToString();
}
}
}";
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics();
comp.VerifyDiagnostics(
// (21,17): warning CS8602: Dereference of a possibly null reference.
// _ = t1.ToString(); // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "t1").WithLocation(21, 17),
// (28,17): warning CS8602: Dereference of a possibly null reference.
// _ = t3.ToString(); // 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "t3").WithLocation(28, 17));
}
[Fact]
......@@ -727,5 +727,61 @@ public override void Initialize(AnalysisContext context)
}, SyntaxKind.IdentifierName);
}
}
[Fact]
public void MultipleConversions()
{
var source = @"
class A { public static explicit operator C(A a) => new D(); }
class B : A { }
class C { }
class D : C { }
class E
{
void M()
{
var d = (D)(C?)new B();
}
}";
var comp = CreateCompilation(source, options: WithNonNullTypesTrue(), parseOptions: TestOptions.Regular8WithNullableAnalysis);
comp.VerifyDiagnostics(
// (10,17): warning CS8600: Converting null literal or possible null value to non-nullable type.
// var d = (D)(C?)new B();
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "(D)(C?)new B()").WithLocation(10, 17));
var syntaxTree = comp.SyntaxTrees[0];
var root = syntaxTree.GetRoot();
var model = comp.GetSemanticModel(syntaxTree);
var aType = comp.GetTypeByMetadataName("A");
var bType = comp.GetTypeByMetadataName("B");
var cType = comp.GetTypeByMetadataName("C");
var dType = comp.GetTypeByMetadataName("D");
var nullable = new NullabilityInfo(PublicNullableAnnotation.Annotated, PublicNullableFlowState.MaybeNull);
var notNullable = new NullabilityInfo(PublicNullableAnnotation.NotAnnotated, PublicNullableFlowState.NotNull);
var dCast = (CastExpressionSyntax)root.DescendantNodes().OfType<EqualsValueClauseSyntax>().Single().Value;
var dInfo = model.GetTypeInfo(dCast);
Assert.Equal(dType, dInfo.Type);
Assert.Equal(dType, dInfo.ConvertedType);
Assert.Equal(nullable, dInfo.Nullability);
Assert.Equal(nullable, dInfo.ConvertedNullability);
var cCast = (CastExpressionSyntax)dCast.Expression;
var cInfo = model.GetTypeInfo(cCast);
Assert.Equal(cType, cInfo.Type);
Assert.Equal(cType, cInfo.ConvertedType);
Assert.Equal(nullable, cInfo.Nullability);
Assert.Equal(nullable, cInfo.ConvertedNullability);
var objectCreation = cCast.Expression;
var creationInfo = model.GetTypeInfo(objectCreation);
Assert.Equal(bType, creationInfo.Type);
Assert.Equal(aType, creationInfo.ConvertedType);
Assert.Equal(notNullable, creationInfo.Nullability);
Assert.Equal(nullable, creationInfo.ConvertedNullability);
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册