未验证 提交 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 ...@@ -79,7 +79,7 @@ internal sealed class VariableState
/// <summary> /// <summary>
/// Types from return expressions. Used when inferring lambda return type in MethodTypeInferrer. /// Types from return expressions. Used when inferring lambda return type in MethodTypeInferrer.
/// </summary> /// </summary>
private readonly ArrayBuilder<(RefKind, TypeSymbolWithAnnotations)> _returnTypes; private readonly ArrayBuilder<(RefKind, TypeSymbolWithAnnotations)> _returnTypesOpt;
/// <summary> /// <summary>
/// An optional callback for callers to receive notification of the inferred type and nullability /// An optional callback for callers to receive notification of the inferred type and nullability
...@@ -100,7 +100,7 @@ internal sealed class VariableState ...@@ -100,7 +100,7 @@ internal sealed class VariableState
/// <summary> /// <summary>
/// Instances being constructed. /// Instances being constructed.
/// </summary> /// </summary>
private PooledDictionary<BoundExpression, ObjectCreationPlaceholderLocal> _placeholderLocals; private PooledDictionary<BoundExpression, ObjectCreationPlaceholderLocal> _placeholderLocalsOpt;
/// <summary> /// <summary>
/// For methods with annotations, we'll need to visit the arguments twice. /// For methods with annotations, we'll need to visit the arguments twice.
...@@ -117,7 +117,7 @@ internal sealed class VariableState ...@@ -117,7 +117,7 @@ internal sealed class VariableState
protected override void Free() protected override void Free()
{ {
_variableTypes.Free(); _variableTypes.Free();
_placeholderLocals?.Free(); _placeholderLocalsOpt?.Free();
base.Free(); base.Free();
} }
...@@ -128,7 +128,7 @@ protected override void Free() ...@@ -128,7 +128,7 @@ protected override void Free()
bool useMethodSignatureParameterTypes, bool useMethodSignatureParameterTypes,
MethodSymbol methodSignatureOpt, MethodSymbol methodSignatureOpt,
BoundNode node, BoundNode node,
ArrayBuilder<(RefKind, TypeSymbolWithAnnotations)> returnTypes, ArrayBuilder<(RefKind, TypeSymbolWithAnnotations)> returnTypesOpt,
VariableState initialState, VariableState initialState,
Action<BoundExpression, TypeSymbolWithAnnotations> callbackOpt) Action<BoundExpression, TypeSymbolWithAnnotations> callbackOpt)
: base(compilation, method, node, new EmptyStructTypeCache(compilation, dev12CompilerCompatibility: false), trackUnassignments: true) : base(compilation, method, node, new EmptyStructTypeCache(compilation, dev12CompilerCompatibility: false), trackUnassignments: true)
...@@ -140,7 +140,7 @@ protected override void Free() ...@@ -140,7 +140,7 @@ protected override void Free()
_useMethodSignatureReturnType = (object)methodSignatureOpt != null && useMethodSignatureReturnType; _useMethodSignatureReturnType = (object)methodSignatureOpt != null && useMethodSignatureReturnType;
_useMethodSignatureParameterTypes = (object)methodSignatureOpt != null && useMethodSignatureParameterTypes; _useMethodSignatureParameterTypes = (object)methodSignatureOpt != null && useMethodSignatureParameterTypes;
_methodSignatureOpt = methodSignatureOpt; _methodSignatureOpt = methodSignatureOpt;
_returnTypes = returnTypes; _returnTypesOpt = returnTypesOpt;
if (initialState != null) if (initialState != null)
{ {
var variableBySlot = initialState.VariableBySlot; var variableBySlot = initialState.VariableBySlot;
...@@ -168,9 +168,9 @@ protected override bool ConvertInsufficientExecutionStackExceptionToCancelledByS ...@@ -168,9 +168,9 @@ protected override bool ConvertInsufficientExecutionStackExceptionToCancelledByS
protected override ImmutableArray<PendingBranch> Scan(ref bool badRegion) protected override ImmutableArray<PendingBranch> Scan(ref bool badRegion)
{ {
if (_returnTypes != null) if (_returnTypesOpt != null)
{ {
_returnTypes.Clear(); _returnTypesOpt.Clear();
} }
this.Diagnostics.Clear(); this.Diagnostics.Clear();
ParameterSymbol methodThisParameter = MethodThisParameter; ParameterSymbol methodThisParameter = MethodThisParameter;
...@@ -439,7 +439,7 @@ protected override int MakeSlot(BoundExpression node) ...@@ -439,7 +439,7 @@ protected override int MakeSlot(BoundExpression node)
case BoundKind.ObjectCreationExpression: case BoundKind.ObjectCreationExpression:
case BoundKind.DynamicObjectCreationExpression: case BoundKind.DynamicObjectCreationExpression:
case BoundKind.AnonymousObjectCreationExpression: case BoundKind.AnonymousObjectCreationExpression:
if (_placeholderLocals != null && _placeholderLocals.TryGetValue(node, out ObjectCreationPlaceholderLocal placeholder)) if (_placeholderLocalsOpt != null && _placeholderLocalsOpt.TryGetValue(node, out ObjectCreationPlaceholderLocal placeholder))
{ {
return GetOrCreateSlot(placeholder); return GetOrCreateSlot(placeholder);
} }
...@@ -534,6 +534,8 @@ private enum AssignmentKind ...@@ -534,6 +534,8 @@ private enum AssignmentKind
/// </summary> /// </summary>
private bool ReportNullableAssignmentIfNecessary(BoundExpression value, TypeSymbolWithAnnotations targetType, TypeSymbolWithAnnotations valueType, bool useLegacyWarnings, AssignmentKind assignmentKind = AssignmentKind.Assignment, Symbol target = null) 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) if (value == null)
{ {
return false; return false;
...@@ -554,7 +556,7 @@ private bool ReportNullableAssignmentIfNecessary(BoundExpression value, TypeSymb ...@@ -554,7 +556,7 @@ private bool ReportNullableAssignmentIfNecessary(BoundExpression value, TypeSymb
return false; return false;
} }
if (reportNullLiteralAssignmentIfNecessary()) if (reportNullLiteralAssignmentIfNecessary(value))
{ {
return true; return true;
} }
...@@ -583,20 +585,20 @@ private bool ReportNullableAssignmentIfNecessary(BoundExpression value, TypeSymb ...@@ -583,20 +585,20 @@ private bool ReportNullableAssignmentIfNecessary(BoundExpression value, TypeSymb
// Report warning converting null literal to non-nullable reference type. // 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)`). // 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; return false;
} }
if (useLegacyWarnings) if (useLegacyWarnings)
{ {
ReportWWarning(value.Syntax); ReportWWarning(expr.Syntax);
} }
else 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; return true;
} }
...@@ -1019,11 +1021,11 @@ protected override BoundNode VisitReturnStatementNoAdjust(BoundReturnStatement n ...@@ -1019,11 +1021,11 @@ protected override BoundNode VisitReturnStatementNoAdjust(BoundReturnStatement n
return null; return null;
} }
if (_returnTypes != null) if (_returnTypesOpt != null)
{ {
// Inferring return type. Should not convert to method return type. // Inferring return type. Should not convert to method return type.
TypeSymbolWithAnnotations result = VisitRvalueWithResult(expr); TypeSymbolWithAnnotations result = VisitRvalueWithResult(expr);
_returnTypes.Add((node.RefKind, result)); _returnTypesOpt.Add((node.RefKind, result));
return null; return null;
} }
...@@ -1256,20 +1258,20 @@ private void SetResult(BoundExpression node) ...@@ -1256,20 +1258,20 @@ private void SetResult(BoundExpression node)
private ObjectCreationPlaceholderLocal GetOrCreateObjectCreationPlaceholder(BoundExpression node) private ObjectCreationPlaceholderLocal GetOrCreateObjectCreationPlaceholder(BoundExpression node)
{ {
ObjectCreationPlaceholderLocal placeholder; ObjectCreationPlaceholderLocal placeholder;
if (_placeholderLocals == null) if (_placeholderLocalsOpt == null)
{ {
_placeholderLocals = PooledDictionary<BoundExpression, ObjectCreationPlaceholderLocal>.GetInstance(); _placeholderLocalsOpt = PooledDictionary<BoundExpression, ObjectCreationPlaceholderLocal>.GetInstance();
placeholder = null; placeholder = null;
} }
else else
{ {
_placeholderLocals.TryGetValue(node, out placeholder); _placeholderLocalsOpt.TryGetValue(node, out placeholder);
} }
if ((object)placeholder == null) if ((object)placeholder == null)
{ {
placeholder = new ObjectCreationPlaceholderLocal(_symbol, node); placeholder = new ObjectCreationPlaceholderLocal(_symbol, node);
_placeholderLocals.Add(node, placeholder); _placeholderLocalsOpt.Add(node, placeholder);
} }
return placeholder; return placeholder;
...@@ -3055,10 +3057,7 @@ private static Symbol AsMemberOfResultType(TypeSymbolWithAnnotations resultType, ...@@ -3055,10 +3057,7 @@ private static Symbol AsMemberOfResultType(TypeSymbolWithAnnotations resultType,
private static Symbol AsMemberOfType(NamedTypeSymbol containingType, Symbol symbol) private static Symbol AsMemberOfType(NamedTypeSymbol containingType, Symbol symbol)
{ {
if (symbol is null) Debug.Assert((object)symbol != null);
{
return null;
}
if (symbol.Kind == SymbolKind.Method) if (symbol.Kind == SymbolKind.Method)
{ {
if (((MethodSymbol)symbol).MethodKind == MethodKind.LocalFunction) if (((MethodSymbol)symbol).MethodKind == MethodKind.LocalFunction)
...@@ -3330,6 +3329,7 @@ private bool HasTopLevelNullabilityConversion(TypeSymbolWithAnnotations source, ...@@ -3330,6 +3329,7 @@ private bool HasTopLevelNullabilityConversion(TypeSymbolWithAnnotations source,
Debug.Assert(node != null); Debug.Assert(node != null);
Debug.Assert(operandOpt != null || !operandType.IsNull); Debug.Assert(operandOpt != null || !operandType.IsNull);
Debug.Assert(!targetTypeWithNullability.IsNull); Debug.Assert(!targetTypeWithNullability.IsNull);
Debug.Assert((object)target != null || assignmentKind != AssignmentKind.Argument);
NullableAnnotation resultAnnotation = NullableAnnotation.Unknown; NullableAnnotation resultAnnotation = NullableAnnotation.Unknown;
bool forceOperandAnnotationForResult = false; bool forceOperandAnnotationForResult = false;
...@@ -3385,10 +3385,11 @@ private bool HasTopLevelNullabilityConversion(TypeSymbolWithAnnotations source, ...@@ -3385,10 +3385,11 @@ private bool HasTopLevelNullabilityConversion(TypeSymbolWithAnnotations source,
conversion.UserDefinedFromConversion, conversion.UserDefinedFromConversion,
TypeSymbolWithAnnotations.Create(conversion.BestUserDefinedConversionAnalysis.FromType), TypeSymbolWithAnnotations.Create(conversion.BestUserDefinedConversionAnalysis.FromType),
operandType, operandType,
checkConversion: false, checkConversion: true,
fromExplicitCast: false, fromExplicitCast: false,
useLegacyWarnings, useLegacyWarnings,
assignmentKind); assignmentKind,
target);
// Update method based on operandType: see https://github.com/dotnet/roslyn/issues/29605. // Update method based on operandType: see https://github.com/dotnet/roslyn/issues/29605.
// (see NullableReferenceTypesTests.ImplicitConversions_07). // (see NullableReferenceTypesTests.ImplicitConversions_07).
...@@ -3665,6 +3666,7 @@ private bool HasTopLevelNullabilityConversion(TypeSymbolWithAnnotations source, ...@@ -3665,6 +3666,7 @@ private bool HasTopLevelNullabilityConversion(TypeSymbolWithAnnotations source,
private TypeSymbolWithAnnotations ClassifyAndApplyConversion(BoundExpression node, TypeSymbolWithAnnotations targetType, TypeSymbolWithAnnotations operandType, bool useLegacyWarnings, AssignmentKind assignmentKind, ParameterSymbol target) 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; HashSet<DiagnosticInfo> useSiteDiagnostics = null;
var conversion = _conversions.ClassifyStandardConversion(null, operandType.TypeSymbol, targetType.TypeSymbol, ref useSiteDiagnostics); var conversion = _conversions.ClassifyStandardConversion(null, operandType.TypeSymbol, targetType.TypeSymbol, ref useSiteDiagnostics);
if (!conversion.Exists) if (!conversion.Exists)
...@@ -4728,7 +4730,7 @@ public override BoundNode VisitEventAssignmentOperator(BoundEventAssignmentOpera ...@@ -4728,7 +4730,7 @@ public override BoundNode VisitEventAssignmentOperator(BoundEventAssignmentOpera
Debug.Assert(!IsConditionalState); Debug.Assert(!IsConditionalState);
var receiverOpt = node.ReceiverOpt; var receiverOpt = node.ReceiverOpt;
var @event = node.Event; var @event = node.Event;
if (!node.Event.IsStatic) if (!@event.IsStatic)
{ {
@event = (EventSymbol)AsMemberOfResultType(_resultType, @event); @event = (EventSymbol)AsMemberOfResultType(_resultType, @event);
// https://github.com/dotnet/roslyn/issues/30598: Mark receiver as not null // https://github.com/dotnet/roslyn/issues/30598: Mark receiver as not null
......
...@@ -65747,5 +65747,66 @@ static void F3(int? z, int? w) ...@@ -65747,5 +65747,66 @@ static void F3(int? z, int? w)
var comp = CreateCompilationWithIndexAndRange(source, options: WithNonNullTypesTrue()); var comp = CreateCompilationWithIndexAndRange(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(); 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.
先完成此消息的编辑!
想要评论请 注册