From 96eb412d0a34ae94ad1e8ec17efeb85053699086 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Tue, 12 Jul 2022 11:44:31 -0400 Subject: [PATCH 1/4] polish: better error message --- .../src/modifications/prependToMemberExpression.ts | 6 ++++++ packages/babel-types/test/modifications.js | 13 +++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 packages/babel-types/test/modifications.js diff --git a/packages/babel-types/src/modifications/prependToMemberExpression.ts b/packages/babel-types/src/modifications/prependToMemberExpression.ts index 5988bb2d025f..f211e814ed4c 100644 --- a/packages/babel-types/src/modifications/prependToMemberExpression.ts +++ b/packages/babel-types/src/modifications/prependToMemberExpression.ts @@ -1,4 +1,5 @@ import { memberExpression } from "../builders/generated"; +import { isSuper } from ".."; import type * as t from ".."; /** @@ -7,6 +8,11 @@ import type * as t from ".."; export default function prependToMemberExpression< T extends Pick, >(member: T, prepend: t.MemberExpression["object"]): T { + if (isSuper(member.object)) { + throw new Error( + "Cannot prepend node to super property access (`super.foo`).", + ); + } member.object = memberExpression(prepend, member.object); return member; diff --git a/packages/babel-types/test/modifications.js b/packages/babel-types/test/modifications.js new file mode 100644 index 000000000000..9eed5a19b373 --- /dev/null +++ b/packages/babel-types/test/modifications.js @@ -0,0 +1,13 @@ +import * as t from "../lib/index.js"; + +describe("prependToMemberExpression", function () { + it("should throw when prepending node to super property", () => { + const object = t.super(); + const memberExpression = t.memberExpression(t.super(), t.identifier("foo")); + expect(() => + t.prependToMemberExpression(memberExpression, object), + ).toThrowError( + "Cannot prepend node to super property access (`super.foo`).", + ); + }); +}); From 6a23cccf3688ae438f71e00a5a02d992fb1d7a4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Tue, 12 Jul 2022 12:09:53 -0400 Subject: [PATCH 2/4] breaking: remove Super from Expression --- .../babel-types/src/ast-types/generated/index.ts | 6 +++--- .../babel-types/src/builders/generated/index.ts | 6 +++--- packages/babel-types/src/definitions/core.ts | 15 ++++++++++----- 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/packages/babel-types/src/ast-types/generated/index.ts b/packages/babel-types/src/ast-types/generated/index.ts index e9d9906eb302..ec419a324510 100644 --- a/packages/babel-types/src/ast-types/generated/index.ts +++ b/packages/babel-types/src/ast-types/generated/index.ts @@ -371,7 +371,7 @@ export interface BreakStatement extends BaseNode { export interface CallExpression extends BaseNode { type: "CallExpression"; - callee: Expression | V8IntrinsicIdentifier; + callee: Expression | Super | V8IntrinsicIdentifier; arguments: Array< Expression | SpreadElement | JSXNamespacedName | ArgumentPlaceholder >; @@ -544,7 +544,7 @@ export interface LogicalExpression extends BaseNode { export interface MemberExpression extends BaseNode { type: "MemberExpression"; - object: Expression; + object: Expression | Super; property: Expression | Identifier | PrivateName; computed: boolean; optional?: true | false | null; @@ -552,7 +552,7 @@ export interface MemberExpression extends BaseNode { export interface NewExpression extends BaseNode { type: "NewExpression"; - callee: Expression | V8IntrinsicIdentifier; + callee: Expression | Super | V8IntrinsicIdentifier; arguments: Array< Expression | SpreadElement | JSXNamespacedName | ArgumentPlaceholder >; diff --git a/packages/babel-types/src/builders/generated/index.ts b/packages/babel-types/src/builders/generated/index.ts index 52d4afca6fdc..5b07d9824037 100644 --- a/packages/babel-types/src/builders/generated/index.ts +++ b/packages/babel-types/src/builders/generated/index.ts @@ -96,7 +96,7 @@ export function breakStatement( }); } export function callExpression( - callee: t.Expression | t.V8IntrinsicIdentifier, + callee: t.Expression | t.Super | t.V8IntrinsicIdentifier, _arguments: Array< t.Expression | t.SpreadElement | t.JSXNamespacedName | t.ArgumentPlaceholder >, @@ -314,7 +314,7 @@ export function logicalExpression( }); } export function memberExpression( - object: t.Expression, + object: t.Expression | t.Super, property: t.Expression | t.Identifier | t.PrivateName, computed: boolean = false, optional: true | false | null = null, @@ -328,7 +328,7 @@ export function memberExpression( }); } export function newExpression( - callee: t.Expression | t.V8IntrinsicIdentifier, + callee: t.Expression | t.Super | t.V8IntrinsicIdentifier, _arguments: Array< t.Expression | t.SpreadElement | t.JSXNamespacedName | t.ArgumentPlaceholder >, diff --git a/packages/babel-types/src/definitions/core.ts b/packages/babel-types/src/definitions/core.ts index c22d18344097..c938999f717c 100644 --- a/packages/babel-types/src/definitions/core.ts +++ b/packages/babel-types/src/definitions/core.ts @@ -177,7 +177,7 @@ defineType("CallExpression", { aliases: ["Expression"], fields: { callee: { - validate: assertNodeType("Expression", "V8IntrinsicIdentifier"), + validate: assertNodeType("Expression", "Super", "V8IntrinsicIdentifier"), }, arguments: { validate: chain( @@ -684,7 +684,7 @@ defineType("MemberExpression", { aliases: ["Expression", "LVal"], fields: { object: { - validate: assertNodeType("Expression"), + validate: assertNodeType("Expression", "Super"), }, property: { validate: (function () { @@ -1930,9 +1930,14 @@ defineType("SpreadElement", { }, }); -defineType("Super", { - aliases: ["Expression"], -}); +defineType( + "Super", + process.env.BABEL_8_BREAKING + ? undefined + : { + aliases: ["Expression"], + }, +); defineType("TaggedTemplateExpression", { visitor: ["tag", "quasi", "typeParameters"], From 447664472c640d9b9ab9adce4298a15a818c3c54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Tue, 12 Jul 2022 12:10:48 -0400 Subject: [PATCH 3/4] supress or fix typing errors --- .../src/generators/expressions.ts | 4 +-- .../src/index.ts | 2 +- .../src/fields.ts | 8 +++--- .../src/index.ts | 7 ++++- .../src/index.ts | 4 +-- .../src/index.ts | 1 + .../src/index.ts | 3 ++- .../src/transform.ts | 26 ++++++++++++------- .../src/index.ts | 7 ++++- .../src/index.ts | 15 +++++++++-- .../src/index.ts | 9 +++++-- 11 files changed, 61 insertions(+), 25 deletions(-) diff --git a/packages/babel-generator/src/generators/expressions.ts b/packages/babel-generator/src/generators/expressions.ts index c79bf17c9ad3..9ff9c6db032e 100644 --- a/packages/babel-generator/src/generators/expressions.ts +++ b/packages/babel-generator/src/generators/expressions.ts @@ -113,7 +113,7 @@ export function Super(this: Printer) { } function isDecoratorMemberExpression( - node: t.Expression | t.V8IntrinsicIdentifier, + node: t.Expression | t.Super | t.V8IntrinsicIdentifier, ): boolean { switch (node.type) { case "Identifier": @@ -129,7 +129,7 @@ function isDecoratorMemberExpression( } } function shouldParenthesizeDecoratorExpression( - node: t.Expression | t.V8IntrinsicIdentifier, + node: t.Expression | t.Super | t.V8IntrinsicIdentifier, ) { if (node.type === "CallExpression") { node = node.callee; diff --git a/packages/babel-helper-builder-binary-assignment-operator-visitor/src/index.ts b/packages/babel-helper-builder-binary-assignment-operator-visitor/src/index.ts index a98fd672aea5..e82a6c3d921d 100644 --- a/packages/babel-helper-builder-binary-assignment-operator-visitor/src/index.ts +++ b/packages/babel-helper-builder-binary-assignment-operator-visitor/src/index.ts @@ -5,7 +5,7 @@ import type * as t from "@babel/types"; export default function (opts: { build: ( - left: t.Expression | t.PrivateName, + left: t.Expression | t.PrivateName | t.Super, right: t.Expression, ) => t.Expression; operator: t.BinaryExpression["operator"]; diff --git a/packages/babel-helper-create-class-features-plugin/src/fields.ts b/packages/babel-helper-create-class-features-plugin/src/fields.ts index 2b0499f5ceef..79451b0c40ee 100644 --- a/packages/babel-helper-create-class-features-plugin/src/fields.ts +++ b/packages/babel-helper-create-class-features-plugin/src/fields.ts @@ -258,7 +258,7 @@ const privateNameHandlerSpec: Handler & Receiver = { memoise(member, count) { const { scope } = member; - const { object } = member.node; + const { object } = member.node as { object: t.Expression }; const memo = scope.maybeGenerateMemoised(object); if (!memo) { @@ -269,10 +269,10 @@ const privateNameHandlerSpec: Handler & Receiver = }, receiver(member) { - const { object } = member.node; + const { object } = member.node as { object: t.Expression }; if (this.memoiser.has(object)) { - return t.cloneNode(this.memoiser.get(object) as t.Expression); + return t.cloneNode(this.memoiser.get(object)); } return t.cloneNode(object); @@ -466,7 +466,7 @@ const privateNameHandlerLoose: Handler = { boundGet(member) { return t.callExpression( t.memberExpression(this.get(member), t.identifier("bind")), - [t.cloneNode(member.node.object)], + [t.cloneNode(member.node.object as t.Expression)], ); }, diff --git a/packages/babel-helper-member-expression-to-functions/src/index.ts b/packages/babel-helper-member-expression-to-functions/src/index.ts index 436c61c43e38..7f8f74ab361d 100644 --- a/packages/babel-helper-member-expression-to-functions/src/index.ts +++ b/packages/babel-helper-member-expression-to-functions/src/index.ts @@ -279,7 +279,12 @@ const handle = { const { object } = regular; context = member.scope.maybeGenerateMemoised(object); if (context) { - regular.object = assignmentExpression("=", context, object); + regular.object = assignmentExpression( + "=", + context, + // object must not be Super when `context` is an identifier + object as t.Expression, + ); } } diff --git a/packages/babel-helper-skip-transparent-expression-wrappers/src/index.ts b/packages/babel-helper-skip-transparent-expression-wrappers/src/index.ts index e3da1587a8fe..b3066dcc4c72 100644 --- a/packages/babel-helper-skip-transparent-expression-wrappers/src/index.ts +++ b/packages/babel-helper-skip-transparent-expression-wrappers/src/index.ts @@ -43,8 +43,8 @@ export function skipTransparentExprWrappers( } export function skipTransparentExprWrapperNodes( - node: t.Expression, -): t.Expression { + node: t.Expression | t.Super, +): t.Expression | t.Super { while (isTransparentExprWrapper(node)) { node = node.expression; } diff --git a/packages/babel-plugin-proposal-function-bind/src/index.ts b/packages/babel-plugin-proposal-function-bind/src/index.ts index 88cc68a6f240..d82b0fa9d4cb 100644 --- a/packages/babel-plugin-proposal-function-bind/src/index.ts +++ b/packages/babel-plugin-proposal-function-bind/src/index.ts @@ -44,6 +44,7 @@ export default declare(api => { bind.callee.object = t.assignmentExpression( "=", tempId, + // @ts-ignore(Babel 7 vs Babel 8) Fixme: support `super.foo(?)` bind.callee.object, ); } diff --git a/packages/babel-plugin-proposal-logical-assignment-operators/src/index.ts b/packages/babel-plugin-proposal-logical-assignment-operators/src/index.ts index 9695099ac4f4..3e2f14522f23 100644 --- a/packages/babel-plugin-proposal-logical-assignment-operators/src/index.ts +++ b/packages/babel-plugin-proposal-logical-assignment-operators/src/index.ts @@ -27,7 +27,8 @@ export default declare(api => { (lhs as t.MemberExpression).object = t.assignmentExpression( "=", t.cloneNode(memo), - object, + // object must not be Super when `memo` is an identifier + object as t.Expression, ); } diff --git a/packages/babel-plugin-proposal-optional-chaining/src/transform.ts b/packages/babel-plugin-proposal-optional-chaining/src/transform.ts index e9c693592927..11aa7378bc3e 100644 --- a/packages/babel-plugin-proposal-optional-chaining/src/transform.ts +++ b/packages/babel-plugin-proposal-optional-chaining/src/transform.ts @@ -9,7 +9,7 @@ import { willPathCastToBoolean, findOutermostTransparentParent } from "./util"; const { ast } = template.expression; function isSimpleMemberExpression( - expression: t.Expression, + expression: t.Expression | t.Super, ): expression is t.Identifier | t.Super | t.MemberExpression { expression = skipTransparentExprWrapperNodes(expression); return ( @@ -102,6 +102,7 @@ export function transform( } } + // todo: Improve replacementPath typings let replacementPath: NodePath = path; if (parentPath.isUnaryExpression({ operator: "delete" })) { replacementPath = parentPath; @@ -139,8 +140,8 @@ export function transform( t.cloneNode(ref), // Here `chainWithTypes` MUST NOT be cloned because it could be // updated when generating the memoised context of a call - // expression - chainWithTypes, + // expression. It must be an Expression when `ref` is an identifier + chainWithTypes as t.Expression, ); isCall ? (node.callee = ref) : (node.object = ref); @@ -160,13 +161,17 @@ export function transform( // Otherwise, we need to memoize the context object, and change the call into a Function#call. // `a.?b.?()` translates roughly to `(_b = _a.b) != null && _b.call(_a)` const { object } = chain; - let context: t.Expression = scope.maybeGenerateMemoised(object); - if (context) { - chain.object = t.assignmentExpression("=", context, object); - } else if (t.isSuper(object)) { + let context: t.Expression; + if (t.isSuper(object)) { context = t.thisExpression(); } else { - context = object; + const memoized = scope.maybeGenerateMemoised(object); + if (memoized) { + context = memoized; + chain.object = t.assignmentExpression("=", memoized, object); + } else { + context = object; + } } node.arguments.unshift(t.cloneNode(context)); @@ -181,7 +186,10 @@ export function transform( // i.e. `?.b` in `(a?.b.c)()` if (i === 0 && parentIsCall) { // `(a?.b)()` to `(a == null ? undefined : a.b.bind(a))()` - const object = skipTransparentExprWrapperNodes(replacement.object); + // object must not be Super as super?.foo is invalid + const object = skipTransparentExprWrapperNodes( + replacement.object, + ) as t.Expression; let baseRef; if (!pureGetters || !isSimpleMemberExpression(object)) { // memoize the context object when getters are not always pure diff --git a/packages/babel-plugin-proposal-partial-application/src/index.ts b/packages/babel-plugin-proposal-partial-application/src/index.ts index 47fd7a425fff..0b269292d62b 100644 --- a/packages/babel-plugin-proposal-partial-application/src/index.ts +++ b/packages/babel-plugin-proposal-partial-application/src/index.ts @@ -101,7 +101,12 @@ export default declare(api => { scope.push({ id: receiverLVal }); sequenceParts.push( - t.assignmentExpression("=", t.cloneNode(receiverLVal), receiver), + t.assignmentExpression( + "=", + t.cloneNode(receiverLVal), + // @ts-ignore(Babel 7 vs Babel 8) Fixme: support `super.foo(?)` + receiver, + ), t.assignmentExpression( "=", t.cloneNode(functionLVal), diff --git a/packages/babel-plugin-transform-proto-to-assign/src/index.ts b/packages/babel-plugin-transform-proto-to-assign/src/index.ts index 1bd61aeeb755..9afdb6927b17 100644 --- a/packages/babel-plugin-transform-proto-to-assign/src/index.ts +++ b/packages/babel-plugin-transform-proto-to-assign/src/index.ts @@ -31,7 +31,11 @@ export default declare(api => { file: File, ) { return t.expressionStatement( - t.callExpression(file.addHelper("defaults"), [ref, expr.right]), + t.callExpression(file.addHelper("defaults"), [ + // @ts-ignore(Babel 7 vs Babel 8) Fixme: support `super.__proto__ = ...` + ref, + expr.right, + ]), ); } @@ -48,7 +52,14 @@ export default declare(api => { if (temp) { nodes.push( - t.expressionStatement(t.assignmentExpression("=", temp, left)), + t.expressionStatement( + t.assignmentExpression( + "=", + temp, + // left must not be Super when `temp` is an identifier + left as t.Expression, + ), + ), ); } nodes.push( diff --git a/packages/babel-plugin-transform-spread/src/index.ts b/packages/babel-plugin-transform-spread/src/index.ts index 971c99dd1a71..f54259325c54 100644 --- a/packages/babel-plugin-transform-spread/src/index.ts +++ b/packages/babel-plugin-transform-spread/src/index.ts @@ -143,7 +143,7 @@ export default declare((api, options: Options) => { "Please add '@babel/plugin-transform-classes' to your Babel configuration.", ); } - let contextLiteral: t.Expression = scope.buildUndefinedNode(); + let contextLiteral: t.Expression | t.Super = scope.buildUndefinedNode(); node.arguments = []; let nodes: t.Expression[]; @@ -175,7 +175,12 @@ export default declare((api, options: Options) => { if (t.isMemberExpression(callee)) { const temp = scope.maybeGenerateMemoised(callee.object); if (temp) { - callee.object = t.assignmentExpression("=", temp, callee.object); + callee.object = t.assignmentExpression( + "=", + temp, + // object must not be Super when `temp` is an identifier + callee.object as t.Expression, + ); contextLiteral = temp; } else { contextLiteral = t.cloneNode(callee.object); From 3835ae10f4d8e30e9003112ec1306390d0fe5c82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Tue, 12 Jul 2022 15:08:10 -0400 Subject: [PATCH 4/4] fix invalid test --- packages/babel-traverse/test/scope.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-traverse/test/scope.js b/packages/babel-traverse/test/scope.js index 7c229183dd93..ec1f999dad80 100644 --- a/packages/babel-traverse/test/scope.js +++ b/packages/babel-traverse/test/scope.js @@ -653,7 +653,7 @@ describe("scope", () => { describe("duplicate declaration", () => { it("should not throw error on duplicate class and function declaration", () => { const ast = [ - t.classDeclaration(t.identifier("A"), t.super(), t.classBody([]), []), + t.classDeclaration(t.identifier("A"), null, t.classBody([]), []), t.functionDeclaration(t.identifier("A"), [], t.blockStatement([])), ];