mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-06 20:14:01 -06:00
Merge pull request #1246 from Microsoft/moreGrammarChecks
Move parser error to grammar check phase.
This commit is contained in:
commit
dc17a24d61
@ -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
@ -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 {
|
||||
|
||||
@ -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 }
|
||||
]
|
||||
},
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user