提交 a1ddc68b 编写于 作者: P Petr Onderka 提交者: Julien Couvreur

Fix default literal in default value of nullable parameter (#22527)

Merged on behalf of @svick. Thanks for the contribution!
上级 d985ea6b
......@@ -63,3 +63,5 @@ static Func<int> M(__arglist)
> break; // new warning: unreachable code
> }
> ```
- https://github.com/dotnet/roslyn/issues/22578 In C# 7.1, the compiler would compute the wrong default value for an optional parameter of nullable type declared with the default literal. For instance, `void M(int? x = default)` would use `0` for the default parameter value, instead of `null`. In C# 7.2 (Visual Studio 2017 version 15.5), the proper default parameter value (`null`) is computed in such cases.
\ No newline at end of file
......@@ -200,8 +200,10 @@ protected ConstantValue MakeDefaultExpression(DiagnosticBag diagnostics, Binder
// If we have something like M(double? x = 1) then the expression we'll get is (double?)1, which
// does not have a constant value. The constant value we want is (double)1.
// The default literal conversion is an exception: (double)default would give the wrong value for M(double? x = default).
if (convertedExpression.ConstantValue == null && convertedExpression.Kind == BoundKind.Conversion)
if (convertedExpression.ConstantValue == null && convertedExpression.Kind == BoundKind.Conversion &&
!(valueBeforeConversion.Kind == BoundKind.DefaultExpression && valueBeforeConversion.Type == null))
{
if (parameterType.IsNullableType())
{
......
......@@ -2630,5 +2630,212 @@ static void Main()
Diagnostic(ErrorCode.ERR_BadSKknown, "System").WithArguments("System", "namespace", "type").WithLocation(6, 17)
);
}
[Fact]
public void DefaultNullableParameter()
{
var text = @"
class C
{
static void Main() { A(); B(); D(); E(); }
static void A(int? x = default) => System.Console.Write($""{x.HasValue} "");
static void B(int? x = default(int?)) => System.Console.Write($""{x.HasValue} "");
static void D(int? x = default(byte?)) => System.Console.Write($""{x.HasValue} "");
static void E(int? x = default(byte)) => System.Console.Write($""{x.HasValue}:{x.Value}"");
}";
var comp = CreateStandardCompilation(text, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe);
comp.VerifyDiagnostics();
CompileAndVerify(comp, expectedOutput: "False False False True:0");
var tree = comp.SyntaxTrees.First();
var model = comp.GetSemanticModel(tree);
var default1 = tree.GetCompilationUnitRoot().DescendantNodes().OfType<LiteralExpressionSyntax>().Single();
Assert.Equal("System.Int32?", model.GetTypeInfo(default1).Type.ToTestDisplayString());
Assert.Equal("System.Int32?", model.GetTypeInfo(default1).ConvertedType.ToTestDisplayString());
Assert.Null(model.GetSymbolInfo(default1).Symbol);
Assert.False(model.GetConstantValue(default1).HasValue);
Assert.True(model.GetConversion(default1).IsNullLiteral);
var default2 = tree.GetCompilationUnitRoot().DescendantNodes().OfType<DefaultExpressionSyntax>().ElementAt(0);
Assert.Equal("System.Int32?", model.GetTypeInfo(default2).Type.ToTestDisplayString());
Assert.Equal("System.Int32?", model.GetTypeInfo(default2).ConvertedType.ToTestDisplayString());
Assert.Null(model.GetSymbolInfo(default2).Symbol);
Assert.False(model.GetConstantValue(default2).HasValue);
Assert.Equal(ConversionKind.Identity, model.GetConversion(default2).Kind);
var default3 = tree.GetCompilationUnitRoot().DescendantNodes().OfType<DefaultExpressionSyntax>().ElementAt(1);
Assert.Equal("System.Byte?", model.GetTypeInfo(default3).Type.ToTestDisplayString());
Assert.Equal("System.Int32?", model.GetTypeInfo(default3).ConvertedType.ToTestDisplayString());
Assert.Null(model.GetSymbolInfo(default3).Symbol);
Assert.False(model.GetConstantValue(default3).HasValue);
Assert.Equal(ConversionKind.ImplicitNullable, model.GetConversion(default3).Kind);
var default4 = tree.GetCompilationUnitRoot().DescendantNodes().OfType<DefaultExpressionSyntax>().ElementAt(2);
Assert.Equal("System.Byte", model.GetTypeInfo(default4).Type.ToTestDisplayString());
Assert.Equal("System.Int32?", model.GetTypeInfo(default4).ConvertedType.ToTestDisplayString());
Assert.Null(model.GetSymbolInfo(default4).Symbol);
Assert.True(model.GetConstantValue(default4).HasValue);
Conversion conversion = model.GetConversion(default4);
Assert.Equal(ConversionKind.ImplicitNullable, conversion.Kind);
Assert.Equal(ConversionKind.ImplicitNumeric, conversion.UnderlyingConversions.Single().Kind);
}
[Fact]
public void TestDefaultInConstWithNullable()
{
string source = @"
struct S { }
class C<T> where T : struct
{
const int? x1 = default;
const int? x2 = default(int?);
const int? x3 = (default);
const S? y1 = default;
const S? y2 = default(S?);
const T? z1 = default;
const T? z2 = default(T?);
}
";
var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1);
comp.VerifyDiagnostics(
// (5,5): error CS0283: The type 'int?' cannot be declared const
// const int? x1 = default;
Diagnostic(ErrorCode.ERR_BadConstType, "const").WithArguments("int?").WithLocation(5, 5),
// (6,5): error CS0283: The type 'int?' cannot be declared const
// const int? x2 = default(int?);
Diagnostic(ErrorCode.ERR_BadConstType, "const").WithArguments("int?").WithLocation(6, 5),
// (7,5): error CS0283: The type 'int?' cannot be declared const
// const int? x3 = (default);
Diagnostic(ErrorCode.ERR_BadConstType, "const").WithArguments("int?").WithLocation(7, 5),
// (8,5): error CS0283: The type 'S?' cannot be declared const
// const S? y1 = default;
Diagnostic(ErrorCode.ERR_BadConstType, "const").WithArguments("S?").WithLocation(8, 5),
// (9,5): error CS0283: The type 'S?' cannot be declared const
// const S? y2 = default(S?);
Diagnostic(ErrorCode.ERR_BadConstType, "const").WithArguments("S?").WithLocation(9, 5),
// (10,5): error CS0283: The type 'T?' cannot be declared const
// const T? z1 = default;
Diagnostic(ErrorCode.ERR_BadConstType, "const").WithArguments("T?").WithLocation(10, 5),
// (11,5): error CS0283: The type 'T?' cannot be declared const
// const T? z2 = default(T?);
Diagnostic(ErrorCode.ERR_BadConstType, "const").WithArguments("T?").WithLocation(11, 5),
// (6,21): error CS0133: The expression being assigned to 'C<T>.x2' must be constant
// const int? x2 = default(int?);
Diagnostic(ErrorCode.ERR_NotConstantExpression, "default(int?)").WithArguments("C<T>.x2").WithLocation(6, 21),
// (7,21): error CS0133: The expression being assigned to 'C<T>.x3' must be constant
// const int? x3 = (default);
Diagnostic(ErrorCode.ERR_NotConstantExpression, "(default)").WithArguments("C<T>.x3").WithLocation(7, 21),
// (8,19): error CS0133: The expression being assigned to 'C<T>.y1' must be constant
// const S? y1 = default;
Diagnostic(ErrorCode.ERR_NotConstantExpression, "default").WithArguments("C<T>.y1").WithLocation(8, 19),
// (9,19): error CS0133: The expression being assigned to 'C<T>.y2' must be constant
// const S? y2 = default(S?);
Diagnostic(ErrorCode.ERR_NotConstantExpression, "default(S?)").WithArguments("C<T>.y2").WithLocation(9, 19),
// (10,19): error CS0133: The expression being assigned to 'C<T>.z1' must be constant
// const T? z1 = default;
Diagnostic(ErrorCode.ERR_NotConstantExpression, "default").WithArguments("C<T>.z1").WithLocation(10, 19),
// (11,19): error CS0133: The expression being assigned to 'C<T>.z2' must be constant
// const T? z2 = default(T?);
Diagnostic(ErrorCode.ERR_NotConstantExpression, "default(T?)").WithArguments("C<T>.z2").WithLocation(11, 19),
// (5,21): error CS0133: The expression being assigned to 'C<T>.x1' must be constant
// const int? x1 = default;
Diagnostic(ErrorCode.ERR_NotConstantExpression, "default").WithArguments("C<T>.x1").WithLocation(5, 21)
);
var tree = comp.SyntaxTrees.First();
var model = comp.GetSemanticModel(tree);
var nodes = tree.GetCompilationUnitRoot().DescendantNodes();
var defaultLiterals = nodes.OfType<LiteralExpressionSyntax>().ToArray();
Assert.Equal(4, defaultLiterals.Length);
foreach (var value in defaultLiterals)
{
Assert.False(model.GetConstantValue(value).HasValue);
}
}
[Fact]
public void TestDefaultInOptionalParameterWithNullable()
{
string source = @"
struct S { }
class C
{
public static void Main()
{
M<long>();
}
static void M<T>(
int? x1 = default,
int? x2 = default(int?),
int? x3 = (default),
S? y1 = default,
S? y2 = default(S?),
T? z1 = default,
T? z2 = default(T?)) where T : struct
{
System.Console.WriteLine($""{x1.HasValue} {x2.HasValue} {x3.HasValue} {y1.HasValue} {y2.HasValue} {z1.HasValue} {z2.HasValue}"");
}
}
";
var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1, options: TestOptions.DebugExe);
comp.VerifyDiagnostics();
CompileAndVerify(comp, expectedOutput: "False False False False False False False");
var tree = comp.SyntaxTrees.First();
var model = comp.GetSemanticModel(tree);
var nodes = tree.GetCompilationUnitRoot().DescendantNodes();
var parameters = nodes.OfType<ParameterSyntax>().ToArray();
Assert.Equal(7, parameters.Length);
foreach (var parameter in parameters)
{
var defaultValue = parameter.Default.Value;
Assert.False(model.GetConstantValue(defaultValue).HasValue);
}
}
[Fact]
public void TestDefaultInAttributeOptionalParameterWithNullable()
{
string source = @"
public struct S { }
public class A : System.Attribute
{
public A(
int? x1 = default,
int? x2 = default(int?),
int? x3 = (default),
S? y1 = default,
S? y2 = default(S?))
{
}
}
[A]
class C
{
}
";
var comp = CreateStandardCompilation(source, parseOptions: TestOptions.Regular7_1);
comp.VerifyDiagnostics(
// (14,2): error CS0181: Attribute constructor parameter 'x1' has type 'int?', which is not a valid attribute parameter type
// [A]
Diagnostic(ErrorCode.ERR_BadAttributeParamType, "A").WithArguments("x1", "int?").WithLocation(14, 2),
// (14,2): error CS0181: Attribute constructor parameter 'x2' has type 'int?', which is not a valid attribute parameter type
// [A]
Diagnostic(ErrorCode.ERR_BadAttributeParamType, "A").WithArguments("x2", "int?").WithLocation(14, 2),
// (14,2): error CS0181: Attribute constructor parameter 'x3' has type 'int?', which is not a valid attribute parameter type
// [A]
Diagnostic(ErrorCode.ERR_BadAttributeParamType, "A").WithArguments("x3", "int?").WithLocation(14, 2),
// (14,2): error CS0181: Attribute constructor parameter 'y1' has type 'S?', which is not a valid attribute parameter type
// [A]
Diagnostic(ErrorCode.ERR_BadAttributeParamType, "A").WithArguments("y1", "S?").WithLocation(14, 2),
// (14,2): error CS0181: Attribute constructor parameter 'y2' has type 'S?', which is not a valid attribute parameter type
// [A]
Diagnostic(ErrorCode.ERR_BadAttributeParamType, "A").WithArguments("y2", "S?").WithLocation(14, 2)
);
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册