Merge pull request #1246 from Microsoft/moreGrammarChecks

Move parser error to grammar check phase.
This commit is contained in:
CyrusNajmabadi 2014-11-22 14:47:23 -08:00
commit dc17a24d61
6 changed files with 83 additions and 45 deletions

View File

@ -1289,7 +1289,7 @@ var definitions = [
children: [
{ name: 'expression', type: 'ILeftHandSideExpressionSyntax' },
{ name: 'openBracketToken', isToken: true, excludeFromAST: true },
{ name: 'argumentExpression', type: 'IExpressionSyntax' },
{ name: 'argumentExpression', type: 'IExpressionSyntax', isOptional: true },
{ name: 'closeBracketToken', isToken: true, excludeFromAST: true }
]
},
@ -1558,7 +1558,7 @@ var definitions = [
interfaces: ['IStatementSyntax'],
children: [
{ name: 'throwKeyword', isToken: true, excludeFromAST: true },
{ name: 'expression', type: 'IExpressionSyntax' },
{ name: 'expression', type: 'IExpressionSyntax', isOptional: true },
{ name: 'semicolonToken', isToken: true, isOptional: true, excludeFromAST: true }
]
},

File diff suppressed because one or more lines are too long

View File

@ -2098,33 +2098,33 @@ module TypeScript.Parser {
parseSyntaxList<IStatementSyntax>(ListParsingState.SwitchClause_Statements));
}
function parseThrowStatementExpression(): IExpressionSyntax {
function parseThrowStatement(throwKeyword: ISyntaxToken): ThrowStatementSyntax {
return new ThrowStatementSyntax(parseNodeData,
consumeToken(throwKeyword), tryParseThrowStatementExpression(), eatExplicitOrAutomaticSemicolon(/*allowWithoutNewline:*/ false));
}
function tryParseThrowStatementExpression(): IExpressionSyntax {
// ThrowStatement[Yield] :
// throw [no LineTerminator here]Expression[In, ?Yield];
// Because of automatic semicolon insertion, we need to report error if this
// throw could be terminated with a semicolon. Note: we can't call 'parseExpression'
// directly as that might consume an expression on the following line.
return canEatExplicitOrAutomaticSemicolon(/*allowWithoutNewline:*/ false)
? createMissingToken(SyntaxKind.IdentifierName, undefined)
: allowInAnd(parseExpression);
// We just return 'undefined' in that case. The actual error will be reported in the
// grammar walker.
return canEatExplicitOrAutomaticSemicolon(/*allowWithoutNewline:*/ false) ? undefined : allowInAnd(parseExpression);
}
function parseThrowStatement(throwKeyword: ISyntaxToken): ThrowStatementSyntax {
return new ThrowStatementSyntax(parseNodeData,
consumeToken(throwKeyword), parseThrowStatementExpression(), eatExplicitOrAutomaticSemicolon(/*allowWithoutNewline:*/ false));
function parseReturnStatement(returnKeyword: ISyntaxToken): ReturnStatementSyntax {
return new ReturnStatementSyntax(parseNodeData,
consumeToken(returnKeyword), tryParseReturnStatementExpression(), eatExplicitOrAutomaticSemicolon(/*allowWithoutNewline:*/ false));
}
function tryParseReturnStatementExpression(): IExpressionSyntax {
// ReturnStatement[Yield] :
// return [no LineTerminator here]Expression[In, ?Yield];
return !canEatExplicitOrAutomaticSemicolon(/*allowWithoutNewline:*/ false) ? allowInAnd(parseExpression) : undefined;
}
function parseReturnStatement(returnKeyword: ISyntaxToken): ReturnStatementSyntax {
return new ReturnStatementSyntax(parseNodeData,
consumeToken(returnKeyword), tryParseReturnStatementExpression(), eatExplicitOrAutomaticSemicolon(/*allowWithoutNewline:*/ false));
return canEatExplicitOrAutomaticSemicolon(/*allowWithoutNewline:*/ false) ? undefined : allowInAnd(parseExpression);
}
function isExpressionStatement(currentToken: ISyntaxToken): boolean {
@ -2650,7 +2650,7 @@ module TypeScript.Parser {
return token0;
}
function tryParseMemberExpressionOrHigher(_currentToken: ISyntaxToken, force: boolean, inObjectCreation: boolean): IMemberExpressionSyntax {
function tryParseMemberExpressionOrHigher(_currentToken: ISyntaxToken, force: boolean): IMemberExpressionSyntax {
// Note: to make our lives simpler, we decompose the the NewExpression productions and
// place ObjectCreationExpression and FunctionExpression into PrimaryExpression.
// like so:
@ -2703,7 +2703,7 @@ module TypeScript.Parser {
return undefined;
}
return parseMemberExpressionRest(expression, inObjectCreation);
return parseMemberExpressionRest(expression);
}
function parseCallExpressionRest(expression: ILeftHandSideExpressionSyntax): ILeftHandSideExpressionSyntax {
@ -2730,7 +2730,7 @@ module TypeScript.Parser {
continue;
case SyntaxKind.OpenBracketToken:
expression = parseElementAccessExpression(expression, _currentToken, /*inObjectCreation:*/ false);
expression = parseElementAccessExpression(expression, _currentToken);
continue;
case SyntaxKind.DotToken:
@ -2747,14 +2747,14 @@ module TypeScript.Parser {
}
}
function parseMemberExpressionRest(expression: IMemberExpressionSyntax, inObjectCreation: boolean): IMemberExpressionSyntax {
function parseMemberExpressionRest(expression: IMemberExpressionSyntax): IMemberExpressionSyntax {
while (true) {
var _currentToken = currentToken();
var currentTokenKind = _currentToken.kind;
switch (currentTokenKind) {
case SyntaxKind.OpenBracketToken:
expression = parseElementAccessExpression(expression, _currentToken, inObjectCreation);
expression = parseElementAccessExpression(expression, _currentToken);
continue;
case SyntaxKind.DotToken:
@ -2808,7 +2808,7 @@ module TypeScript.Parser {
expression = parseSuperExpression(_currentToken);
}
else {
expression = tryParseMemberExpressionOrHigher(_currentToken, force, /*inObjectCreation:*/ false);
expression = tryParseMemberExpressionOrHigher(_currentToken, force);
if (expression === undefined) {
return undefined;
}
@ -2941,29 +2941,18 @@ module TypeScript.Parser {
return allowInAnd(force ? parseAssignmentExpressionOrHigher : tryParseAssignmentExpressionOrHigher);
}
function parseElementAccessArgumentExpression(openBracketToken: ISyntaxToken, inObjectCreation: boolean) {
function parseElementAccessArgumentExpression(openBracketToken: ISyntaxToken) {
// MemberExpression[?Yield] [ Expression[In, ?Yield] ]
// It's not uncommon for a user to write: "new Type[]". Check for that common pattern
// and report a better error message.
if (inObjectCreation && currentToken().kind === SyntaxKind.CloseBracketToken) {
var errorStart = start(openBracketToken, source.text);
var errorEnd = fullEnd(currentToken());
var diagnostic = new Diagnostic(fileName, source.text.lineMap(), errorStart, errorEnd - errorStart,
DiagnosticCode.new_T_cannot_be_used_to_create_an_array_Use_new_Array_T_instead, undefined);
addDiagnostic(diagnostic);
return createEmptyToken(SyntaxKind.IdentifierName);
}
else {
return allowInAnd(parseExpression);
}
// For error recovery purposes. Allow a missing expression here. We'll report the
// appropriate message in the grammar checker.
return currentToken().kind === SyntaxKind.CloseBracketToken ? undefined : allowInAnd(parseExpression);
}
function parseElementAccessExpression(expression: ILeftHandSideExpressionSyntax, openBracketToken: ISyntaxToken, inObjectCreation: boolean): ElementAccessExpressionSyntax {
function parseElementAccessExpression(expression: ILeftHandSideExpressionSyntax, openBracketToken: ISyntaxToken): ElementAccessExpressionSyntax {
// Debug.assert(currentToken().kind === SyntaxKind.OpenBracketToken);
return new ElementAccessExpressionSyntax(parseNodeData, expression, consumeToken(openBracketToken),
parseElementAccessArgumentExpression(openBracketToken, inObjectCreation), eatToken(SyntaxKind.CloseBracketToken));
parseElementAccessArgumentExpression(openBracketToken), eatToken(SyntaxKind.CloseBracketToken));
}
function tryParsePrimaryExpression(_currentToken: ISyntaxToken, force: boolean): IPrimaryExpressionSyntax {
@ -3069,7 +3058,7 @@ module TypeScript.Parser {
// this decision.
return new ObjectCreationExpressionSyntax(parseNodeData,
consumeToken(newKeyword), tryParseMemberExpressionOrHigher(currentToken(), /*force:*/ true, /*inObjectCreation:*/ true), tryParseArgumentList());
consumeToken(newKeyword), tryParseMemberExpressionOrHigher(currentToken(), /*force:*/ true), tryParseArgumentList());
}
function parseTemplateExpression(startToken: ISyntaxToken): IPrimaryExpressionSyntax {

View File

@ -435,7 +435,7 @@ var definitions:ITypeDefinition[] = [
children: [
<any>{ name: 'expression', type: 'ILeftHandSideExpressionSyntax' },
<any>{ name: 'openBracketToken', isToken: true, excludeFromAST: true },
<any>{ name: 'argumentExpression', type: 'IExpressionSyntax' },
<any>{ name: 'argumentExpression', type: 'IExpressionSyntax', isOptional: true },
<any>{ name: 'closeBracketToken', isToken: true, excludeFromAST: true }
]
},
@ -705,7 +705,7 @@ var definitions:ITypeDefinition[] = [
interfaces: ['IStatementSyntax'],
children: [
<any>{ name: 'throwKeyword', isToken: true, excludeFromAST: true },
<any>{ name: 'expression', type: 'IExpressionSyntax' },
<any>{ name: 'expression', type: 'IExpressionSyntax', isOptional: true },
<any>{ name: 'semicolonToken', isToken: true, isOptional: true, excludeFromAST: true }
]
},

View File

@ -893,7 +893,7 @@ module TypeScript {
this.expression = expression,
this.semicolonToken = semicolonToken,
throwKeyword.parent = this,
expression.parent = this,
expression && (expression.parent = this),
semicolonToken && (semicolonToken.parent = this);
};
ThrowStatementSyntax.prototype.kind = SyntaxKind.ThrowStatement;
@ -1347,7 +1347,7 @@ module TypeScript {
this.closeBracketToken = closeBracketToken,
expression.parent = this,
openBracketToken.parent = this,
argumentExpression.parent = this,
argumentExpression && (argumentExpression.parent = this),
closeBracketToken.parent = this;
};
ElementAccessExpressionSyntax.prototype.kind = SyntaxKind.ElementAccessExpression;

View File

@ -138,8 +138,12 @@ module TypeScript {
}
private pushDiagnostic(element: ISyntaxElement, diagnosticKey: string, args?: any[]): void {
this.pushDiagnosticAt(start(element, this.text), width(element), diagnosticKey, args);
}
private pushDiagnosticAt(start: number, length: number, diagnosticKey: string, args?: any[]): void {
this.diagnostics.push(new Diagnostic(
this.syntaxTree.fileName(), this.syntaxTree.lineMap(), start(element, this.text), width(element), diagnosticKey, args));
this.syntaxTree.fileName(), this.syntaxTree.lineMap(), start, length, diagnosticKey, args));
}
public visitCatchClause(node: CatchClauseSyntax): void {
@ -715,6 +719,33 @@ module TypeScript {
super.visitSetAccessor(node);
}
public visitElementAccessExpression(node: ElementAccessExpressionSyntax): void {
if (this.checkForMissingArgumentExpression(node)) {
return;
}
super.visitElementAccessExpression(node);
}
public checkForMissingArgumentExpression(node: ElementAccessExpressionSyntax): boolean {
if (node.argumentExpression === undefined) {
if (node.parent.kind === SyntaxKind.ObjectCreationExpression && (<ObjectCreationExpressionSyntax>node.parent).expression === node) {
// Provide a specialized message for the very common case where someone writes:
// new Foo[]
var start = TypeScript.start(node.openBracketToken);
var end = TypeScript.fullEnd(node.closeBracketToken);
this.pushDiagnosticAt(start, end - start, DiagnosticCode.new_T_cannot_be_used_to_create_an_array_Use_new_Array_T_instead);
}
else {
this.pushDiagnostic(node.closeBracketToken, DiagnosticCode.Expression_expected);
}
return true;
}
return false;
}
public visitEnumDeclaration(node: EnumDeclarationSyntax): void {
if (this.checkForDisallowedDeclareModifier(node.modifiers) ||
this.checkForRequiredDeclareModifier(node, node.identifier, node.modifiers) ||
@ -1264,13 +1295,23 @@ module TypeScript {
}
public visitThrowStatement(node: ThrowStatementSyntax): void {
if (this.checkForStatementInAmbientContxt(node)) {
if (this.checkForStatementInAmbientContxt(node) ||
this.checkForMissingThrowStatementExpression(node)) {
return;
}
super.visitThrowStatement(node);
}
public checkForMissingThrowStatementExpression(node: ThrowStatementSyntax): boolean {
if (node.expression === undefined) {
this.pushDiagnosticAt(fullEnd(node.throwKeyword), 0, DiagnosticCode.Expression_expected);
return true;
}
return false;
}
public visitTryStatement(node: TryStatementSyntax): void {
if (this.checkForStatementInAmbientContxt(node)) {
return;