From a1eabb84ead333fcaeea22787113f5ad4da06bad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Tue, 4 Aug 2020 17:00:21 -0400 Subject: [PATCH] rescan gt/lt token after TsAsExpression is parsed (#11912) * refactor: move inType checks to flow plugin * polish: replace hardcoded char codes * fix: rescan greater/less token after asExpression is parsed --- packages/babel-parser/src/plugins/flow.js | 3 + .../src/plugins/typescript/index.js | 18 ++- packages/babel-parser/src/tokenizer/index.js | 2 +- .../test/fixtures/typescript/cast/as/input.ts | 2 + .../fixtures/typescript/cast/as/output.json | 121 +++++++++++++++--- 5 files changed, 126 insertions(+), 20 deletions(-) diff --git a/packages/babel-parser/src/plugins/flow.js b/packages/babel-parser/src/plugins/flow.js index a5e597cc7949..b532ce04f47e 100644 --- a/packages/babel-parser/src/plugins/flow.js +++ b/packages/babel-parser/src/plugins/flow.js @@ -2135,6 +2135,9 @@ export default (superClass: Class): Class => (code === charCodes.greaterThan || code === charCodes.lessThan) ) { return this.finishOp(tt.relational, 1); + } else if (this.state.inType && code === charCodes.questionMark) { + // allow double nullable types in Flow: ??string + return this.finishOp(tt.question, 1); } else if (isIteratorStart(code, next)) { this.state.isIterator = true; return super.readWord(); diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index a9b3bf2e8b92..a101638448e0 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -1884,6 +1884,8 @@ export default (superClass: Class): Class => node.typeAnnotation = this.tsNextThenParseType(); } this.finishNode(node, "TSAsExpression"); + // rescan `<`, `>` because they were scanned when this.state.inType was true + this.reScan_lt_gt(); return this.parseExprOp( node, leftStartPos, @@ -2628,13 +2630,27 @@ export default (superClass: Class): Class => // ensure that inside types, we bypass the jsx parser plugin getTokenFromCode(code: number): void { - if (this.state.inType && (code === 62 || code === 60)) { + if ( + this.state.inType && + (code === charCodes.greaterThan || code === charCodes.lessThan) + ) { return this.finishOp(tt.relational, 1); } else { return super.getTokenFromCode(code); } } + // used after we have finished parsing types + reScan_lt_gt() { + if (this.match(tt.relational)) { + const code = this.input.charCodeAt(this.state.start); + if (code === charCodes.lessThan || code === charCodes.greaterThan) { + this.state.pos -= 1; + this.readToken_lt_gt(code); + } + } + } + toAssignableList(exprList: N.Expression[]): $ReadOnlyArray { for (let i = 0; i < exprList.length; i++) { const expr = exprList[i]; diff --git a/packages/babel-parser/src/tokenizer/index.js b/packages/babel-parser/src/tokenizer/index.js index 74db04c5914f..fca85af541e8 100644 --- a/packages/babel-parser/src/tokenizer/index.js +++ b/packages/babel-parser/src/tokenizer/index.js @@ -693,7 +693,7 @@ export default class Tokenizer extends ParserErrors { // '?' const next = this.input.charCodeAt(this.state.pos + 1); const next2 = this.input.charCodeAt(this.state.pos + 2); - if (next === charCodes.questionMark && !this.state.inType) { + if (next === charCodes.questionMark) { if (next2 === charCodes.equalsTo) { // '??=' this.finishOp(tt.assign, 3); diff --git a/packages/babel-parser/test/fixtures/typescript/cast/as/input.ts b/packages/babel-parser/test/fixtures/typescript/cast/as/input.ts index 4ecdb2197e25..3d6627d18956 100644 --- a/packages/babel-parser/test/fixtures/typescript/cast/as/input.ts +++ b/packages/babel-parser/test/fixtures/typescript/cast/as/input.ts @@ -1,4 +1,6 @@ x as T; x < y as boolean; // (x < y) as boolean; +x as boolean <= y; // (x as boolean) <= y; x === 1 as number; // x === (1 as number); x as any as T; +x as boolean ?? y; // (x as boolean) ?? y; diff --git a/packages/babel-parser/test/fixtures/typescript/cast/as/output.json b/packages/babel-parser/test/fixtures/typescript/cast/as/output.json index 3cba96e6515c..8cac92ca5770 100644 --- a/packages/babel-parser/test/fixtures/typescript/cast/as/output.json +++ b/packages/babel-parser/test/fixtures/typescript/cast/as/output.json @@ -1,9 +1,9 @@ { "type": "File", - "start":0,"end":106,"loc":{"start":{"line":1,"column":0},"end":{"line":4,"column":14}}, + "start":0,"end":192,"loc":{"start":{"line":1,"column":0},"end":{"line":6,"column":42}}, "program": { "type": "Program", - "start":0,"end":106,"loc":{"start":{"line":1,"column":0},"end":{"line":4,"column":14}}, + "start":0,"end":192,"loc":{"start":{"line":1,"column":0},"end":{"line":6,"column":42}}, "sourceType": "module", "interpreter": null, "body": [ @@ -70,17 +70,58 @@ "type": "BinaryExpression", "start":49,"end":66,"loc":{"start":{"line":3,"column":0},"end":{"line":3,"column":17}}, "left": { + "type": "TSAsExpression", + "start":49,"end":61,"loc":{"start":{"line":3,"column":0},"end":{"line":3,"column":12}}, + "expression": { + "type": "Identifier", + "start":49,"end":50,"loc":{"start":{"line":3,"column":0},"end":{"line":3,"column":1},"identifierName":"x"}, + "name": "x" + }, + "typeAnnotation": { + "type": "TSBooleanKeyword", + "start":54,"end":61,"loc":{"start":{"line":3,"column":5},"end":{"line":3,"column":12}} + } + }, + "operator": "<=", + "right": { "type": "Identifier", - "start":49,"end":50,"loc":{"start":{"line":3,"column":0},"end":{"line":3,"column":1},"identifierName":"x"}, + "start":65,"end":66,"loc":{"start":{"line":3,"column":16},"end":{"line":3,"column":17},"identifierName":"y"}, + "name": "y" + } + }, + "leadingComments": [ + { + "type": "CommentLine", + "value": " (x < y) as boolean;", + "start":26,"end":48,"loc":{"start":{"line":2,"column":18},"end":{"line":2,"column":40}} + } + ], + "trailingComments": [ + { + "type": "CommentLine", + "value": " (x as boolean) <= y;", + "start":68,"end":91,"loc":{"start":{"line":3,"column":19},"end":{"line":3,"column":42}} + } + ] + }, + { + "type": "ExpressionStatement", + "start":92,"end":110,"loc":{"start":{"line":4,"column":0},"end":{"line":4,"column":18}}, + "expression": { + "type": "BinaryExpression", + "start":92,"end":109,"loc":{"start":{"line":4,"column":0},"end":{"line":4,"column":17}}, + "left": { + "type": "Identifier", + "start":92,"end":93,"loc":{"start":{"line":4,"column":0},"end":{"line":4,"column":1},"identifierName":"x"}, "name": "x" }, "operator": "===", "right": { "type": "TSAsExpression", - "start":55,"end":66,"loc":{"start":{"line":3,"column":6},"end":{"line":3,"column":17}}, + "start":98,"end":109,"loc":{"start":{"line":4,"column":6},"end":{"line":4,"column":17}}, "expression": { "type": "NumericLiteral", - "start":55,"end":56,"loc":{"start":{"line":3,"column":6},"end":{"line":3,"column":7}}, + "start":98,"end":99,"loc":{"start":{"line":4,"column":6},"end":{"line":4,"column":7}}, "extra": { "rawValue": 1, "raw": "1" @@ -89,50 +130,50 @@ }, "typeAnnotation": { "type": "TSNumberKeyword", - "start":60,"end":66,"loc":{"start":{"line":3,"column":11},"end":{"line":3,"column":17}} + "start":103,"end":109,"loc":{"start":{"line":4,"column":11},"end":{"line":4,"column":17}} } } }, "leadingComments": [ { "type": "CommentLine", - "value": " (x < y) as boolean;", - "start":26,"end":48,"loc":{"start":{"line":2,"column":18},"end":{"line":2,"column":40}} + "value": " (x as boolean) <= y;", + "start":68,"end":91,"loc":{"start":{"line":3,"column":19},"end":{"line":3,"column":42}} } ], "trailingComments": [ { "type": "CommentLine", "value": " x === (1 as number);", - "start":68,"end":91,"loc":{"start":{"line":3,"column":19},"end":{"line":3,"column":42}} + "start":111,"end":134,"loc":{"start":{"line":4,"column":19},"end":{"line":4,"column":42}} } ] }, { "type": "ExpressionStatement", - "start":92,"end":106,"loc":{"start":{"line":4,"column":0},"end":{"line":4,"column":14}}, + "start":135,"end":149,"loc":{"start":{"line":5,"column":0},"end":{"line":5,"column":14}}, "expression": { "type": "TSAsExpression", - "start":92,"end":105,"loc":{"start":{"line":4,"column":0},"end":{"line":4,"column":13}}, + "start":135,"end":148,"loc":{"start":{"line":5,"column":0},"end":{"line":5,"column":13}}, "expression": { "type": "TSAsExpression", - "start":92,"end":100,"loc":{"start":{"line":4,"column":0},"end":{"line":4,"column":8}}, + "start":135,"end":143,"loc":{"start":{"line":5,"column":0},"end":{"line":5,"column":8}}, "expression": { "type": "Identifier", - "start":92,"end":93,"loc":{"start":{"line":4,"column":0},"end":{"line":4,"column":1},"identifierName":"x"}, + "start":135,"end":136,"loc":{"start":{"line":5,"column":0},"end":{"line":5,"column":1},"identifierName":"x"}, "name": "x" }, "typeAnnotation": { "type": "TSAnyKeyword", - "start":97,"end":100,"loc":{"start":{"line":4,"column":5},"end":{"line":4,"column":8}} + "start":140,"end":143,"loc":{"start":{"line":5,"column":5},"end":{"line":5,"column":8}} } }, "typeAnnotation": { "type": "TSTypeReference", - "start":104,"end":105,"loc":{"start":{"line":4,"column":12},"end":{"line":4,"column":13}}, + "start":147,"end":148,"loc":{"start":{"line":5,"column":12},"end":{"line":5,"column":13}}, "typeName": { "type": "Identifier", - "start":104,"end":105,"loc":{"start":{"line":4,"column":12},"end":{"line":4,"column":13},"identifierName":"T"}, + "start":147,"end":148,"loc":{"start":{"line":5,"column":12},"end":{"line":5,"column":13},"identifierName":"T"}, "name": "T" } } @@ -141,7 +182,41 @@ { "type": "CommentLine", "value": " x === (1 as number);", - "start":68,"end":91,"loc":{"start":{"line":3,"column":19},"end":{"line":3,"column":42}} + "start":111,"end":134,"loc":{"start":{"line":4,"column":19},"end":{"line":4,"column":42}} + } + ] + }, + { + "type": "ExpressionStatement", + "start":150,"end":168,"loc":{"start":{"line":6,"column":0},"end":{"line":6,"column":18}}, + "expression": { + "type": "LogicalExpression", + "start":150,"end":167,"loc":{"start":{"line":6,"column":0},"end":{"line":6,"column":17}}, + "left": { + "type": "TSAsExpression", + "start":150,"end":162,"loc":{"start":{"line":6,"column":0},"end":{"line":6,"column":12}}, + "expression": { + "type": "Identifier", + "start":150,"end":151,"loc":{"start":{"line":6,"column":0},"end":{"line":6,"column":1},"identifierName":"x"}, + "name": "x" + }, + "typeAnnotation": { + "type": "TSBooleanKeyword", + "start":155,"end":162,"loc":{"start":{"line":6,"column":5},"end":{"line":6,"column":12}} + } + }, + "operator": "??", + "right": { + "type": "Identifier", + "start":166,"end":167,"loc":{"start":{"line":6,"column":16},"end":{"line":6,"column":17},"identifierName":"y"}, + "name": "y" + } + }, + "trailingComments": [ + { + "type": "CommentLine", + "value": " (x as boolean) ?? y;", + "start":169,"end":192,"loc":{"start":{"line":6,"column":19},"end":{"line":6,"column":42}} } ] } @@ -156,8 +231,18 @@ }, { "type": "CommentLine", - "value": " x === (1 as number);", + "value": " (x as boolean) <= y;", "start":68,"end":91,"loc":{"start":{"line":3,"column":19},"end":{"line":3,"column":42}} + }, + { + "type": "CommentLine", + "value": " x === (1 as number);", + "start":111,"end":134,"loc":{"start":{"line":4,"column":19},"end":{"line":4,"column":42}} + }, + { + "type": "CommentLine", + "value": " (x as boolean) ?? y;", + "start":169,"end":192,"loc":{"start":{"line":6,"column":19},"end":{"line":6,"column":42}} } ] } \ No newline at end of file