未验证 提交 e4ade656 编写于 作者: C Charles Stoner 提交者: GitHub

Report nested nullability warnings for arguments to user-defined conversions (#31771)

上级 e98a9832
......@@ -79,7 +79,7 @@ internal sealed class VariableState
/// <summary>
/// Types from return expressions. Used when inferring lambda return type in MethodTypeInferrer.
/// </summary>
private readonly ArrayBuilder<(RefKind, TypeSymbolWithAnnotations)> _returnTypes;
private readonly ArrayBuilder<(RefKind, TypeSymbolWithAnnotations)> _returnTypesOpt;
/// <summary>
/// An optional callback for callers to receive notification of the inferred type and nullability
......@@ -100,7 +100,7 @@ internal sealed class VariableState
/// <summary>
/// Instances being constructed.
/// </summary>
private PooledDictionary<BoundExpression, ObjectCreationPlaceholderLocal> _placeholderLocals;
private PooledDictionary<BoundExpression, ObjectCreationPlaceholderLocal> _placeholderLocalsOpt;
/// <summary>
/// For methods with annotations, we'll need to visit the arguments twice.
......@@ -117,7 +117,7 @@ internal sealed class VariableState
protected override void Free()
{
_variableTypes.Free();
_placeholderLocals?.Free();
_placeholderLocalsOpt?.Free();
base.Free();
}
......@@ -128,7 +128,7 @@ protected override void Free()
bool useMethodSignatureParameterTypes,
MethodSymbol methodSignatureOpt,
BoundNode node,
ArrayBuilder<(RefKind, TypeSymbolWithAnnotations)> returnTypes,
ArrayBuilder<(RefKind, TypeSymbolWithAnnotations)> returnTypesOpt,
VariableState initialState,
Action<BoundExpression, TypeSymbolWithAnnotations> callbackOpt)
: base(compilation, method, node, new EmptyStructTypeCache(compilation, dev12CompilerCompatibility: false), trackUnassignments: true)
......@@ -140,7 +140,7 @@ protected override void Free()
_useMethodSignatureReturnType = (object)methodSignatureOpt != null && useMethodSignatureReturnType;
_useMethodSignatureParameterTypes = (object)methodSignatureOpt != null && useMethodSignatureParameterTypes;
_methodSignatureOpt = methodSignatureOpt;
_returnTypes = returnTypes;
_returnTypesOpt = returnTypesOpt;
if (initialState != null)
{
var variableBySlot = initialState.VariableBySlot;
......@@ -168,9 +168,9 @@ protected override bool ConvertInsufficientExecutionStackExceptionToCancelledByS
protected override ImmutableArray<PendingBranch> Scan(ref bool badRegion)
{
if (_returnTypes != null)
if (_returnTypesOpt != null)
{
_returnTypes.Clear();
_returnTypesOpt.Clear();
}
this.Diagnostics.Clear();
ParameterSymbol methodThisParameter = MethodThisParameter;
......@@ -439,7 +439,7 @@ protected override int MakeSlot(BoundExpression node)
case BoundKind.ObjectCreationExpression:
case BoundKind.DynamicObjectCreationExpression:
case BoundKind.AnonymousObjectCreationExpression:
if (_placeholderLocals != null && _placeholderLocals.TryGetValue(node, out ObjectCreationPlaceholderLocal placeholder))
if (_placeholderLocalsOpt != null && _placeholderLocalsOpt.TryGetValue(node, out ObjectCreationPlaceholderLocal placeholder))
{
return GetOrCreateSlot(placeholder);
}
......@@ -534,6 +534,8 @@ private enum AssignmentKind
/// </summary>
private bool ReportNullableAssignmentIfNecessary(BoundExpression value, TypeSymbolWithAnnotations targetType, TypeSymbolWithAnnotations valueType, bool useLegacyWarnings, AssignmentKind assignmentKind = AssignmentKind.Assignment, Symbol target = null)
{
Debug.Assert((object)target != null || assignmentKind != AssignmentKind.Argument);
if (value == null)
{
return false;
......@@ -554,7 +556,7 @@ private bool ReportNullableAssignmentIfNecessary(BoundExpression value, TypeSymb
return false;
}
if (reportNullLiteralAssignmentIfNecessary())
if (reportNullLiteralAssignmentIfNecessary(value))
{
return true;
}
......@@ -583,20 +585,20 @@ private bool ReportNullableAssignmentIfNecessary(BoundExpression value, TypeSymb
// Report warning converting null literal to non-nullable reference type.
// target (e.g.: `object x = null;` or calling `void F(object y)` with `F(null)`).
bool reportNullLiteralAssignmentIfNecessary()
bool reportNullLiteralAssignmentIfNecessary(BoundExpression expr)
{
if (value.ConstantValue?.IsNull != true && !isDefaultOfUnconstrainedTypeParameter(value))
if (expr.ConstantValue?.IsNull != true && !isDefaultOfUnconstrainedTypeParameter(expr))
{
return false;
}
if (useLegacyWarnings)
{
ReportWWarning(value.Syntax);
ReportWWarning(expr.Syntax);
}
else
{
ReportDiagnostic(assignmentKind == AssignmentKind.Return ? ErrorCode.WRN_NullReferenceReturn : ErrorCode.WRN_NullAsNonNullable, value.Syntax);
ReportDiagnostic(assignmentKind == AssignmentKind.Return ? ErrorCode.WRN_NullReferenceReturn : ErrorCode.WRN_NullAsNonNullable, expr.Syntax);
}
return true;
}
......@@ -1019,11 +1021,11 @@ protected override BoundNode VisitReturnStatementNoAdjust(BoundReturnStatement n
return null;
}
if (_returnTypes != null)
if (_returnTypesOpt != null)
{
// Inferring return type. Should not convert to method return type.
TypeSymbolWithAnnotations result = VisitRvalueWithResult(expr);
_returnTypes.Add((node.RefKind, result));
_returnTypesOpt.Add((node.RefKind, result));
return null;
}
......@@ -1256,20 +1258,20 @@ private void SetResult(BoundExpression node)
private ObjectCreationPlaceholderLocal GetOrCreateObjectCreationPlaceholder(BoundExpression node)
{
ObjectCreationPlaceholderLocal placeholder;
if (_placeholderLocals == null)
if (_placeholderLocalsOpt == null)
{
_placeholderLocals = PooledDictionary<BoundExpression, ObjectCreationPlaceholderLocal>.GetInstance();
_placeholderLocalsOpt = PooledDictionary<BoundExpression, ObjectCreationPlaceholderLocal>.GetInstance();
placeholder = null;
}
else
{
_placeholderLocals.TryGetValue(node, out placeholder);
_placeholderLocalsOpt.TryGetValue(node, out placeholder);
}
if ((object)placeholder == null)
{
placeholder = new ObjectCreationPlaceholderLocal(_symbol, node);
_placeholderLocals.Add(node, placeholder);
_placeholderLocalsOpt.Add(node, placeholder);
}
return placeholder;
......@@ -3055,10 +3057,7 @@ private static Symbol AsMemberOfResultType(TypeSymbolWithAnnotations resultType,
private static Symbol AsMemberOfType(NamedTypeSymbol containingType, Symbol symbol)
{
if (symbol is null)
{
return null;
}
Debug.Assert((object)symbol != null);
if (symbol.Kind == SymbolKind.Method)
{
if (((MethodSymbol)symbol).MethodKind == MethodKind.LocalFunction)
......@@ -3330,6 +3329,7 @@ private bool HasTopLevelNullabilityConversion(TypeSymbolWithAnnotations source,
Debug.Assert(node != null);
Debug.Assert(operandOpt != null || !operandType.IsNull);
Debug.Assert(!targetTypeWithNullability.IsNull);
Debug.Assert((object)target != null || assignmentKind != AssignmentKind.Argument);
NullableAnnotation resultAnnotation = NullableAnnotation.Unknown;
bool forceOperandAnnotationForResult = false;
......@@ -3385,10 +3385,11 @@ private bool HasTopLevelNullabilityConversion(TypeSymbolWithAnnotations source,
conversion.UserDefinedFromConversion,
TypeSymbolWithAnnotations.Create(conversion.BestUserDefinedConversionAnalysis.FromType),
operandType,
checkConversion: false,
checkConversion: true,
fromExplicitCast: false,
useLegacyWarnings,
assignmentKind);
assignmentKind,
target);
// Update method based on operandType: see https://github.com/dotnet/roslyn/issues/29605.
// (see NullableReferenceTypesTests.ImplicitConversions_07).
......@@ -3665,6 +3666,7 @@ private bool HasTopLevelNullabilityConversion(TypeSymbolWithAnnotations source,
private TypeSymbolWithAnnotations ClassifyAndApplyConversion(BoundExpression node, TypeSymbolWithAnnotations targetType, TypeSymbolWithAnnotations operandType, bool useLegacyWarnings, AssignmentKind assignmentKind, ParameterSymbol target)
{
Debug.Assert((object)target != null || assignmentKind != AssignmentKind.Argument);
HashSet<DiagnosticInfo> useSiteDiagnostics = null;
var conversion = _conversions.ClassifyStandardConversion(null, operandType.TypeSymbol, targetType.TypeSymbol, ref useSiteDiagnostics);
if (!conversion.Exists)
......@@ -4728,7 +4730,7 @@ public override BoundNode VisitEventAssignmentOperator(BoundEventAssignmentOpera
Debug.Assert(!IsConditionalState);
var receiverOpt = node.ReceiverOpt;
var @event = node.Event;
if (!node.Event.IsStatic)
if (!@event.IsStatic)
{
@event = (EventSymbol)AsMemberOfResultType(_resultType, @event);
// https://github.com/dotnet/roslyn/issues/30598: Mark receiver as not null
......
......@@ -65747,5 +65747,66 @@ static void F3(int? z, int? w)
var comp = CreateCompilationWithIndexAndRange(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics();
}
[WorkItem(31770, "https://github.com/dotnet/roslyn/issues/31770")]
[Fact]
public void UserDefinedConversion_NestedNullability_01()
{
var source =
@"class A<T> { }
class B
{
public static implicit operator B(A<object> a) => throw null;
}
class Program
{
static void F(B b) { }
static void Main()
{
A<object?> a = new A<object?>();
B b = a; // 1
F(a); // 2
}
}";
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
// https://github.com/dotnet/roslyn/issues/31798: Consider improving warning to reference user-defined operator.
comp.VerifyDiagnostics(
// (12,15): warning CS8619: Nullability of reference types in value of type 'A<object?>' doesn't match target type 'A<object>'.
// B b = a; // 1
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "a").WithArguments("A<object?>", "A<object>").WithLocation(12, 15),
// (13,11): warning CS8620: Nullability of reference types in argument of type 'A<object?>' doesn't match target type 'A<object>' for parameter 'b' in 'void Program.F(B b)'.
// F(a); // 2
Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "a").WithArguments("A<object?>", "A<object>", "b", "void Program.F(B b)").WithLocation(13, 11));
}
[Fact]
public void UserDefinedConversion_NestedNullability_02()
{
var source =
@"class A<T> { }
class B
{
public static implicit operator A<object>(B b) => throw null;
}
class Program
{
static void F(A<object?> a) { }
static void Main()
{
B b = new B();
A<object?> a = b; // 1
F(b); // 2
}
}";
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
// https://github.com/dotnet/roslyn/issues/31798: Consider improving warning to reference user-defined operator.
comp.VerifyDiagnostics(
// (12,24): warning CS8619: Nullability of reference types in value of type 'A<object>' doesn't match target type 'A<object?>'.
// A<object?> a = b; // 1
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "b").WithArguments("A<object>", "A<object?>").WithLocation(12, 24),
// (13,11): warning CS8620: Nullability of reference types in argument of type 'A<object>' doesn't match target type 'A<object?>' for parameter 'a' in 'void Program.F(A<object?> a)'.
// F(b); // 2
Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "b").WithArguments("A<object>", "A<object?>", "a", "void Program.F(A<object?> a)").WithLocation(13, 11));
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册