未验证 提交 9737cff2 编写于 作者: R Rikki Gibson 提交者: GitHub

Handle NotNullIfNotNull in delegate creation and overrides (#47572)

上级 c06c096f
......@@ -1196,13 +1196,18 @@ private bool IsValidOverrideReturnType(Symbol overridingSymbol, TypeWithAnnotati
}
var conversions = compilation.Conversions.WithNullability(true);
var overriddenParameters = overriddenMethod.Parameters;
var overridingParameters = overridingMethod.Parameters;
var overridingMethodOffset = invokedAsExtensionMethod ? 1 : 0;
Debug.Assert(overriddenMethod.ParameterCount == overridingMethod.ParameterCount - overridingMethodOffset);
if (reportMismatchInReturnType != null)
{
var overridingReturnType = getNotNullIfNotNullOutputType(overridingMethod.ReturnTypeWithAnnotations, overridingMethod.ReturnNotNullIfParameterNotNull);
// check nested nullability
if (!isValidNullableConversion(
conversions,
overridingMethod.RefKind,
overridingMethod.ReturnTypeWithAnnotations.Type,
overridingReturnType.Type,
overriddenMethod.ReturnTypeWithAnnotations.Type))
{
reportMismatchInReturnType(diagnostics, overriddenMethod, overridingMethod, false, extraArgument);
......@@ -1214,7 +1219,7 @@ private bool IsValidOverrideReturnType(Symbol overridingSymbol, TypeWithAnnotati
overridingMethod.RefKind == RefKind.Ref ? RefKind.Ref : RefKind.Out,
overriddenMethod.ReturnTypeWithAnnotations,
overriddenMethod.ReturnTypeFlowAnalysisAnnotations,
overridingMethod.ReturnTypeWithAnnotations,
overridingReturnType,
overridingMethod.ReturnTypeFlowAnalysisAnnotations))
{
reportMismatchInReturnType(diagnostics, overriddenMethod, overridingMethod, true, extraArgument);
......@@ -1227,17 +1232,12 @@ private bool IsValidOverrideReturnType(Symbol overridingSymbol, TypeWithAnnotati
return;
}
ImmutableArray<ParameterSymbol> overridingParameters = overridingMethod.GetParameters();
var overriddenParameters = overriddenMethod.GetParameters();
int overridingMethodOffset = invokedAsExtensionMethod ? 1 : 0;
Debug.Assert(overriddenMethod.ParameterCount == overridingMethod.ParameterCount - overridingMethodOffset);
for (int i = 0; i < overriddenMethod.ParameterCount; i++)
for (int i = 0; i < overriddenParameters.Length; i++)
{
var overriddenParameter = overriddenParameters[i];
var overriddenParameterType = overriddenParameter.TypeWithAnnotations;
var overridingParameter = overridingParameters[i + overridingMethodOffset];
var overridingParameterType = overridingParameter.TypeWithAnnotations;
var overridingParameterType = getNotNullIfNotNullOutputType(overridingParameter.TypeWithAnnotations, overridingParameter.NotNullIfParameterNotNull);
// check nested nullability
if (!isValidNullableConversion(
conversions,
......@@ -1259,6 +1259,23 @@ private bool IsValidOverrideReturnType(Symbol overridingSymbol, TypeWithAnnotati
}
}
TypeWithAnnotations getNotNullIfNotNullOutputType(TypeWithAnnotations outputType, ImmutableHashSet<string> notNullIfParameterNotNull)
{
if (!notNullIfParameterNotNull.IsEmpty)
{
for (var i = 0; i < overriddenParameters.Length; i++)
{
var overridingParam = overridingParameters[i + overridingMethodOffset];
if (notNullIfParameterNotNull.Contains(overridingParam.Name) && !overriddenParameters[i].TypeWithAnnotations.NullableAnnotation.IsAnnotated())
{
return outputType.AsNotAnnotated();
}
}
}
return outputType;
}
static bool isValidNullableConversion(
ConversionsBase conversions,
RefKind refKind,
......
......@@ -55065,6 +55065,369 @@ void M()
);
}
[Fact]
[WorkItem(44129, "https://github.com/dotnet/roslyn/issues/44174")]
public void DelegateCreation_FromMethodGroup_NotNullIfNotNull_01()
{
var source = @"
using System;
using System.Diagnostics.CodeAnalysis;
public class C
{
public Func<string?, string?> M1()
{
return Helper.Method;
}
public Func<string, string> M2()
{
return Helper.Method;
}
public Func<string, string?> M3()
{
return Helper.Method;
}
public Func<string?, string> M4()
{
return Helper.Method; // 1
}
public Func<
#nullable disable
string,
#nullable enable
string> M5()
{
return Helper.Method;
}
}
static class Helper
{
[return: NotNullIfNotNull(""arg"")]
public static string? Method(string? arg) => arg;
}
";
var comp = CreateNullableCompilation(new[] { source, NotNullIfNotNullAttributeDefinition });
comp.VerifyDiagnostics(
// (24,16): warning CS8621: Nullability of reference types in return type of 'string? Helper.Method(string? arg)' doesn't match the target delegate 'Func<string?, string>' (possibly because of nullability attributes).
// return Helper.Method; // 1
Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOfTargetDelegate, "Helper.Method").WithArguments("string? Helper.Method(string? arg)", "System.Func<string?, string>").WithLocation(24, 16)
);
}
[Fact]
[WorkItem(44129, "https://github.com/dotnet/roslyn/issues/44174")]
public void DelegateCreation_FromMethodGroup_NotNullIfNotNull_02()
{
var source = @"
using System.Diagnostics.CodeAnalysis;
public delegate void D1(string? x, out string? y);
public delegate void D2(string x, out string y);
public delegate void D3(string x, out string? y);
public delegate void D4(string? x, out string y);
public delegate void D5(
#nullable disable
string x,
#nullable enable
out string y);
public delegate void D6(string? x, out string? y, out string? z);
public delegate void D7(string x, out string? y, out string z);
public delegate void D8(string x, out string y, out string z);
public delegate void D9(string? x, out string y, out string z);
public class C {
public D1 M1() {
return Helper.Method1;
}
public D2 M2() {
return Helper.Method1;
}
public D3 M3() {
return Helper.Method1;
}
public D4 M4() {
return Helper.Method1; // 1
}
public D5 M5() {
return Helper.Method1;
}
public D6 M6() {
return Helper.Method2;
}
public D7 M7() {
return Helper.Method2; // 2
}
public D8 M8() {
return Helper.Method3;
}
public D9 M9() {
return Helper.Method3; // 3, 4
}
}
static class Helper
{
public static void Method1(string? x, [NotNullIfNotNull(""x"")] out string? y)
{
y = x;
}
public static void Method2(string? x, [NotNullIfNotNull(""x"")] out string? y, [NotNullIfNotNull(""y"")] out string? z)
{
z = y = x;
}
public static void Method3(string? x, [NotNullIfNotNull(""x"")] out string? y, [NotNullIfNotNull(""x"")] out string? z)
{
z = y = x;
}
}
";
// Diagnostic 2 is verifying something a bit esoteric: non-nullability of 'x' does not propagate to 'z'.
var comp = CreateNullableCompilation(new[] { source, NotNullIfNotNullAttributeDefinition });
comp.VerifyDiagnostics(
// (34,16): warning CS8622: Nullability of reference types in type of parameter 'y' of 'void Helper.Method1(string? x, out string? y)' doesn't match the target delegate 'D4' (possibly because of nullability attributes).
// return Helper.Method1; // 1
Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOfTargetDelegate, "Helper.Method1").WithArguments("y", "void Helper.Method1(string? x, out string? y)", "D4").WithLocation(34, 16),
// (46,16): warning CS8622: Nullability of reference types in type of parameter 'z' of 'void Helper.Method2(string? x, out string? y, out string? z)' doesn't match the target delegate 'D7' (possibly because of nullability attributes).
// return Helper.Method2; // 2
Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOfTargetDelegate, "Helper.Method2").WithArguments("z", "void Helper.Method2(string? x, out string? y, out string? z)", "D7").WithLocation(46, 16),
// (54,16): warning CS8622: Nullability of reference types in type of parameter 'y' of 'void Helper.Method3(string? x, out string? y, out string? z)' doesn't match the target delegate 'D9' (possibly because of nullability attributes).
// return Helper.Method3; // 3, 4
Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOfTargetDelegate, "Helper.Method3").WithArguments("y", "void Helper.Method3(string? x, out string? y, out string? z)", "D9").WithLocation(54, 16),
// (54,16): warning CS8622: Nullability of reference types in type of parameter 'z' of 'void Helper.Method3(string? x, out string? y, out string? z)' doesn't match the target delegate 'D9' (possibly because of nullability attributes).
// return Helper.Method3; // 3, 4
Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOfTargetDelegate, "Helper.Method3").WithArguments("z", "void Helper.Method3(string? x, out string? y, out string? z)", "D9").WithLocation(54, 16)
);
}
[Fact]
[WorkItem(44129, "https://github.com/dotnet/roslyn/issues/44174")]
public void DelegateCreation_FromMethodGroup_NotNullIfNotNull_03()
{
var source = @"
using System;
using System.Diagnostics.CodeAnalysis;
public class C {
public Func<string, string> M1() {
return Helper.Method1<string?>;
}
public Func<string, string?> M2() {
return Helper.Method1<string?>;
}
public Func<string?, string> M3() {
return Helper.Method1<string?>; // 1
}
public Func<string?, string?> M4() {
return Helper.Method1<string?>;
}
public Func<T, T> M5<T>() {
return Helper.Method1<T?>;
}
public Func<T, T?> M6<T>() {
return Helper.Method1<T?>;
}
public Func<T?, T> M7<T>() {
return Helper.Method1<T?>; // 2
}
public Func<T?, T?> M8<T>() {
return Helper.Method1<T?>;
}
}
static class Helper
{
[return: NotNullIfNotNull(""t"")]
public static T Method1<T>(T t) => t;
}
";
var comp = CreateNullableCompilation(new[] { source, NotNullIfNotNullAttributeDefinition });
comp.VerifyDiagnostics(
// (15,16): warning CS8621: Nullability of reference types in return type of 'string? Helper.Method1<string?>(string? t)' doesn't match the target delegate 'Func<string?, string>' (possibly because of nullability attributes).
// return Helper.Method1<string?>; // 1
Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOfTargetDelegate, "Helper.Method1<string?>").WithArguments("string? Helper.Method1<string?>(string? t)", "System.Func<string?, string>").WithLocation(15, 16),
// (31,16): warning CS8621: Nullability of reference types in return type of 'T? Helper.Method1<T?>(T? t)' doesn't match the target delegate 'Func<T?, T>' (possibly because of nullability attributes).
// return Helper.Method1<T?>; // 2
Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOfTargetDelegate, "Helper.Method1<T?>").WithArguments("T? Helper.Method1<T?>(T? t)", "System.Func<T?, T>").WithLocation(31, 16)
);
}
[Fact]
[WorkItem(44129, "https://github.com/dotnet/roslyn/issues/44174")]
public void DelegateCreation_FromMethodGroup_NotNullIfNotNull_04()
{
var source = @"
using System.Diagnostics.CodeAnalysis;
public delegate void Del<T1, T2, T3>(T1 x, T2 y, out T3 z);
public class C {
public Del<string, string, string> M1() {
return Helper.Method1;
}
public Del<string, string, string?> M2() {
return Helper.Method1;
}
public Del<string, string?, string> M3() {
return Helper.Method1;
}
public Del<string?, string, string> M4() {
return Helper.Method1;
}
public Del<string?, string?, string> M5() {
return Helper.Method1; // 1
}
public Del<string?, string?, string?> M6() {
return Helper.Method1;
}
}
static class Helper
{
public static void Method1(string? x, string? y, [NotNullIfNotNull(""x""), NotNullIfNotNull(""y"")] out string? z)
{
z = x ?? y;
}
}
";
var comp = CreateNullableCompilation(new[] { source, NotNullIfNotNullAttributeDefinition });
comp.VerifyDiagnostics(
// (24,16): warning CS8622: Nullability of reference types in type of parameter 'z' of 'void Helper.Method1(string? x, string? y, out string? z)' doesn't match the target delegate 'Del<string?, string?, string>' (possibly because of nullability attributes).
// return Helper.Method1; // 1
Diagnostic(ErrorCode.WRN_NullabilityMismatchInParameterTypeOfTargetDelegate, "Helper.Method1").WithArguments("z", "void Helper.Method1(string? x, string? y, out string? z)", "Del<string?, string?, string>").WithLocation(24, 16)
);
}
[Fact]
[WorkItem(44129, "https://github.com/dotnet/roslyn/issues/44174")]
public void DelegateCreation_FromMethodGroup_NotNullIfNotNull_05()
{
var source = @"
using System;
using System.Diagnostics.CodeAnalysis;
public class C {
public Func<string, string, string> M1() {
return Helper.Method1;
}
public Func<string, string, string?> M2() {
return Helper.Method1;
}
public Func<string, string?, string> M3() {
return Helper.Method1;
}
public Func<string?, string, string> M4() {
return Helper.Method1;
}
public Func<string?, string?, string> M5() {
return Helper.Method1; // 1
}
public Func<string?, string?, string?> M6() {
return Helper.Method1;
}
}
static class Helper
{
[return: NotNullIfNotNull(""x""), NotNullIfNotNull(""y"")]
public static string? Method1(string? x, string? y)
{
return x ?? y;
}
}
";
var comp = CreateNullableCompilation(new[] { source, NotNullIfNotNullAttributeDefinition });
comp.VerifyDiagnostics(
// (23,16): warning CS8621: Nullability of reference types in return type of 'string? Helper.Method1(string? x, string? y)' doesn't match the target delegate 'Func<string?, string?, string>' (possibly because of nullability attributes).
// return Helper.Method1; // 1
Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOfTargetDelegate, "Helper.Method1").WithArguments("string? Helper.Method1(string? x, string? y)", "System.Func<string?, string?, string>").WithLocation(23, 16)
);
}
[Fact]
[WorkItem(44129, "https://github.com/dotnet/roslyn/issues/44174")]
public void NotNullIfNotNull_Override()
{
var source = @"
using System.Diagnostics.CodeAnalysis;
abstract class Base1 {
public abstract string? Method(string? arg);
}
class Derived1 : Base1 {
[return: NotNullIfNotNull(""arg"")]
public override string? Method(string? arg) => arg;
}
abstract class Base2 {
public abstract string Method(string arg);
}
class Derived2 : Base2 {
[return: NotNullIfNotNull(""arg"")]
public override string? Method(string? arg) => arg;
}
abstract class Base3 {
public abstract string? Method(string arg);
}
class Derived3 : Base3 {
[return: NotNullIfNotNull(""arg"")]
public override string? Method(string? arg) => arg;
}
abstract class Base4 {
public abstract string Method(string? arg);
}
class Derived4 : Base4 {
[return: NotNullIfNotNull(""arg"")]
public override string? Method(string? arg) => arg; // 1
}
";
var comp = CreateNullableCompilation(new[] { source, NotNullIfNotNullAttributeDefinition });
comp.VerifyDiagnostics(
// (37,29): warning CS8764: Nullability of return type doesn't match overridden member (possibly because of nullability attributes).
// public override string? Method(string? arg) => arg; // 1
Diagnostic(ErrorCode.WRN_TopLevelNullabilityMismatchInReturnTypeOnOverride, "Method").WithLocation(37, 29)
);
}
[Fact]
public void IdentityConversion_DelegateReturnType()
{
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册