提交 545d7b48 编写于 作者: J Jason Malinowski

Enhance type inferrence of reference types before a ??

If we have inferred the thing to the left of a ?? is a reference type,
we can annotate it; we didn't update that yet in the type inferrer
though until we had the ability to properly remove the ? if the
user wasn't in the right context. The parent commit of this commit fixes
that so we can fix this now.
上级 4a3d95ac
......@@ -154,18 +154,7 @@ public async Task TestCoalesce3(TestMode mode)
public async Task TestCoalesce4()
{
await TestInMethodAsync(
@"var q = [|Goo()|] ?? string.Empty;", "global::System.String", TestMode.Node);
}
// This is skipped for now. This is a case where we know we can unilaterally mark the reference type as nullable, as long as the user has #nullable enable on.
// But right now there's no compiler API to know if it is, so we have to skip this. Once there is an API, we'll have it always return a nullable reference type
// and we'll remove the ? if it's in a non-nullable context no differently than we always generate types fully qualified and then clean up based on context.
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/37178"), Trait(Traits.Feature, Traits.Features.TypeInferenceService)]
public async Task TestCoalesceInNullableEnabled()
{
await TestInMethodAsync(
@"#nullable enable
var q = [|Goo()|] ?? string.Empty;", "global::System.String?", TestMode.Node);
@"var q = [|Goo()|] ?? string.Empty;", "global::System.String?", TestMode.Node);
}
[Theory, CombinatorialData, Trait(Traits.Feature, Traits.Features.TypeInferenceService)]
......@@ -2805,10 +2794,12 @@ public async Task TestNullCoalescingOperator1(TestMode mode)
{
void M()
{
object z = [|a|]?? null;
object z = [|a|] ?? null;
}
}";
await TestAsync(text, "global::System.Object", mode);
// In position mode, we are inferring that the thing to the right is an object, because it's being assigned to a local of type object.
// In node mode, we are inferring the node is an object? because it's to the left of the ??.
await TestAsync(text, mode == TestMode.Node ? "global::System.Object?" : "global::System.Object", mode);
}
[Theory, CombinatorialData, Trait(Traits.Feature, Traits.Features.TypeInferenceService)]
......@@ -2823,7 +2814,9 @@ void M()
object z = [|a|] ?? b ?? c;
}
}";
await TestAsync(text, "global::System.Object", mode);
// In position mode, we are inferring that the thing to the right is an object, because it's being assigned to a local of type object.
// In node mode, we are inferring the node is an object? because it's to the left of the ??.
await TestAsync(text, mode == TestMode.Node ? "global::System.Object?" : "global::System.Object", mode);
}
[Theory, CombinatorialData, Trait(Traits.Feature, Traits.Features.TypeInferenceService)]
......@@ -2838,7 +2831,10 @@ void M()
object z = a ?? [|b|] ?? c;
}
}";
await TestAsync(text, "global::System.Object", mode);
// In position mode, we are inferring that the thing to the right is an object, because it's to the right of the first ??
// and thus must be the same type as the object being assigned to.
// In node mode, we are inferring the node is an object? because it's to the left of the ??.
await TestAsync(text, mode == TestMode.Node ? "global::System.Object?" : "global::System.Object", mode);
}
[Fact, Trait(Traits.Feature, Traits.Features.TypeInferenceService)]
......
......@@ -1080,15 +1080,27 @@ private IEnumerable<TypeInferenceInfo> InferTypeInCatchFilterClause(CatchFilterC
var rightTypes = GetTypes(coalesceExpression.Right);
if (!rightTypes.Any())
{
return CreateResult(SpecialType.System_Object);
return CreateResult(SpecialType.System_Object, NullableAnnotation.Annotated);
}
// TODO: mark the reference case with a ? once we can check if we're within a nullable context
// (tracked by https://github.com/dotnet/roslyn/issues/36101)
return rightTypes
.Select(x => x.InferredType.IsValueType
? new TypeInferenceInfo(this.Compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(x.InferredType.WithoutNullability())) // Goo() ?? 0
: x); // Goo() ?? ""
.Select(x => new TypeInferenceInfo(MakeNullable(x.InferredType, this.Compilation))); // Goo() ?? ""
static ITypeSymbol MakeNullable(ITypeSymbol symbol, Compilation compilation)
{
if (symbol.IsValueType)
{
return compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(symbol.WithoutNullability());
}
else if (symbol.IsReferenceType)
{
return symbol.WithNullability(NullableAnnotation.Annotated);
}
else // it's neither a value nor reference type, so is an unconstrained generic
{
return symbol;
}
}
}
private IEnumerable<TypeInferenceInfo> InferTypeInConditionalAccessExpression(ConditionalAccessExpressionSyntax expression)
......
......@@ -76,8 +76,8 @@ private ImmutableArray<TypeInferenceInfo> Filter(IEnumerable<TypeInferenceInfo>
.ToImmutableArray();
}
protected IEnumerable<TypeInferenceInfo> CreateResult(SpecialType type)
=> CreateResult(Compilation.GetSpecialType(type));
protected IEnumerable<TypeInferenceInfo> CreateResult(SpecialType type, NullableAnnotation nullableAnnotation = NullableAnnotation.None)
=> CreateResult(Compilation.GetSpecialType(type).WithNullability(nullableAnnotation));
protected IEnumerable<TypeInferenceInfo> CreateResult(ITypeSymbol type)
=> type == null
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册