提交 4b039cb7 编写于 作者: P pgavlin

The C# compiler was not using the correct syntax node to verify whether or not...

The C# compiler was not using the correct syntax node to verify whether or not the expression body of an expression-bodied lambda or member was classified as a statement expression. This caused code to unexpectedly succeed to compile in certain cases (e.g. when the expression body was a parenthesized-expression that wrapped a valid statement expression), which broke conversions to void-returning delegates (and thus overload resolution, as in the original repro). This change adjusts the check to use the correct syntax node.
***NO_CI***
 (changeset 1406995)
上级 5c7b91c9
......@@ -34,7 +34,7 @@ public QueryUnboundLambdaState(UnboundLambda unbound, Binder binder, RangeVariab
Debug.Assert(castTypeSyntax != null);
// We transform the expression from "expr" to "expr.Cast<castTypeOpt>()".
expression = lambdaBodyBinder.MakeQueryInvocation(body, expression, "Cast", castTypeSyntax, castType, diagnostics);
return lambdaBodyBinder.CreateBlockFromExpression(lambdaBodyBinder.Locals, expression, body, diagnostics);
return lambdaBodyBinder.CreateBlockFromExpression(body, lambdaBodyBinder.Locals, body, expression, diagnostics);
})
{ }
......
......@@ -6311,40 +6311,32 @@ private BoundConditionalAccess BindConditionalAccessExpression(ConditionalAccess
// We are not doing this for types that can be made nullable to still allow expression evaluator to
// to get the value.
bool resultIsNotUsed = false;
CSharpSyntaxNode child = node;
CSharpSyntaxNode parent = child.Parent;
// Skip through parenthesis
while (parent != null && parent.Kind() == SyntaxKind.ParenthesizedExpression)
{
child = parent;
parent = child.Parent;
}
CSharpSyntaxNode parent = node.Parent;
if (parent != null)
{
switch (parent.Kind())
{
case SyntaxKind.ExpressionStatement:
resultIsNotUsed = ((ExpressionStatementSyntax)parent).Expression == child;
resultIsNotUsed = ((ExpressionStatementSyntax)parent).Expression == node;
break;
case SyntaxKind.SimpleLambdaExpression:
resultIsNotUsed = (((SimpleLambdaExpressionSyntax)parent).Body == child) && ContainingMethodOrLambdaReturnsVoid();
resultIsNotUsed = (((SimpleLambdaExpressionSyntax)parent).Body == node) && ContainingMethodOrLambdaReturnsVoid();
break;
case SyntaxKind.ParenthesizedLambdaExpression:
resultIsNotUsed = (((ParenthesizedLambdaExpressionSyntax)parent).Body == child) && ContainingMethodOrLambdaReturnsVoid();
resultIsNotUsed = (((ParenthesizedLambdaExpressionSyntax)parent).Body == node) && ContainingMethodOrLambdaReturnsVoid();
break;
case SyntaxKind.ArrowExpressionClause:
resultIsNotUsed = (((ArrowExpressionClauseSyntax)parent).Expression == child) && ContainingMethodOrLambdaReturnsVoid();
resultIsNotUsed = (((ArrowExpressionClauseSyntax)parent).Expression == node) && ContainingMethodOrLambdaReturnsVoid();
break;
case SyntaxKind.ForStatement:
// Incrementors and Initializers doesn't have to produce a value
var loop = (ForStatementSyntax)parent;
resultIsNotUsed = loop.Incrementors.Contains(child) || loop.Initializers.Contains(child);
resultIsNotUsed = loop.Incrementors.Contains(node) || loop.Initializers.Contains(node);
break;
}
}
......
......@@ -481,7 +481,7 @@ UnboundLambda MakePairLambda(CSharpSyntaxNode node, QueryTranslationState state,
var x1Expression = new BoundParameter(node, lambdaSymbol.Parameters[0]) { WasCompilerGenerated = true };
var x2Expression = new BoundParameter(node, lambdaSymbol.Parameters[1]) { WasCompilerGenerated = true };
var construction = MakePair(node, x1.Name, x1Expression, x2.Name, x2Expression, state, d);
return lambdaBodyBinder.CreateBlockFromExpression(ImmutableArray<LocalSymbol>.Empty, construction, node, d);
return lambdaBodyBinder.CreateBlockFromExpression(node, ImmutableArray<LocalSymbol>.Empty, null, construction, d);
};
var result = MakeQueryUnboundLambda(state.RangeVariableMap(), Args(x1, x2), node, resolver);
state.rangeVariable = state.TransparentRangeVariable(this);
......@@ -541,7 +541,7 @@ void ReduceLet(LetClauseSyntax let, QueryTranslationState state, DiagnosticBag d
}
var construction = MakePair(let, x.Name, xExpression, let.Identifier.ValueText, yExpression, state, d);
return lambdaBodyBinder.CreateBlockFromExpression(lambdaBodyBinder.Locals, construction, let, d);
return lambdaBodyBinder.CreateBlockFromExpression(let, lambdaBodyBinder.Locals, null, construction, d);
};
var lambda = MakeQueryUnboundLambda(state.RangeVariableMap(), x, let.Expression, resolver);
state.rangeVariable = state.TransparentRangeVariable(this);
......
......@@ -3164,13 +3164,12 @@ private static bool IsValidStatementExpression(CSharpSyntaxNode syntax, BoundExp
/// Wrap a given expression e into a block as either { e; } or { return e; }
/// Shared between lambda and expression-bodied method binding.
/// </summary>
internal BoundBlock CreateBlockFromExpression(ImmutableArray<LocalSymbol> locals,
BoundExpression expression, CSharpSyntaxNode node, DiagnosticBag diagnostics)
internal BoundBlock CreateBlockFromExpression(CSharpSyntaxNode node, ImmutableArray<LocalSymbol> locals, ExpressionSyntax expressionSyntax, BoundExpression expression, DiagnosticBag diagnostics)
{
var returnType = GetCurrentReturnType();
BoundStatement statement;
var syntax = expression.Syntax;
var syntax = expressionSyntax ?? expression.Syntax;
BoundStatement statement;
if ((object)returnType != null)
{
if (returnType.SpecialType == SpecialType.System_Void || IsTaskReturningAsyncMethod())
......@@ -3178,10 +3177,12 @@ private static bool IsValidStatementExpression(CSharpSyntaxNode syntax, BoundExp
// If the return type is void then the expression is required to be a legal
// statement expression.
Debug.Assert(expressionSyntax != null || !IsValidStatementExpression(expression.Syntax, expression));
bool errors = false;
if (!IsValidStatementExpression(syntax, expression))
if (expressionSyntax == null || !IsValidStatementExpression(expressionSyntax, expression))
{
Error(diagnostics, ErrorCode.ERR_IllegalStatement, node);
Error(diagnostics, ErrorCode.ERR_IllegalStatement, syntax);
errors = true;
}
......@@ -3193,7 +3194,7 @@ private static bool IsValidStatementExpression(CSharpSyntaxNode syntax, BoundExp
}
else
{
expression = CreateReturnConversion(node, diagnostics, expression, returnType);
expression = CreateReturnConversion(syntax, diagnostics, expression, returnType);
statement = new BoundReturnStatement(syntax, expression) { WasCompilerGenerated = true };
}
}
......@@ -3217,7 +3218,7 @@ private static bool IsValidStatementExpression(CSharpSyntaxNode syntax, BoundExp
DiagnosticBag diagnostics)
{
BoundExpression expression = this.BindValue(expressionBody.Expression, diagnostics, BindValueKind.RValue);
return CreateBlockFromExpression(this.Locals, expression, expressionBody, diagnostics);
return CreateBlockFromExpression(expressionBody, this.Locals, expressionBody.Expression, expression, diagnostics);
}
/// <summary>
......@@ -3226,7 +3227,7 @@ private static bool IsValidStatementExpression(CSharpSyntaxNode syntax, BoundExp
public BoundBlock BindLambdaExpressionAsBlock(ExpressionSyntax body, DiagnosticBag diagnostics)
{
BoundExpression expression = this.BindValue(body, diagnostics, BindValueKind.RValue);
return CreateBlockFromExpression(this.Locals, expression, body, diagnostics);
return CreateBlockFromExpression(body, this.Locals, body, expression, diagnostics);
}
internal virtual ImmutableArray<LocalSymbol> Locals
......
......@@ -5142,11 +5142,11 @@ class C<T>
static public void F1(C<T> c)
{
System.Console.WriteLine(""F1"");
Action a = () => ((c?.M()));
Action a = () => c?.M();
a();
}
static public void F2(C<T> c) => (c?.M());
static public void F2(C<T> c) => c?.M();
T M()
{
......@@ -5271,11 +5271,11 @@ unsafe class C
static public void F1(C c)
{
System.Console.WriteLine(""F1"");
Action<object> a = o => (c?.M());
Action<object> a = o => c?.M();
a(null);
}
static public void F2(C c) => ((c?.M()));
static public void F2(C c) => c?.M();
void* M()
{
......
......@@ -365,5 +365,20 @@ class Program
Assert.Equal(typeInfo1.Type, typeInfo2.Type);
}
[WorkItem(1112875, "DevDiv")]
[Fact]
public void Bug1112875()
{
var comp = CreateCompilationWithMscorlib(@"
class Program
{
private void M() => (new object());
}
");
comp.VerifyDiagnostics(
// (4,25): error CS0201: Only assignment, call, increment, decrement, and new object expressions can be used as a statement
// private void M() => (new object());
Diagnostic(ErrorCode.ERR_IllegalStatement, "(new object())").WithLocation(4, 25));
}
}
}
......@@ -1095,5 +1095,47 @@ static void M(System.Func<int, int, System.Action<C>> x)
Assert.Equal(0, symbolInfo.CandidateSymbols.Length);
Assert.Equal(CandidateReason.None, symbolInfo.CandidateReason);
}
[WorkItem(1112875, "DevDiv")]
[Fact]
public void Bug1112875_1()
{
var comp = CreateCompilationWithMscorlib(@"
using System;
class Program
{
static void Main()
{
ICloneable c = """";
Foo(() => (c.Clone()), null);
}
static void Foo(Action x, string y) { }
static void Foo(Func<object> x, object y) { Console.WriteLine(42); }
}", options: TestOptions.ReleaseExe);
comp.VerifyDiagnostics();
CompileAndVerify(comp, expectedOutput: "42");
}
[WorkItem(1112875, "DevDiv")]
[Fact]
public void Bug1112875_2()
{
var comp = CreateCompilationWithMscorlib(@"
class Program
{
void M()
{
var d = new System.Action(() => (new object()));
}
}
");
comp.VerifyDiagnostics(
// (6,41): error CS0201: Only assignment, call, increment, decrement, and new object expressions can be used as a statement
// var d = new System.Action(() => (new object()));
Diagnostic(ErrorCode.ERR_IllegalStatement, "(new object())").WithLocation(6, 41));
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册