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

Ignore 'this' parameter when checking nullability of extension method delegate (#35572)

上级 9d7a2525
......@@ -146,6 +146,12 @@ public VisitArgumentResult(VisitResult visitResult, Optional<LocalState> stateFo
// https://github.com/dotnet/roslyn/issues/35043: remove this when all expression are supported
private bool _disableNullabilityAnalysis;
/// <summary>
/// State of method group receivers, used later when analyzing the conversion to a delegate.
/// (Could be replaced by _analyzedNullabilityMapOpt if that map is always available.)
/// </summary>
private PooledDictionary<BoundExpression, TypeWithState> _methodGroupReceiverMapOpt;
#if DEBUG
/// <summary>
/// Contains the expressions that should not be inserted into <see cref="_analyzedNullabilityMapOpt"/>.
......@@ -291,6 +297,7 @@ private void SetAnalyzedNullability(BoundExpression expr, VisitResult result, bo
protected override void Free()
{
_methodGroupReceiverMapOpt?.Free();
_variableTypes.Free();
_placeholderLocalsOpt?.Free();
base.Free();
......@@ -4044,7 +4051,7 @@ public override BoundNode VisitTupleBinaryOperator(BoundTupleBinaryOperator node
return null;
}
private void ReportNullabilityMismatchWithTargetDelegate(Location location, NamedTypeSymbol delegateType, MethodSymbol method)
private void ReportNullabilityMismatchWithTargetDelegate(Location location, NamedTypeSymbol delegateType, MethodSymbol method, bool invokedAsExtensionMethod)
{
Debug.Assert((object)method != null);
Debug.Assert(method.MethodKind != MethodKind.LambdaMethod);
......@@ -4062,11 +4069,12 @@ private void ReportNullabilityMismatchWithTargetDelegate(Location location, Name
delegateType);
}
int count = Math.Min(invoke.ParameterCount, method.ParameterCount);
int methodOffset = invokedAsExtensionMethod ? 1 : 0;
int count = Math.Min(invoke.ParameterCount, method.ParameterCount - methodOffset);
for (int i = 0; i < count; i++)
{
var invokeParameter = invoke.Parameters[i];
var methodParameter = method.Parameters[i];
var methodParameter = method.Parameters[i + methodOffset];
if (IsNullabilityMismatch(invokeParameter.TypeWithAnnotations, methodParameter.TypeWithAnnotations, requireIdentity: invokeParameter.RefKind != RefKind.None))
{
ReportSafetyDiagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOfTargetDelegate, location,
......@@ -4182,9 +4190,25 @@ private bool HasTopLevelNullabilityConversion(TypeWithAnnotations source, TypeWi
switch (conversion.Kind)
{
case ConversionKind.MethodGroup:
if (reportRemainingWarnings)
{
ReportNullabilityMismatchWithTargetDelegate(location, targetType.GetDelegateType(), conversion.Method);
var receiverOpt = (operandOpt as BoundMethodGroup)?.ReceiverOpt;
// https://github.com/dotnet/roslyn/issues/33637: Should update method based on inferred receiver type.
var method = conversion.Method;
if (TryGetMethodGroupReceiverNullability(receiverOpt, out TypeWithState receiverType))
{
if (conversion.IsExtensionMethod)
{
CheckExtensionMethodThisNullability(receiverOpt, Conversion.Identity, method.Parameters[0], receiverType);
}
else
{
CheckPossibleNullReceiver(receiverOpt, receiverType, checkNullableValueType: false);
}
}
if (reportRemainingWarnings)
{
ReportNullabilityMismatchWithTargetDelegate(location, targetType.GetDelegateType(), method, conversion.IsExtensionMethod);
}
}
resultState = NullableFlowState.NotNull;
break;
......@@ -4195,14 +4219,7 @@ private bool HasTopLevelNullabilityConversion(TypeWithAnnotations source, TypeWi
var lambda = (BoundLambda)operandOpt;
var delegateType = targetType.GetDelegateType();
var variableState = GetVariableState(stateForLambda);
Analyze(compilation,
lambda,
_conversions,
Diagnostics,
delegateInvokeMethod: delegateType?.DelegateInvokeMethod,
returnTypes: null,
initialState: variableState,
analyzedNullabilityMapOpt: _disableNullabilityAnalysis ? null : _analyzedNullabilityMapOpt);
VisitLambda(lambda, delegateType, Diagnostics, variableState);
if (reportRemainingWarnings)
{
ReportNullabilityMismatchWithTargetDelegate(location, delegateType, lambda.UnboundLambda);
......@@ -4612,19 +4629,34 @@ public override BoundNode VisitDelegateCreationExpression(BoundDelegateCreationE
{
case BoundMethodGroup group:
{
VisitRvalue(group.ReceiverOpt);
SetAnalyzedNullability(group, default);
// https://github.com/dotnet/roslyn/issues/33637: Should update method based on inferred receiver type.
var method = node.MethodOpt;
if (!(method is null) && !group.IsSuppressed)
if (method is object)
{
ReportNullabilityMismatchWithTargetDelegate(group.Syntax.Location, delegateType, method);
var receiverOpt = group.ReceiverOpt;
if (receiverOpt != null)
{
VisitRvalue(receiverOpt);
if (node.IsExtensionMethod)
{
CheckExtensionMethodThisNullability(receiverOpt, Conversion.Identity, method.Parameters[0], ResultType);
}
else
{
CheckPossibleNullReceiver(receiverOpt);
}
}
if (!group.IsSuppressed)
{
ReportNullabilityMismatchWithTargetDelegate(group.Syntax.Location, delegateType, method, node.IsExtensionMethod);
}
}
SetAnalyzedNullability(group, default);
}
break;
case BoundLambda lambda:
{
VisitLambda(lambda, Diagnostics);
VisitLambda(lambda, delegateType, Diagnostics);
SetNotNullResult(lambda);
if (!lambda.IsSuppressed)
{
......@@ -4649,17 +4681,37 @@ public override BoundNode VisitMethodGroup(BoundMethodGroup node)
if (receiverOpt != null)
{
VisitRvalue(receiverOpt);
// https://github.com/dotnet/roslyn/issues/30563: Should not check receiver here.
// That check should be handled when applying the method group conversion,
// Receiver nullability is checked when applying the method group conversion,
// when we have a specific method, to avoid reporting null receiver warnings
// for extension method delegates.
_ = CheckPossibleNullReceiver(receiverOpt);
// for extension method delegates. Here, store the receiver state for that check.
SetMethodGroupReceiverNullability(receiverOpt, ResultType);
}
SetNotNullResult(node);
return null;
}
private bool TryGetMethodGroupReceiverNullability(BoundExpression receiverOpt, out TypeWithState type)
{
if (receiverOpt != null &&
_methodGroupReceiverMapOpt != null &&
_methodGroupReceiverMapOpt.TryGetValue(receiverOpt, out type))
{
return true;
}
else
{
type = default;
return false;
}
}
private void SetMethodGroupReceiverNullability(BoundExpression receiver, TypeWithState type)
{
_methodGroupReceiverMapOpt ??= PooledDictionary<BoundExpression, TypeWithState>.GetInstance();
_methodGroupReceiverMapOpt[receiver] = type;
}
public override BoundNode VisitLambda(BoundLambda node)
{
// It's possible to reach VisitLambda without having analyzed the lambda body in error scenarios,
......@@ -4673,16 +4725,25 @@ public override BoundNode VisitLambda(BoundLambda node)
if (!_disableNullabilityAnalysis)
{
var bag = new DiagnosticBag();
VisitLambda(node, bag);
VisitLambda(node, node.Type.GetDelegateType(), bag);
bag.Free();
}
SetNotNullResult(node);
return null;
}
private void VisitLambda(BoundLambda node, DiagnosticBag diagnostics)
private void VisitLambda(BoundLambda node, NamedTypeSymbol delegateTypeOpt, DiagnosticBag diagnostics, VariableState initialState = null)
{
Analyze(compilation, node, _conversions, diagnostics, node.Type.GetDelegateType()?.DelegateInvokeMethod, returnTypes: null, initialState: GetVariableState(State.Clone()), _analyzedNullabilityMapOpt);
Debug.Assert(delegateTypeOpt?.IsDelegateType() != false);
Analyze(
compilation,
node,
_conversions,
diagnostics,
delegateTypeOpt?.DelegateInvokeMethod,
returnTypes: null,
initialState: initialState ?? GetVariableState(State.Clone()),
_disableNullabilityAnalysis ? null : _analyzedNullabilityMapOpt);
}
public override BoundNode VisitUnboundLambda(UnboundLambda node)
......@@ -4690,14 +4751,7 @@ public override BoundNode VisitUnboundLambda(UnboundLambda node)
// The presence of this node suggests an error was detected in an earlier phase.
// Analyze the body to report any additional warnings.
var lambda = node.BindForErrorRecovery();
Analyze(compilation,
lambda,
_conversions,
Diagnostics,
delegateInvokeMethod: null,
returnTypes: null,
initialState: GetVariableState(State.Clone()),
_disableNullabilityAnalysis ? null : _analyzedNullabilityMapOpt);
VisitLambda(lambda, delegateTypeOpt: null, Diagnostics);
SetNotNullResult(node);
return null;
}
......@@ -4845,7 +4899,6 @@ private void VisitDeconstructionArguments(ArrayBuilder<DeconstructionVariable> v
SetResultType(right, rightResultOpt.Value);
}
var rightResult = ResultType;
var rightResultWithAnnotations = rightResult.ToTypeWithAnnotations();
var invocation = conversion.DeconstructionInfo.Invocation as BoundCall;
var deconstructMethod = invocation?.Method;
......@@ -4865,17 +4918,13 @@ private void VisitDeconstructionArguments(ArrayBuilder<DeconstructionVariable> v
else
{
// Check nullability for `this` parameter
var parameter = deconstructMethod.Parameters[0];
VisitArgumentConversion(
right, conversion, parameter.RefKind, parameter, parameter.TypeWithAnnotations,
new VisitArgumentResult(new VisitResult(rightResult, rightResultWithAnnotations), stateForLambda: default),
extensionMethodThisArgument: true);
CheckExtensionMethodThisNullability(right, conversion, deconstructMethod.Parameters[0], rightResult);
if (deconstructMethod.IsGenericMethod)
{
// re-infer the deconstruct parameters based on the 'this' parameter
ArrayBuilder<BoundExpression> placeholderArgs = ArrayBuilder<BoundExpression>.GetInstance(n + 1);
placeholderArgs.Add(CreatePlaceholderIfNecessary(right, rightResultWithAnnotations));
placeholderArgs.Add(CreatePlaceholderIfNecessary(right, rightResult.ToTypeWithAnnotations()));
for (int i = 0; i < n; i++)
{
placeholderArgs.Add(new BoundExpressionWithNullability(variables[i].Expression.Syntax, variables[i].Expression, NullableAnnotation.Oblivious, conversion.DeconstructionInfo.OutputPlaceholders[i].Type));
......@@ -6113,7 +6162,16 @@ public override BoundNode VisitDynamicMemberAccess(BoundDynamicMemberAccess node
public override BoundNode VisitDynamicInvocation(BoundDynamicInvocation node)
{
VisitRvalue(node.Expression);
var expr = node.Expression;
VisitRvalue(expr);
// If the expression was a MethodGroup, check nullability of receiver.
var receiverOpt = (expr as BoundMethodGroup)?.ReceiverOpt;
if (TryGetMethodGroupReceiverNullability(receiverOpt, out TypeWithState receiverType))
{
CheckPossibleNullReceiver(receiverOpt, receiverType, checkNullableValueType: false);
}
VisitArgumentsEvaluate(node.Arguments, node.ArgumentRefKindsOpt);
Debug.Assert(node.Type.IsDynamic());
Debug.Assert(node.Type.IsReferenceType);
......@@ -6234,12 +6292,17 @@ public override BoundNode VisitDynamicIndexerAccess(BoundDynamicIndexerAccess no
}
private bool CheckPossibleNullReceiver(BoundExpression receiverOpt, bool checkNullableValueType = false)
{
return CheckPossibleNullReceiver(receiverOpt, ResultType, checkNullableValueType);
}
private bool CheckPossibleNullReceiver(BoundExpression receiverOpt, TypeWithState resultType, bool checkNullableValueType)
{
Debug.Assert(!this.IsConditionalState);
bool reportedDiagnostic = false;
if (receiverOpt != null && this.State.Reachable)
{
var resultTypeSymbol = ResultType.Type;
var resultTypeSymbol = resultType.Type;
if (resultTypeSymbol is null)
{
return false;
......@@ -6247,7 +6310,7 @@ private bool CheckPossibleNullReceiver(BoundExpression receiverOpt, bool checkNu
#if DEBUG
Debug.Assert(receiverOpt.Type is null || AreCloseEnough(receiverOpt.Type, resultTypeSymbol));
#endif
if (!ReportPossibleNullReceiverIfNeeded(resultTypeSymbol, ResultType.State, checkNullableValueType, receiverOpt.Syntax, out reportedDiagnostic))
if (!ReportPossibleNullReceiverIfNeeded(resultTypeSymbol, resultType.State, checkNullableValueType, receiverOpt.Syntax, out reportedDiagnostic))
{
return reportedDiagnostic;
}
......@@ -6277,6 +6340,19 @@ private bool ReportPossibleNullReceiverIfNeeded(TypeSymbol type, NullableFlowSta
return true;
}
private void CheckExtensionMethodThisNullability(BoundExpression expr, Conversion conversion, ParameterSymbol parameter, TypeWithState result)
{
VisitArgumentConversion(
expr,
conversion,
parameter.RefKind,
parameter,
parameter.TypeWithAnnotations,
new VisitArgumentResult(new VisitResult(result, result.ToTypeWithAnnotations()),
stateForLambda: default),
extensionMethodThisArgument: true);
}
private static bool IsNullabilityMismatch(TypeWithAnnotations type1, TypeWithAnnotations type2)
{
// Note, when we are paying attention to nullability, we ignore oblivious mismatch.
......
......@@ -9840,7 +9840,6 @@ public void M(T t)
";
var compilation = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue());
compilation.VerifyTypes();
compilation.VerifyDiagnostics(
// (11,13): warning CS8600: Converting null literal or possible null value to non-nullable type.
// t = null; // warn
......@@ -9869,15 +9868,15 @@ void M()
MyDelegate x1 = Method;
MyDelegate x2 = FalseMethod;
MyDelegate x4 = NullableReturnMethod; // warn 1
MyDelegate x5 = NullableParameterMethod; // warn 2
MyDelegate x5 = NullableParameterMethod;
MyFalseDelegate y1 = Method;
MyFalseDelegate y2 = FalseMethod;
MyFalseDelegate y4 = NullableReturnMethod;
MyFalseDelegate y5 = NullableParameterMethod;
MyNullableDelegate z1 = Method; // warn 3
MyNullableDelegate z1 = Method; // warn 2
MyNullableDelegate z2 = FalseMethod;
MyNullableDelegate z4 = NullableReturnMethod; // warn 4
MyNullableDelegate z5 = NullableParameterMethod; // warn 5
MyNullableDelegate z4 = NullableReturnMethod; // warn 3
MyNullableDelegate z5 = NullableParameterMethod;
}
" + NonNullTypesOn() + @"
......@@ -9892,20 +9891,17 @@ void M()
";
var compilation = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue());
compilation.VerifyTypes();
compilation.VerifyDiagnostics(
// (18,25): warning CS8621: Nullability of reference types in return type of 'string[]? C.NullableReturnMethod(string[] x)' doesn't match the target delegate 'MyDelegate'.
// MyDelegate x4 = NullableReturnMethod; // warn 1
Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOfTargetDelegate, "NullableReturnMethod").WithArguments("string[]? C.NullableReturnMethod(string[] x)", "MyDelegate").WithLocation(18, 25),
// (24,33): warning CS8622: Nullability of reference types in type of parameter 'x' of 'string[] C.Method(string[] x)' doesn't match the target delegate 'MyNullableDelegate'.
// MyNullableDelegate z1 = Method; // warn 3
// MyNullableDelegate z1 = Method; // warn 2
Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOfTargetDelegate, "Method").WithArguments("x", "string[] C.Method(string[] x)", "MyNullableDelegate").WithLocation(24, 33),
// (26,33): warning CS8622: Nullability of reference types in type of parameter 'x' of 'string[]? C.NullableReturnMethod(string[] x)' doesn't match the target delegate 'MyNullableDelegate'.
// MyNullableDelegate z4 = NullableReturnMethod; // warn 4
// MyNullableDelegate z4 = NullableReturnMethod; // warn 3
Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOfTargetDelegate, "NullableReturnMethod").WithArguments("x", "string[]? C.NullableReturnMethod(string[] x)", "MyNullableDelegate").WithLocation(26, 33)
);
// https://github.com/dotnet/roslyn/issues/29844: Missing warnings 2 and 5
}
[Fact]
......@@ -9946,7 +9942,6 @@ void M()
";
var compilation = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue());
compilation.VerifyTypes();
compilation.VerifyDiagnostics(
// (27,15): warning CS8604: Possible null reference argument for parameter 'x' in 'C.C(string[] x)'.
// new C(nullableField); // warn
......@@ -33550,6 +33545,30 @@ class Program
Diagnostic(ErrorCode.WRN_NullReferenceReturn, "F<T?>()").WithLocation(7, 28));
}
[Fact]
public void DelegateCreation_09()
{
var source =
@"delegate void D();
class C
{
void F() { }
static void M()
{
D d = default(C).F; // 1
_ = new D(default(C).F); // 2
}
}";
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(
// (7,15): warning CS8602: Dereference of a possibly null reference.
// D d = default(C).F; // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "default(C)").WithLocation(7, 15),
// (8,19): warning CS8602: Dereference of a possibly null reference.
// _ = new D(default(C).F); // 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "default(C)").WithLocation(8, 19));
}
[Fact]
[WorkItem(32499, "https://github.com/dotnet/roslyn/issues/32499")]
public void DelegateCreation_ReturnType_01()
......@@ -72884,14 +72903,10 @@ static class E
internal static void F2(this C? c) { }
}";
var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue());
// https://github.com/dotnet/roslyn/issues/30563: Should not report "CS8602: Possible dereference" for y.F2.
comp.VerifyDiagnostics(
// (8,13): warning CS8602: Dereference of a possibly null reference.
// d = x.F1; // warning
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(8, 13),
// (9,13): warning CS8602: Dereference of a possibly null reference.
// d = y.F2; // ok
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "y").WithLocation(9, 13));
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(8, 13));
}
[WorkItem(30563, "https://github.com/dotnet/roslyn/issues/30563")]
......@@ -72919,15 +72934,16 @@ static class E
internal static void F(this C x) { }
}";
var comp = CreateCompilation(new[] { source }, options: WithNonNullTypesTrue());
// https://github.com/dotnet/roslyn/issues/30563: Should not report "CS8602: Possible dereference"
// for F2(y.F). Should report "CS8604: Possible null reference argument" instead.
comp.VerifyDiagnostics(
// (10,12): warning CS8602: Dereference of a possibly null reference.
// F1(x.F); // 1
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(10, 12),
// (12,12): warning CS8602: Dereference of a possibly null reference.
// (12,12): warning CS8604: Possible null reference argument for parameter 'x' in 'void E.F(C x)'.
// F2(y.F); // 2
Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "y").WithLocation(12, 12));
Diagnostic(ErrorCode.WRN_NullReferenceArgument, "y").WithArguments("x", "void E.F(C x)").WithLocation(12, 12),
// (13,12): warning CS8604: Possible null reference argument for parameter 'x' in 'void E.F(C x)'.
// F2(y.F); // 3
Diagnostic(ErrorCode.WRN_NullReferenceArgument, "y").WithArguments("x", "void E.F(C x)").WithLocation(13, 12));
}
[WorkItem(33637, "https://github.com/dotnet/roslyn/issues/33637")]
......@@ -95923,5 +95939,292 @@ void M()
Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, @"new[] { new[] { ""string"" , null } }").WithArguments("string?[][]", "System.Collections.Generic.IEnumerable<System.Collections.Generic.IEnumerable<string>>").WithLocation(11, 47)
);
}
[Fact]
[WorkItem(30563, "https://github.com/dotnet/roslyn/issues/30563")]
public void ExtensionMethodDelegate_01()
{
var source =
@"delegate void D0();
delegate void D1<T>(T t);
static class E
{
internal static void F<T>(this T t) { }
}
class Program
{
static void M0(string? x, string y)
{
D0 d;
d = x.F;
d = x.F<string?>;
d = x.F<string>; // 1
d = y.F;
d = y.F<string?>;
d = y.F<string>;
}
static void M1()
{
D1<string?> d;
D1<string> e;
d = E.F<string?>;
d = E.F<string>; // 2
e = E.F<string?>;
e = E.F<string>;
}
}";
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(
// (14,13): warning CS8604: Possible null reference argument for parameter 't' in 'void E.F<string>(string t)'.
// d = x.F<string>; // 1
Diagnostic(ErrorCode.WRN_NullReferenceArgument, "x").WithArguments("t", "void E.F<string>(string t)").WithLocation(14, 13),
// (24,13): warning CS8622: Nullability of reference types in type of parameter 't' of 'void E.F<string>(string t)' doesn't match the target delegate 'D1<string?>'.
// d = E.F<string>; // 2
Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOfTargetDelegate, "E.F<string>").WithArguments("t", "void E.F<string>(string t)", "D1<string?>").WithLocation(24, 13));
}
[Fact]
public void ExtensionMethodDelegate_02()
{
var source =
@"delegate void D0();
delegate void D1<T>(T t);
static class E
{
internal static void F<T>(this T t) { }
}
class Program
{
static void M0(string? x, string y)
{
_ = new D0(x.F);
_ = new D0(x.F<string?>);
_ = new D0(x.F<string>); // 1
_ = new D0(y.F);
_ = new D0(y.F<string?>);
_ = new D0(y.F<string>);
}
static void M1()
{
_ = new D1<string?>(E.F<string?>);
_ = new D1<string?>(E.F<string>); // 2
_ = new D1<string>(E.F<string?>);
_ = new D1<string>(E.F<string>);
}
}";
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(
// (13,20): warning CS8604: Possible null reference argument for parameter 't' in 'void E.F<string>(string t)'.
// _ = new D0(x.F<string>); // 1
Diagnostic(ErrorCode.WRN_NullReferenceArgument, "x").WithArguments("t", "void E.F<string>(string t)").WithLocation(13, 20),
// (21,29): warning CS8622: Nullability of reference types in type of parameter 't' of 'void E.F<string>(string t)' doesn't match the target delegate 'D1<string?>'.
// _ = new D1<string?>(E.F<string>); // 2
Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOfTargetDelegate, "E.F<string>").WithArguments("t", "void E.F<string>(string t)", "D1<string?>").WithLocation(21, 29));
}
[Fact]
[WorkItem(30287, "https://github.com/dotnet/roslyn/issues/30287")]
[WorkItem(30563, "https://github.com/dotnet/roslyn/issues/30563")]
public void ExtensionMethodDelegate_03()
{
var source =
@"delegate void D<in T>(T t);
static class E
{
internal static void F<T>(this T t, int i) { }
}
class Program
{
static void M(string? x, string y)
{
D<int> d;
d = x.F;
d = x.F<string?>;
d = x.F<string>; // 1
d = y.F;
d = y.F<string?>;
d = y.F<string>;
}
}";
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(
// (13,13): warning CS8604: Possible null reference argument for parameter 't' in 'void E.F<string>(string t, int i)'.
// d = x.F<string>; // 1
Diagnostic(ErrorCode.WRN_NullReferenceArgument, "x").WithArguments("t", "void E.F<string>(string t, int i)").WithLocation(13, 13));
}
[Fact]
[WorkItem(30287, "https://github.com/dotnet/roslyn/issues/30287")]
public void ExtensionMethodDelegate_04()
{
var source =
@"delegate void D<in T>(T t);
static class E
{
internal static void F<T>(this T t, int i) { }
}
class Program
{
static void M(string? x, string y)
{
_ = new D<int>(x.F);
_ = new D<int>(x.F<string?>);
_ = new D<int>(x.F<string>); // 1
_ = new D<int>(y.F);
_ = new D<int>(y.F<string?>);
_ = new D<int>(y.F<string>);
}
}";
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(
// (12,24): warning CS8604: Possible null reference argument for parameter 't' in 'void E.F<string>(string t, int i)'.
// _ = new D<int>(x.F<string>); // 1
Diagnostic(ErrorCode.WRN_NullReferenceArgument, "x").WithArguments("t", "void E.F<string>(string t, int i)").WithLocation(12, 24));
}
[Fact]
[WorkItem(30287, "https://github.com/dotnet/roslyn/issues/30287")]
[WorkItem(30563, "https://github.com/dotnet/roslyn/issues/30563")]
public void ExtensionMethodDelegate_05()
{
var source =
@"delegate void D<in T>(T t);
static class E
{
internal static void F<T, U>(this T t, U u) { }
}
class Program
{
static void M(string? x, string y)
{
D<string?> d;
D<string> e;
d = x.F<string?, string?>;
e = x.F<string?, string>;
d = x.F<string, string?>; // 1
e = x.F<string, string>; // 2
d = y.F<string?, string?>;
e = y.F<string?, string>;
d = y.F<string, string?>;
e = y.F<string, string>;
}
}";
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(
// (14,13): warning CS8604: Possible null reference argument for parameter 't' in 'void E.F<string, string?>(string t, string? u)'.
// d = x.F<string, string?>; // 1
Diagnostic(ErrorCode.WRN_NullReferenceArgument, "x").WithArguments("t", "void E.F<string, string?>(string t, string? u)").WithLocation(14, 13),
// (15,13): warning CS8604: Possible null reference argument for parameter 't' in 'void E.F<string, string>(string t, string u)'.
// e = x.F<string, string>; // 2
Diagnostic(ErrorCode.WRN_NullReferenceArgument, "x").WithArguments("t", "void E.F<string, string>(string t, string u)").WithLocation(15, 13));
}
[Fact]
[WorkItem(30287, "https://github.com/dotnet/roslyn/issues/30287")]
public void ExtensionMethodDelegate_06()
{
var source =
@"delegate void D<in T>(T t);
static class E
{
internal static void F<T, U>(this T t, U u) { }
}
class Program
{
static void M(string? x, string y)
{
_ = new D<string?>(x.F<string?, string?>);
_ = new D<string>(x.F<string?, string>);
_ = new D<string?>(x.F<string, string?>); // 1
_ = new D<string>(x.F<string, string>); // 2
_ = new D<string?>(y.F<string?, string?>);
_ = new D<string>(y.F<string?, string>);
_ = new D<string?>(y.F<string, string?>);
_ = new D<string>(y.F<string, string>);
}
}";
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(
// (12,28): warning CS8604: Possible null reference argument for parameter 't' in 'void E.F<string, string?>(string t, string? u)'.
// _ = new D<string?>(x.F<string, string?>); // 1
Diagnostic(ErrorCode.WRN_NullReferenceArgument, "x").WithArguments("t", "void E.F<string, string?>(string t, string? u)").WithLocation(12, 28),
// (13,27): warning CS8604: Possible null reference argument for parameter 't' in 'void E.F<string, string>(string t, string u)'.
// _ = new D<string>(x.F<string, string>); // 2
Diagnostic(ErrorCode.WRN_NullReferenceArgument, "x").WithArguments("t", "void E.F<string, string>(string t, string u)").WithLocation(13, 27));
}
[Fact]
[WorkItem(30563, "https://github.com/dotnet/roslyn/issues/30563")]
public void ExtensionMethodDelegate_07()
{
var source =
@"delegate void D();
static class E
{
internal static void F<T>(this T t) { }
}
class Program
{
static void M<T, U, V>()
where U : class
where V : struct
{
D d;
d = default(T).F;
d = default(U).F;
d = default(V).F;
d = default(V?).F;
}
}";
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(
// (13,13): error CS1113: Extension method 'E.F<T>(T)' defined on value type 'T' cannot be used to create delegates
// d = default(T).F;
Diagnostic(ErrorCode.ERR_ValueTypeExtDelegate, "default(T).F").WithArguments("E.F<T>(T)", "T").WithLocation(13, 13),
// (13,13): warning CS8653: A default expression introduces a null value when 'T' is a non-nullable reference type.
// d = default(T).F;
Diagnostic(ErrorCode.WRN_DefaultExpressionMayIntroduceNullT, "default(T)").WithArguments("T").WithLocation(13, 13),
// (15,13): error CS1113: Extension method 'E.F<V>(V)' defined on value type 'V' cannot be used to create delegates
// d = default(V).F;
Diagnostic(ErrorCode.ERR_ValueTypeExtDelegate, "default(V).F").WithArguments("E.F<V>(V)", "V").WithLocation(15, 13),
// (16,13): error CS1113: Extension method 'E.F<V?>(V?)' defined on value type 'V?' cannot be used to create delegates
// d = default(V?).F;
Diagnostic(ErrorCode.ERR_ValueTypeExtDelegate, "default(V?).F").WithArguments("E.F<V?>(V?)", "V?").WithLocation(16, 13));
}
[Fact]
[WorkItem(30563, "https://github.com/dotnet/roslyn/issues/30563")]
public void ExtensionMethodDelegate_08()
{
var source =
@"delegate void D();
static class E
{
internal static void F<T>(this T t) { }
}
class Program
{
static void M<T, U, V>()
where U : class
where V : struct
{
_ = new D(default(T).F);
_ = new D(default(U).F);
_ = new D(default(V).F);
_ = new D(default(V?).F);
}
}";
var comp = CreateCompilation(source, options: WithNonNullTypesTrue());
comp.VerifyDiagnostics(
// (12,19): error CS1113: Extension method 'E.F<T>(T)' defined on value type 'T' cannot be used to create delegates
// _ = new D(default(T).F);
Diagnostic(ErrorCode.ERR_ValueTypeExtDelegate, "default(T).F").WithArguments("E.F<T>(T)", "T").WithLocation(12, 19),
// (14,19): error CS1113: Extension method 'E.F<V>(V)' defined on value type 'V' cannot be used to create delegates
// _ = new D(default(V).F);
Diagnostic(ErrorCode.ERR_ValueTypeExtDelegate, "default(V).F").WithArguments("E.F<V>(V)", "V").WithLocation(14, 19),
// (15,19): error CS1113: Extension method 'E.F<V?>(V?)' defined on value type 'V?' cannot be used to create delegates
// _ = new D(default(V?).F);
Diagnostic(ErrorCode.ERR_ValueTypeExtDelegate, "default(V?).F").WithArguments("E.F<V?>(V?)", "V?").WithLocation(15, 19));
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册