From 970de55710a2774f80513412f5d01a5f6ea2698c Mon Sep 17 00:00:00 2001 From: Brian Ng Date: Mon, 19 Oct 2020 14:34:05 -0500 Subject: [PATCH 1/2] [ts] Error on invalid type casts in JSX --- .../babel-parser/src/parser/expression.js | 17 +-- packages/babel-parser/src/plugins/estree.js | 15 +- packages/babel-parser/src/plugins/flow.js | 25 +++ .../src/plugins/typescript/index.js | 10 ++ .../fixtures/typescript/cast/invalid/input.ts | 2 + .../typescript/cast/invalid/output.json | 95 +++++++++++- .../typescript/tsx/cast-invalid/input.tsx | 6 + .../typescript/tsx/cast-invalid/output.json | 142 ++++++++++++++++++ 8 files changed, 294 insertions(+), 18 deletions(-) create mode 100644 packages/babel-parser/test/fixtures/typescript/tsx/cast-invalid/input.tsx create mode 100644 packages/babel-parser/test/fixtures/typescript/tsx/cast-invalid/output.json diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index 559a4b58e1f9..a6fc085271f0 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -752,7 +752,7 @@ export default class ExpressionParser extends LValParser { this.state.yieldPos = oldYieldPos; this.state.awaitPos = oldAwaitPos; } else { - this.toReferencedListDeep(node.arguments); + this.finishCoverCallExpressionArguments(node); // We keep the old value if it isn't null, for cases like // (x = async(yield)) => {} @@ -791,6 +791,13 @@ export default class ExpressionParser extends LValParser { return node; } + finishCoverCallExpressionArguments( + node: N.CallExpression | N.OptionalCallExpression, + isParenthesizedExpr?: boolean, + ) { + this.toReferencedListDeep(node.arguments, isParenthesizedExpr); + } + // MemberExpression [?Yield, ?Await] TemplateLiteral[?Yield, ?Await, +Tagged] // CallExpression [?Yield, ?Await] TemplateLiteral[?Yield, ?Await, +Tagged] parseTaggedTemplateExpression( @@ -2057,14 +2064,6 @@ export default class ExpressionParser extends LValParser { refExpressionErrors, node, ); - if (canBePattern && !this.state.maybeInArrowParameters) { - // This could be an array pattern: - // ([a: string, b: string]) => {} - // In this case, we don't have to call toReferencedList. We will - // call it, if needed, when we are sure that it is a parenthesized - // expression by calling toReferencedListDeep. - this.toReferencedList(node.elements); - } this.state.inFSharpPipelineDirectBody = oldInFSharpPipelineDirectBody; return this.finishNode( node, diff --git a/packages/babel-parser/src/plugins/estree.js b/packages/babel-parser/src/plugins/estree.js index fe0936d7d048..d1834639364a 100644 --- a/packages/babel-parser/src/plugins/estree.js +++ b/packages/babel-parser/src/plugins/estree.js @@ -379,16 +379,19 @@ export default (superClass: Class): Class => return node; } - toReferencedListDeep( - exprList: $ReadOnlyArray, - isParenthesizedExpr?: boolean, - ): void { + finishCoverCallExpressionArguments( + node: + | N.CallExpression + | N.OptionalCallExpression + | N.EstreeImportExpression, + /* isParenthesizedExpr?: boolean, */ + ) { // ImportExpressions do not have an arguments array. - if (!exprList) { + if (node.type === "ImportExpression") { return; } - super.toReferencedListDeep(exprList, isParenthesizedExpr); + super.finishCoverCallExpressionArguments(node); } parseExport(node: N.Node) { diff --git a/packages/babel-parser/src/plugins/flow.js b/packages/babel-parser/src/plugins/flow.js index 26ddfeaae787..e3026a3e774a 100644 --- a/packages/babel-parser/src/plugins/flow.js +++ b/packages/babel-parser/src/plugins/flow.js @@ -2233,6 +2233,31 @@ export default (superClass: Class): Class => return exprList; } + parseArrayLike( + close: TokenType, + canBePattern: boolean, + isTuple: boolean, + refExpressionErrors: ?ExpressionErrors, + ): N.ArrayExpression | N.TupleExpression { + const node = super.parseArrayLike( + close, + canBePattern, + isTuple, + refExpressionErrors, + ); + + // This could be an array pattern: + // ([a: string, b: string]) => {} + // In this case, we don't have to call toReferencedList. We will + // call it, if needed, when we are sure that it is a parenthesized + // expression by calling toReferencedListDeep. + if (canBePattern && !this.state.maybeInArrowParameters) { + this.toReferencedList(node.elements); + } + + return node; + } + checkLVal( expr: N.Expression, bindingType: BindingTypes = BIND_NONE, diff --git a/packages/babel-parser/src/plugins/typescript/index.js b/packages/babel-parser/src/plugins/typescript/index.js index 47782c7b8273..d52d4287c39e 100644 --- a/packages/babel-parser/src/plugins/typescript/index.js +++ b/packages/babel-parser/src/plugins/typescript/index.js @@ -1839,6 +1839,16 @@ export default (superClass: Class): Class => return exprList; } + parseArrayLike(...args): N.ArrayExpression | N.TupleExpression { + const node = super.parseArrayLike(...args); + + if (node.type === "ArrayExpression") { + this.tsCheckForInvalidTypeCasts(node.elements); + } + + return node; + } + parseSubscript( base: N.Expression, startPos: number, diff --git a/packages/babel-parser/test/fixtures/typescript/cast/invalid/input.ts b/packages/babel-parser/test/fixtures/typescript/cast/invalid/input.ts index 6c18863fda3f..517b16757a7f 100644 --- a/packages/babel-parser/test/fixtures/typescript/cast/invalid/input.ts +++ b/packages/babel-parser/test/fixtures/typescript/cast/invalid/input.ts @@ -1,2 +1,4 @@ (a:b); (a:b,c:d); +[a:b]; +[a:b, c:d]; diff --git a/packages/babel-parser/test/fixtures/typescript/cast/invalid/output.json b/packages/babel-parser/test/fixtures/typescript/cast/invalid/output.json index 60e974ee9da9..331f76ac5365 100644 --- a/packages/babel-parser/test/fixtures/typescript/cast/invalid/output.json +++ b/packages/babel-parser/test/fixtures/typescript/cast/invalid/output.json @@ -1,14 +1,17 @@ { "type": "File", - "start":0,"end":17,"loc":{"start":{"line":1,"column":0},"end":{"line":2,"column":10}}, + "start":0,"end":36,"loc":{"start":{"line":1,"column":0},"end":{"line":4,"column":11}}, "errors": [ "SyntaxError: Did not expect a type annotation here. (1:2)", "SyntaxError: Did not expect a type annotation here. (2:2)", - "SyntaxError: Did not expect a type annotation here. (2:6)" + "SyntaxError: Did not expect a type annotation here. (2:6)", + "SyntaxError: Did not expect a type annotation here. (3:2)", + "SyntaxError: Did not expect a type annotation here. (4:2)", + "SyntaxError: Did not expect a type annotation here. (4:7)" ], "program": { "type": "Program", - "start":0,"end":17,"loc":{"start":{"line":1,"column":0},"end":{"line":2,"column":10}}, + "start":0,"end":36,"loc":{"start":{"line":1,"column":0},"end":{"line":4,"column":11}}, "sourceType": "module", "interpreter": null, "body": [ @@ -99,6 +102,92 @@ } ] } + }, + { + "type": "ExpressionStatement", + "start":18,"end":24,"loc":{"start":{"line":3,"column":0},"end":{"line":3,"column":6}}, + "expression": { + "type": "ArrayExpression", + "start":18,"end":23,"loc":{"start":{"line":3,"column":0},"end":{"line":3,"column":5}}, + "elements": [ + { + "type": "TSTypeCastExpression", + "start":19,"end":22,"loc":{"start":{"line":3,"column":1},"end":{"line":3,"column":4}}, + "expression": { + "type": "Identifier", + "start":19,"end":20,"loc":{"start":{"line":3,"column":1},"end":{"line":3,"column":2},"identifierName":"a"}, + "name": "a" + }, + "typeAnnotation": { + "type": "TSTypeAnnotation", + "start":20,"end":22,"loc":{"start":{"line":3,"column":2},"end":{"line":3,"column":4}}, + "typeAnnotation": { + "type": "TSTypeReference", + "start":21,"end":22,"loc":{"start":{"line":3,"column":3},"end":{"line":3,"column":4}}, + "typeName": { + "type": "Identifier", + "start":21,"end":22,"loc":{"start":{"line":3,"column":3},"end":{"line":3,"column":4},"identifierName":"b"}, + "name": "b" + } + } + } + } + ] + } + }, + { + "type": "ExpressionStatement", + "start":25,"end":36,"loc":{"start":{"line":4,"column":0},"end":{"line":4,"column":11}}, + "expression": { + "type": "ArrayExpression", + "start":25,"end":35,"loc":{"start":{"line":4,"column":0},"end":{"line":4,"column":10}}, + "elements": [ + { + "type": "TSTypeCastExpression", + "start":26,"end":29,"loc":{"start":{"line":4,"column":1},"end":{"line":4,"column":4}}, + "expression": { + "type": "Identifier", + "start":26,"end":27,"loc":{"start":{"line":4,"column":1},"end":{"line":4,"column":2},"identifierName":"a"}, + "name": "a" + }, + "typeAnnotation": { + "type": "TSTypeAnnotation", + "start":27,"end":29,"loc":{"start":{"line":4,"column":2},"end":{"line":4,"column":4}}, + "typeAnnotation": { + "type": "TSTypeReference", + "start":28,"end":29,"loc":{"start":{"line":4,"column":3},"end":{"line":4,"column":4}}, + "typeName": { + "type": "Identifier", + "start":28,"end":29,"loc":{"start":{"line":4,"column":3},"end":{"line":4,"column":4},"identifierName":"b"}, + "name": "b" + } + } + } + }, + { + "type": "TSTypeCastExpression", + "start":31,"end":34,"loc":{"start":{"line":4,"column":6},"end":{"line":4,"column":9}}, + "expression": { + "type": "Identifier", + "start":31,"end":32,"loc":{"start":{"line":4,"column":6},"end":{"line":4,"column":7},"identifierName":"c"}, + "name": "c" + }, + "typeAnnotation": { + "type": "TSTypeAnnotation", + "start":32,"end":34,"loc":{"start":{"line":4,"column":7},"end":{"line":4,"column":9}}, + "typeAnnotation": { + "type": "TSTypeReference", + "start":33,"end":34,"loc":{"start":{"line":4,"column":8},"end":{"line":4,"column":9}}, + "typeName": { + "type": "Identifier", + "start":33,"end":34,"loc":{"start":{"line":4,"column":8},"end":{"line":4,"column":9},"identifierName":"d"}, + "name": "d" + } + } + } + } + ] + } } ], "directives": [] diff --git a/packages/babel-parser/test/fixtures/typescript/tsx/cast-invalid/input.tsx b/packages/babel-parser/test/fixtures/typescript/tsx/cast-invalid/input.tsx new file mode 100644 index 000000000000..64ee911bea24 --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/tsx/cast-invalid/input.tsx @@ -0,0 +1,6 @@ +function Foo() { + return ( +
+ ); +} + diff --git a/packages/babel-parser/test/fixtures/typescript/tsx/cast-invalid/output.json b/packages/babel-parser/test/fixtures/typescript/tsx/cast-invalid/output.json new file mode 100644 index 000000000000..945b8714ec0e --- /dev/null +++ b/packages/babel-parser/test/fixtures/typescript/tsx/cast-invalid/output.json @@ -0,0 +1,142 @@ +{ + "type": "File", + "start":0,"end":85,"loc":{"start":{"line":1,"column":0},"end":{"line":5,"column":1}}, + "errors": [ + "SyntaxError: Did not expect a type annotation here. (3:21)", + "SyntaxError: Did not expect a type annotation here. (3:42)" + ], + "program": { + "type": "Program", + "start":0,"end":85,"loc":{"start":{"line":1,"column":0},"end":{"line":5,"column":1}}, + "sourceType": "module", + "interpreter": null, + "body": [ + { + "type": "FunctionDeclaration", + "start":0,"end":85,"loc":{"start":{"line":1,"column":0},"end":{"line":5,"column":1}}, + "id": { + "type": "Identifier", + "start":9,"end":12,"loc":{"start":{"line":1,"column":9},"end":{"line":1,"column":12},"identifierName":"Foo"}, + "name": "Foo" + }, + "generator": false, + "async": false, + "params": [], + "body": { + "type": "BlockStatement", + "start":15,"end":85,"loc":{"start":{"line":1,"column":15},"end":{"line":5,"column":1}}, + "body": [ + { + "type": "ReturnStatement", + "start":19,"end":83,"loc":{"start":{"line":2,"column":2},"end":{"line":4,"column":4}}, + "argument": { + "type": "JSXElement", + "start":32,"end":78,"loc":{"start":{"line":3,"column":4},"end":{"line":3,"column":50}}, + "extra": { + "parenthesized": true, + "parenStart": 26 + }, + "openingElement": { + "type": "JSXOpeningElement", + "start":32,"end":78,"loc":{"start":{"line":3,"column":4},"end":{"line":3,"column":50}}, + "name": { + "type": "JSXIdentifier", + "start":33,"end":36,"loc":{"start":{"line":3,"column":5},"end":{"line":3,"column":8}}, + "name": "div" + }, + "attributes": [ + { + "type": "JSXAttribute", + "start":37,"end":59,"loc":{"start":{"line":3,"column":9},"end":{"line":3,"column":31}}, + "name": { + "type": "JSXIdentifier", + "start":37,"end":42,"loc":{"start":{"line":3,"column":9},"end":{"line":3,"column":14}}, + "name": "propA" + }, + "value": { + "type": "JSXExpressionContainer", + "start":43,"end":59,"loc":{"start":{"line":3,"column":15},"end":{"line":3,"column":31}}, + "expression": { + "type": "ArrayExpression", + "start":44,"end":58,"loc":{"start":{"line":3,"column":16},"end":{"line":3,"column":30}}, + "elements": [ + { + "type": "TSTypeCastExpression", + "start":46,"end":56,"loc":{"start":{"line":3,"column":18},"end":{"line":3,"column":28}}, + "expression": { + "type": "Identifier", + "start":46,"end":49,"loc":{"start":{"line":3,"column":18},"end":{"line":3,"column":21},"identifierName":"key"}, + "name": "key" + }, + "typeAnnotation": { + "type": "TSTypeAnnotation", + "start":49,"end":56,"loc":{"start":{"line":3,"column":21},"end":{"line":3,"column":28}}, + "typeAnnotation": { + "type": "TSTypeReference", + "start":51,"end":56,"loc":{"start":{"line":3,"column":23},"end":{"line":3,"column":28}}, + "typeName": { + "type": "Identifier", + "start":51,"end":56,"loc":{"start":{"line":3,"column":23},"end":{"line":3,"column":28},"identifierName":"value"}, + "name": "value" + } + } + } + } + ] + } + } + }, + { + "type": "JSXAttribute", + "start":60,"end":75,"loc":{"start":{"line":3,"column":32},"end":{"line":3,"column":47}}, + "name": { + "type": "JSXIdentifier", + "start":60,"end":66,"loc":{"start":{"line":3,"column":32},"end":{"line":3,"column":38}}, + "name": "propsB" + }, + "value": { + "type": "JSXExpressionContainer", + "start":67,"end":75,"loc":{"start":{"line":3,"column":39},"end":{"line":3,"column":47}}, + "expression": { + "type": "TSTypeCastExpression", + "start":69,"end":73,"loc":{"start":{"line":3,"column":41},"end":{"line":3,"column":45}}, + "extra": { + "parenthesized": true, + "parenStart": 68 + }, + "expression": { + "type": "Identifier", + "start":69,"end":70,"loc":{"start":{"line":3,"column":41},"end":{"line":3,"column":42},"identifierName":"a"}, + "name": "a" + }, + "typeAnnotation": { + "type": "TSTypeAnnotation", + "start":70,"end":73,"loc":{"start":{"line":3,"column":42},"end":{"line":3,"column":45}}, + "typeAnnotation": { + "type": "TSTypeReference", + "start":72,"end":73,"loc":{"start":{"line":3,"column":44},"end":{"line":3,"column":45}}, + "typeName": { + "type": "Identifier", + "start":72,"end":73,"loc":{"start":{"line":3,"column":44},"end":{"line":3,"column":45},"identifierName":"b"}, + "name": "b" + } + } + } + } + } + } + ], + "selfClosing": true + }, + "closingElement": null, + "children": [] + } + } + ], + "directives": [] + } + } + ], + "directives": [] + } +} \ No newline at end of file From be6082c47297ec81b5fd14d4293cb5c652806a3a Mon Sep 17 00:00:00 2001 From: Brian Ng Date: Mon, 19 Oct 2020 16:00:49 -0500 Subject: [PATCH 2/2] rename --- packages/babel-parser/src/parser/expression.js | 4 ++-- packages/babel-parser/src/plugins/estree.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/babel-parser/src/parser/expression.js b/packages/babel-parser/src/parser/expression.js index a6fc085271f0..82229e972734 100644 --- a/packages/babel-parser/src/parser/expression.js +++ b/packages/babel-parser/src/parser/expression.js @@ -752,7 +752,7 @@ export default class ExpressionParser extends LValParser { this.state.yieldPos = oldYieldPos; this.state.awaitPos = oldAwaitPos; } else { - this.finishCoverCallExpressionArguments(node); + this.toReferencedArguments(node); // We keep the old value if it isn't null, for cases like // (x = async(yield)) => {} @@ -791,7 +791,7 @@ export default class ExpressionParser extends LValParser { return node; } - finishCoverCallExpressionArguments( + toReferencedArguments( node: N.CallExpression | N.OptionalCallExpression, isParenthesizedExpr?: boolean, ) { diff --git a/packages/babel-parser/src/plugins/estree.js b/packages/babel-parser/src/plugins/estree.js index d1834639364a..27df1ad74148 100644 --- a/packages/babel-parser/src/plugins/estree.js +++ b/packages/babel-parser/src/plugins/estree.js @@ -379,7 +379,7 @@ export default (superClass: Class): Class => return node; } - finishCoverCallExpressionArguments( + toReferencedArguments( node: | N.CallExpression | N.OptionalCallExpression @@ -391,7 +391,7 @@ export default (superClass: Class): Class => return; } - super.finishCoverCallExpressionArguments(node); + super.toReferencedArguments(node); } parseExport(node: N.Node) {