提交 70b44545 编写于 作者: C CyrusNajmabadi 提交者: GitHub

Merge pull request #16419 from CyrusNajmabadi/parserDiagnostics35

Move syntax checks about lambda-parameter type mismatches out of the parser.
......@@ -61,6 +61,7 @@ internal partial class Binder
hasSignature = true;
var paren = (ParenthesizedLambdaExpressionSyntax)syntax;
parameterSyntaxList = paren.ParameterList.Parameters;
CheckParanthesizedLambdaParameters(parameterSyntaxList.Value, diagnostics);
isAsync = (paren.AsyncKeyword.Kind() == SyntaxKind.AsyncKeyword);
break;
case SyntaxKind.AnonymousMethodExpression:
......@@ -180,6 +181,32 @@ internal partial class Binder
return Tuple.Create(refKinds, types, names, isAsync);
}
private void CheckParanthesizedLambdaParameters(
SeparatedSyntaxList<ParameterSyntax> parameterSyntaxList, DiagnosticBag diagnostics)
{
if (parameterSyntaxList.Count > 0)
{
var hasTypes = parameterSyntaxList[0].Type != null;
for (int i = 1, n = parameterSyntaxList.Count; i < n; i++)
{
var parameter = parameterSyntaxList[i];
// Ignore parameters with missing names. We'll have already reported an error
// about them in the parser.
if (!parameter.Identifier.IsMissing)
{
var thisParameterHasType = parameter.Type != null;
if (hasTypes != thisParameterHasType)
{
diagnostics.Add(ErrorCode.ERR_InconsistentLambdaParameterUsage,
parameter.Type?.GetLocation() ?? parameter.Identifier.GetLocation());
}
}
}
}
}
private UnboundLambda BindAnonymousFunction(CSharpSyntaxNode syntax, DiagnosticBag diagnostics)
{
Debug.Assert(syntax != null);
......
......@@ -10780,59 +10780,60 @@ private AnonymousMethodExpressionSyntax ParseAnonymousMethodExpression()
return _syntaxFactory.AnonymousMethodExpression(asyncToken, @delegate, parameterList, body);
}
private ExpressionSyntax ParseLambdaExpression()
private LambdaExpressionSyntax ParseLambdaExpression()
{
bool parentScopeIsInAsync = IsInAsync;
SyntaxToken asyncToken = null;
if (this.CurrentToken.ContextualKind == SyntaxKind.AsyncKeyword && PeekToken(1).Kind != SyntaxKind.EqualsGreaterThanToken)
if (this.CurrentToken.ContextualKind == SyntaxKind.AsyncKeyword &&
PeekToken(1).Kind != SyntaxKind.EqualsGreaterThanToken)
{
asyncToken = this.EatContextualToken(SyntaxKind.AsyncKeyword);
asyncToken = CheckFeatureAvailability(asyncToken, MessageID.IDS_FeatureAsync);
IsInAsync = true;
}
ExpressionSyntax result;
var result = ParseLambdaExpression(asyncToken);
IsInAsync = parentScopeIsInAsync;
return result;
}
private LambdaExpressionSyntax ParseLambdaExpression(SyntaxToken asyncToken)
{
if (this.CurrentToken.Kind == SyntaxKind.OpenParenToken)
{
var paramList = this.ParseLambdaParameterList();
var arrow = this.EatToken(SyntaxKind.EqualsGreaterThanToken);
arrow = CheckFeatureAvailability(arrow, MessageID.IDS_FeatureLambda);
CSharpSyntaxNode body;
if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken)
{
body = this.ParseBlock();
}
else
{
body = this.ParsePossibleRefExpression();
}
var body = ParseLambdaBody();
result = _syntaxFactory.ParenthesizedLambdaExpression(asyncToken, paramList, arrow, body);
return _syntaxFactory.ParenthesizedLambdaExpression(asyncToken, paramList, arrow, body);
}
else
{
var name = this.ParseIdentifierToken();
var arrow = this.EatToken(SyntaxKind.EqualsGreaterThanToken);
arrow = CheckFeatureAvailability(arrow, MessageID.IDS_FeatureLambda);
CSharpSyntaxNode body;
if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken)
{
body = this.ParseBlock();
}
else
{
body = this.ParsePossibleRefExpression();
}
result = _syntaxFactory.SimpleLambdaExpression(
asyncToken,
_syntaxFactory.Parameter(default(SyntaxList<AttributeListSyntax>), default(SyntaxList<SyntaxToken>), type: null, identifier: name, @default: null),
arrow,
body);
var parameter = _syntaxFactory.Parameter(
default(SyntaxList<AttributeListSyntax>), default(SyntaxList<SyntaxToken>),
type: null, identifier: name, @default: null);
var body = ParseLambdaBody();
return _syntaxFactory.SimpleLambdaExpression(asyncToken, parameter, arrow, body);
}
}
IsInAsync = parentScopeIsInAsync;
return result;
private CSharpSyntaxNode ParseLambdaBody()
{
if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken)
{
return this.ParseBlock();
}
else
{
return this.ParsePossibleRefExpression();
}
}
private ParameterListSyntax ParseLambdaParameterList()
......@@ -10844,15 +10845,13 @@ private ParameterListSyntax ParseLambdaParameterList()
var nodes = _pool.AllocateSeparated<ParameterSyntax>();
try
{
bool hasTypes = false;
if (this.CurrentToken.Kind != SyntaxKind.CloseParenToken)
{
tryAgain:
if (this.IsPossibleLambdaParameter() || this.CurrentToken.Kind == SyntaxKind.CommaToken)
if (this.CurrentToken.Kind == SyntaxKind.CommaToken || this.IsPossibleLambdaParameter())
{
// first parameter
var parameter = this.ParseLambdaParameter(isFirst: true, hasTypes: ref hasTypes);
var parameter = this.ParseLambdaParameter();
nodes.Add(parameter);
// additional parameters
......@@ -10866,7 +10865,7 @@ private ParameterListSyntax ParseLambdaParameterList()
else if (this.CurrentToken.Kind == SyntaxKind.CommaToken || this.IsPossibleLambdaParameter())
{
nodes.AddSeparator(this.EatToken(SyntaxKind.CommaToken));
parameter = this.ParseLambdaParameter(false, ref hasTypes);
parameter = this.ParseLambdaParameter();
nodes.Add(parameter);
continue;
}
......@@ -10921,45 +10920,66 @@ private PostSkipAction SkipBadLambdaParameterListTokens(ref SyntaxToken openPare
expected);
}
private ParameterSyntax ParseLambdaParameter(bool isFirst, ref bool hasTypes)
private ParameterSyntax ParseLambdaParameter()
{
TypeSyntax paramType = null;
SyntaxToken paramName = null;
SyntaxToken refOrOutOrParams = null;
// Params are actually illegal in a lambda, but we'll allow it for error recovery purposes and
// give the "params unexpected" error at semantic analysis time.
bool isRefOrOutOrParams = this.CurrentToken.Kind == SyntaxKind.RefKeyword || this.CurrentToken.Kind == SyntaxKind.OutKeyword || this.CurrentToken.Kind == SyntaxKind.ParamsKeyword;
var pk = this.PeekToken(1).Kind;
if (isRefOrOutOrParams
|| (pk != SyntaxKind.CommaToken && pk != SyntaxKind.CloseParenToken && (hasTypes || isFirst))
|| (this.CurrentToken.Kind == SyntaxKind.OpenParenToken && pk == SyntaxKind.CloseParenToken && (hasTypes || isFirst))
|| IsPredefinedType(this.CurrentToken.Kind))
{
if (isRefOrOutOrParams)
{
refOrOutOrParams = this.EatToken();
}
SyntaxToken refOrOutOrParams = this.CurrentToken.Kind == SyntaxKind.RefKeyword || this.CurrentToken.Kind == SyntaxKind.OutKeyword || this.CurrentToken.Kind == SyntaxKind.ParamsKeyword
? this.EatToken()
: null;
paramType = this.ParseType(ParseTypeMode.Parameter);
}
TypeSyntax paramType = ShouldParseLambdaParameterType(refOrOutOrParams)
? ParseType(ParseTypeMode.Parameter)
: null;
paramName = this.ParseIdentifierToken();
SyntaxToken paramName = this.ParseIdentifierToken();
return _syntaxFactory.Parameter(
default(SyntaxList<AttributeListSyntax>),
refOrOutOrParams, paramType, paramName,
@default: null);
}
private bool ShouldParseLambdaParameterType(SyntaxToken refOrOutOrParams)
{
// If we have "ref/out/params" always try to parse out a type.
if (refOrOutOrParams != null)
{
return true;
}
if (isFirst)
// If we have "int/string/etc." always parse out a type.
if (IsPredefinedType(this.CurrentToken.Kind))
{
hasTypes = paramType != null;
return true;
}
else if (paramType != null && !hasTypes && !paramName.IsMissing)
// if we have a tuple type in a lambda.
if (this.CurrentToken.Kind == SyntaxKind.OpenParenToken)
{
paramType = this.AddError(paramType, ErrorCode.ERR_InconsistentLambdaParameterUsage);
return true;
}
else if (paramType == null && hasTypes && !paramName.IsMissing)
if (this.IsTrueIdentifier(this.CurrentToken))
{
paramName = this.AddError(paramName, ErrorCode.ERR_InconsistentLambdaParameterUsage);
// Don't parse out a type if we see:
//
// (a,
// (a)
// (a =>
// (a {
//
// In all other cases, parse out a type.
var peek1 = this.PeekToken(1);
if (peek1.Kind != SyntaxKind.CommaToken &&
peek1.Kind != SyntaxKind.CloseParenToken &&
peek1.Kind != SyntaxKind.EqualsGreaterThanToken &&
peek1.Kind != SyntaxKind.OpenBraceToken)
{
return true;
}
}
return _syntaxFactory.Parameter(default(SyntaxList<AttributeListSyntax>), refOrOutOrParams, paramType, paramName, null);
return false;
}
private bool IsCurrentTokenQueryContextualKeyword
......
......@@ -511,10 +511,6 @@ class C {
N(SyntaxKind.CommaToken);
M(SyntaxKind.Parameter);
{
M(SyntaxKind.IdentifierName);
{
M(SyntaxKind.IdentifierToken);
}
M(SyntaxKind.IdentifierToken);
}
M(SyntaxKind.CloseParenToken);
......
......@@ -1002,41 +1002,42 @@ class C
delegate T Func<T>();
delegate T Func<A0, T>(A0 a0);
delegate T Func<A0, A1, T>(A0 a0, A1 a1);
delegate T Func<A0, A1, A2, T>(A0 a0, A1 a1, A2 a2);
delegate T Func<A0, A1, A2, A3, T>(A0 a0, A1 a1, A2 a2, A3 a3);
static void X()
{
Func<int,int> f1 = (int x, y) => 1; // err: mixed parameters
Func<int,int> f2 = (x, int y) => 1; // err: mixed parameters
Func<int,int> f3 = (int x, int y, z) => 1; // err: mixed parameters
Func<int,int> f4 = (int x, y, int z) => 1; // err: mixed parameters
Func<int,int> f5 = (x, int y, int z) => 1; // err: mixed parameters
Func<int,int> f6 = (x, y, int z) => 1; // err: mixed parameters
Func<int,int,int> f1 = (int x, y) => 1; // err: mixed parameters
Func<int,int,int> f2 = (x, int y) => 1; // err: mixed parameters
Func<int,int,int,int> f3 = (int x, int y, z) => 1; // err: mixed parameters
Func<int,int,int,int> f4 = (int x, y, int z) => 1; // err: mixed parameters
Func<int,int,int,int> f5 = (x, int y, int z) => 1; // err: mixed parameters
Func<int,int,int,int> f6 = (x, y, int z) => 1; // err: mixed parameters
}
}
";
ParseAndValidate(test,
// (10,41): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit
// Func<int,int> f1 = (int x, y) => 1; // err: mixed parameters
Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "y"),
// (11,37): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit
// Func<int,int> f2 = (x, int y) => 1; // err: mixed parameters
Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "int"),
// (12,48): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit
// Func<int,int> f3 = (int x, int y, z) => 1; // err: mixed parameters
Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "z"),
// (13,41): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit
// Func<int,int> f4 = (int x, y, int z) => 1; // err: mixed parameters
Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "y"),
// (14,37): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit
// Func<int,int> f5 = (x, int y, int z) => 1; // err: mixed parameters
Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "int"),
// (14,44): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit
// Func<int,int> f5 = (x, int y, int z) => 1; // err: mixed parameters
Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "int"),
// (15,40): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit
// Func<int,int> f6 = (x, y, int z) => 1; // err: mixed parameters
Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "int"));
CreateStandardCompilation(test).VerifyDiagnostics(
// (10,41): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit
// Func<int,int> f1 = (int x, y) => 1; // err: mixed parameters
Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "y"),
// (11,37): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit
// Func<int,int> f2 = (x, int y) => 1; // err: mixed parameters
Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "int"),
// (12,48): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit
// Func<int,int> f3 = (int x, int y, z) => 1; // err: mixed parameters
Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "z"),
// (13,41): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit
// Func<int,int> f4 = (int x, y, int z) => 1; // err: mixed parameters
Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "y"),
// (14,37): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit
// Func<int,int> f5 = (x, int y, int z) => 1; // err: mixed parameters
Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "int"),
// (14,44): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit
// Func<int,int> f5 = (x, int y, int z) => 1; // err: mixed parameters
Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "int"),
// (15,40): error CS0748: Inconsistent lambda parameter usage; parameter types must be all explicit or all implicit
// Func<int,int> f6 = (x, y, int z) => 1; // err: mixed parameters
Diagnostic(ErrorCode.ERR_InconsistentLambdaParameterUsage, "int"));
}
[WorkItem(535915, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/535915")]
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册