Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove Super from Expression alias #14750

Merged
merged 4 commits into from Jul 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/babel-generator/src/generators/expressions.ts
Expand Up @@ -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":
Expand All @@ -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;
Expand Down
Expand Up @@ -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"];
Expand Down
Expand Up @@ -258,7 +258,7 @@ const privateNameHandlerSpec: Handler<PrivateNameState & Receiver> & 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) {
Expand All @@ -269,10 +269,10 @@ const privateNameHandlerSpec: Handler<PrivateNameState & Receiver> & 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);
Expand Down Expand Up @@ -466,7 +466,7 @@ const privateNameHandlerLoose: Handler<PrivateNameState> = {
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)],
);
},

Expand Down
Expand Up @@ -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,
);
}
}

Expand Down
Expand Up @@ -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;
}
Expand Down
1 change: 1 addition & 0 deletions packages/babel-plugin-proposal-function-bind/src/index.ts
Expand Up @@ -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,
);
}
Expand Down
Expand Up @@ -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,
);
}

Expand Down
26 changes: 17 additions & 9 deletions packages/babel-plugin-proposal-optional-chaining/src/transform.ts
Expand Up @@ -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 (
Expand Down Expand Up @@ -102,6 +102,7 @@ export function transform(
}
}

// todo: Improve replacementPath typings
let replacementPath: NodePath<any> = path;
if (parentPath.isUnaryExpression({ operator: "delete" })) {
replacementPath = parentPath;
Expand Down Expand Up @@ -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);
Expand All @@ -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));
Expand All @@ -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
Expand Down
Expand Up @@ -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),
Expand Down
15 changes: 13 additions & 2 deletions packages/babel-plugin-transform-proto-to-assign/src/index.ts
Expand Up @@ -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,
]),
);
}

Expand All @@ -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(
Expand Down
9 changes: 7 additions & 2 deletions packages/babel-plugin-transform-spread/src/index.ts
Expand Up @@ -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[];
Expand Down Expand Up @@ -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,
Comment on lines +178 to +182
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is pretty annoying that we can't have TS narrowed down the object type in such pattern const foo = scope.maybeGenerateMemoised(object); if (foo) { // object must not be Super | TopicReference ... }. I tried conditional type like

maybeGenerateMemoised<T extends t.Node>(object: T): T extends t.Super ? null : t.Identifier

TS can infer maybeGenerateMemoised returns null when object is Super, but it can't infer T from the return results.

);
contextLiteral = temp;
} else {
contextLiteral = t.cloneNode(callee.object);
Expand Down
2 changes: 1 addition & 1 deletion packages/babel-traverse/test/scope.js
Expand Up @@ -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([]), []),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

class A extends super {} is invalid.

t.functionDeclaration(t.identifier("A"), [], t.blockStatement([])),
];

Expand Down
6 changes: 3 additions & 3 deletions packages/babel-types/src/ast-types/generated/index.ts
Expand Up @@ -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
>;
Expand Down Expand Up @@ -544,15 +544,15 @@ 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;
}

export interface NewExpression extends BaseNode {
type: "NewExpression";
callee: Expression | V8IntrinsicIdentifier;
callee: Expression | Super | V8IntrinsicIdentifier;
arguments: Array<
Expression | SpreadElement | JSXNamespacedName | ArgumentPlaceholder
>;
Expand Down
6 changes: 3 additions & 3 deletions packages/babel-types/src/builders/generated/index.ts
Expand Up @@ -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
>,
Expand Down Expand Up @@ -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,
Expand All @@ -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
>,
Expand Down
15 changes: 10 additions & 5 deletions packages/babel-types/src/definitions/core.ts
Expand Up @@ -177,7 +177,7 @@ defineType("CallExpression", {
aliases: ["Expression"],
fields: {
callee: {
validate: assertNodeType("Expression", "V8IntrinsicIdentifier"),
validate: assertNodeType("Expression", "Super", "V8IntrinsicIdentifier"),
},
arguments: {
validate: chain(
Expand Down Expand Up @@ -684,7 +684,7 @@ defineType("MemberExpression", {
aliases: ["Expression", "LVal"],
fields: {
object: {
validate: assertNodeType("Expression"),
validate: assertNodeType("Expression", "Super"),
},
property: {
validate: (function () {
Expand Down Expand Up @@ -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"],
Expand Down
@@ -1,4 +1,5 @@
import { memberExpression } from "../builders/generated";
import { isSuper } from "..";
import type * as t from "..";

/**
Expand All @@ -7,6 +8,11 @@ import type * as t from "..";
export default function prependToMemberExpression<
T extends Pick<t.MemberExpression, "object" | "property">,
>(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;
Expand Down
13 changes: 13 additions & 0 deletions 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`).",
);
});
});