From a472fcdac41c023b345e2df2fb6339a93c4307d5 Mon Sep 17 00:00:00 2001 From: sosukesuzuki Date: Sat, 12 Sep 2020 16:14:39 +0900 Subject: [PATCH] Address review comment --- .../babel-parser/src/parser/expression.js | 2 + packages/babel-parser/src/parser/statement.js | 24 +++++++-- .../src/plugins/typescript/index.js | 46 ++++++++++++----- .../typescript/declare/namespace/input.ts | 3 ++ .../typescript/declare/namespace/output.json | 50 +++++++++++++++++++ 5 files changed, 107 insertions(+), 18 deletions(-) create mode 100644 packages/babel-parser/test/fixtures/typescript/declare/namespace/input.ts create mode 100644 packages/babel-parser/test/fixtures/typescript/declare/namespace/output.json diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index ccdd7bf8fee4..9a6ca8646ca4 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -2101,6 +2101,8 @@ export default class ExpressionParser extends LValParser { node: N.BodilessFunctionOrMethodBase, type: string, isMethod?: boolean = false, + // eslint-disable-next-line no-unused-vars -- this is used in /plugins/typescript + isDeclare?: boolean = false, ): void { // $FlowIgnore (node is not bodiless if we get here) this.parseFunctionBody(node, false, isMethod); diff --git a/packages/babel-parser/src/parser/statement.js b/packages/babel-parser/src/parser/statement.js index bee813c59196..0193a6b2ea43 100644 --- a/packages/babel-parser/src/parser/statement.js +++ b/packages/babel-parser/src/parser/statement.js @@ -144,14 +144,22 @@ export default class StatementParser extends ExpressionParser { // https://tc39.es/ecma262/#prod-Statement // ImportDeclaration and ExportDeclaration are also handled here so we can throw recoverable errors // when they are not at the top level - parseStatement(context: ?string, topLevel?: boolean): N.Statement { + parseStatement( + context: ?string, + topLevel?: boolean, + isDeclare?: boolean = false, + ): N.Statement { if (this.match(tt.at)) { this.parseDecorators(true); } - return this.parseStatementContent(context, topLevel); + return this.parseStatementContent(context, topLevel, isDeclare); } - parseStatementContent(context: ?string, topLevel: ?boolean): N.Statement { + parseStatementContent( + context: ?string, + topLevel: ?boolean, + isDeclare?: boolean = false, + ): N.Statement { let starttype = this.state.type; const node = this.startNode(); let kind; @@ -185,7 +193,7 @@ export default class StatementParser extends ExpressionParser { this.raise(this.state.start, Errors.SloppyFunction); } } - return this.parseFunctionStatement(node, false, !context); + return this.parseFunctionStatement(node, false, !context, isDeclare); case tt._class: if (context) this.unexpected(); @@ -541,12 +549,14 @@ export default class StatementParser extends ExpressionParser { node: N.FunctionDeclaration, isAsync?: boolean, declarationPosition?: boolean, + isDeclare?: boolean = false, ): N.FunctionDeclaration { this.next(); return this.parseFunction( node, FUNC_STATEMENT | (declarationPosition ? 0 : FUNC_HANGING_STATEMENT), isAsync, + isDeclare, ); } @@ -856,6 +866,7 @@ export default class StatementParser extends ExpressionParser { topLevel: boolean, end: TokenType, afterBlockParse?: (hasStrictModeDirective: boolean) => void, + isDeclare?: boolean = false, ): void { const octalPositions = []; const oldStrict = this.state.strict; @@ -868,7 +879,7 @@ export default class StatementParser extends ExpressionParser { octalPositions.push(...this.state.octalPositions); } - const stmt = this.parseStatement(null, topLevel); + const stmt = this.parseStatement(null, topLevel, isDeclare); if (directives && !parsedNonDirective && this.isValidDirective(stmt)) { const directive = this.stmtToDirective(stmt); @@ -1054,6 +1065,7 @@ export default class StatementParser extends ExpressionParser { node: T, statement?: number = FUNC_NO_FLAGS, isAsync?: boolean = false, + isDeclare?: boolean = false, ): T { const isStatement = statement & FUNC_STATEMENT; const isHangingStatement = statement & FUNC_HANGING_STATEMENT; @@ -1093,6 +1105,8 @@ export default class StatementParser extends ExpressionParser { this.parseFunctionBodyAndFinish( node, isStatement ? "FunctionDeclaration" : "FunctionExpression", + /* method */ false, + isDeclare, ); }); diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 3c870b5b0884..614f5a9bdc89 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -1318,7 +1318,7 @@ export default (superClass: Class): Class => return this.finishNode(node, "TSEnumDeclaration"); } - tsParseModuleBlock(): N.TsModuleBlock { + tsParseModuleBlock(isDeclare?: boolean = false): N.TsModuleBlock { const node: N.TsModuleBlock = this.startNode(); this.scope.enter(SCOPE_OTHER); @@ -1329,6 +1329,8 @@ export default (superClass: Class): Class => /* directives */ undefined, /* topLevel */ true, /* end */ tt.braceR, + /* afterBlockParse */ undefined, + /* declare */ isDeclare, ); this.scope.exit(); return this.finishNode(node, "TSModuleBlock"); @@ -1337,6 +1339,7 @@ export default (superClass: Class): Class => tsParseModuleOrNamespaceDeclaration( node: N.TsModuleDeclaration, nested?: boolean = false, + isDeclare?: boolean = false, ): N.TsModuleDeclaration { node.id = this.parseIdentifier(); @@ -1356,7 +1359,7 @@ export default (superClass: Class): Class => } else { this.scope.enter(SCOPE_TS_MODULE); this.prodParam.enter(PARAM); - node.body = this.tsParseModuleBlock(); + node.body = this.tsParseModuleBlock(isDeclare); this.prodParam.exit(); this.scope.exit(); } @@ -1478,6 +1481,7 @@ export default (superClass: Class): Class => nany, /* async */ false, /* declarationPosition */ true, + /* declare */ true, ); case tt._class: // While this is also set by tsParseExpressionStatement, we need to set it @@ -1504,7 +1508,12 @@ export default (superClass: Class): Class => if (value === "global") { return this.tsParseAmbientExternalModuleDeclaration(nany); } else { - return this.tsParseDeclaration(nany, value, /* next */ true); + return this.tsParseDeclaration( + nany, + value, + /* next */ true, + /* declare */ true, + ); } } } @@ -1555,6 +1564,7 @@ export default (superClass: Class): Class => node: any, value: string, next: boolean, + isDeclare?: boolean = false, ): ?N.Declaration { switch (value) { case "abstract": @@ -1601,7 +1611,11 @@ export default (superClass: Class): Class => case "namespace": if (this.tsCheckLineTerminatorAndMatch(tt.name, next)) { if (next) this.next(); - return this.tsParseModuleOrNamespaceDeclaration(node); + return this.tsParseModuleOrNamespaceDeclaration( + node, + /* nested */ false, + isDeclare, + ); } break; @@ -1752,6 +1766,7 @@ export default (superClass: Class): Class => node: N.BodilessFunctionOrMethodBase, type: string, isMethod?: boolean = false, + isDeclare?: boolean = false, ): void { if (this.match(tt.colon)) { node.returnType = this.tsParseTypeOrTypePredicateAnnotation(tt.colon); @@ -1767,14 +1782,15 @@ export default (superClass: Class): Class => this.finishNode(node, bodilessType); return; } - if ( - bodilessType === "TSDeclareFunction" && - // $FlowIgnore - node.declare - ) { + if (bodilessType === "TSDeclareFunction" && isDeclare) { this.raise(node.start, TSErrors.DeclareFunctionHasImplementation); - this.finishNode(node, bodilessType); - return; + if ( + // $FlowIgnore + node.declare + ) { + this.finishNode(node, bodilessType); + return; + } } super.parseFunctionBodyAndFinish(node, type, isMethod); @@ -2035,7 +2051,11 @@ export default (superClass: Class): Class => return super.parseExportDefaultExpression(); } - parseStatementContent(context: ?string, topLevel: ?boolean): N.Statement { + parseStatementContent( + context: ?string, + topLevel: ?boolean, + isDeclare?: boolean = false, + ): N.Statement { if (this.state.type === tt._const) { const ahead = this.lookahead(); if (ahead.type === tt.name && ahead.value === "enum") { @@ -2045,7 +2065,7 @@ export default (superClass: Class): Class => return this.tsParseEnumDeclaration(node, /* isConst */ true); } } - return super.parseStatementContent(context, topLevel); + return super.parseStatementContent(context, topLevel, isDeclare); } parseAccessModifier(): ?N.Accessibility { diff --git a/packages/babel-parser/test/fixtures/typescript/declare/namespace/input.ts b/packages/babel-parser/test/fixtures/typescript/declare/namespace/input.ts new file mode 100644 index 000000000000..c7f3afddc9a3 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/declare/namespace/input.ts @@ -0,0 +1,3 @@ +declare namespace n { + function foo() {} +} diff --git a/packages/babel-parser/test/fixtures/typescript/declare/namespace/output.json b/packages/babel-parser/test/fixtures/typescript/declare/namespace/output.json new file mode 100644 index 000000000000..251434a13486 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/declare/namespace/output.json @@ -0,0 +1,50 @@ +{ + "type": "File", + "start":0,"end":43,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}}, + "errors": [ + "SyntaxError: An implementation cannot be declared in ambient contexts. (2:2)" + ], + "program": { + "type": "Program", + "start":0,"end":43,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "TSModuleDeclaration", + "start":0,"end":43,"loc":{"start":{"line":1,"column":0},"end":{"line":3,"column":1}}, + "id": { + "type": "Identifier", + "start":18,"end":19,"loc":{"start":{"line":1,"column":18},"end":{"line":1,"column":19},"identifierName":"n"}, + "name": "n" + }, + "body": { + "type": "TSModuleBlock", + "start":20,"end":43,"loc":{"start":{"line":1,"column":20},"end":{"line":3,"column":1}}, + "body": [ + { + "type": "FunctionDeclaration", + "start":24,"end":41,"loc":{"start":{"line":2,"column":2},"end":{"line":2,"column":19}}, + "id": { + "type": "Identifier", + "start":33,"end":36,"loc":{"start":{"line":2,"column":11},"end":{"line":2,"column":14},"identifierName":"foo"}, + "name": "foo" + }, + "generator": false, + "async": false, + "params": [], + "body": { + "type": "BlockStatement", + "start":39,"end":41,"loc":{"start":{"line":2,"column":17},"end":{"line":2,"column":19}}, + "body": [], + "directives": [] + } + } + ] + }, + "declare": true + } + ], + "directives": [] + } +} \ No newline at end of file