Print parentheses around identifier let where necessary (#13269)
This commit is contained in:
parent
96fce81438
commit
68bc4dfd31
@ -74,7 +74,10 @@ export function ObjectExpression(
|
|||||||
parent: any,
|
parent: any,
|
||||||
printStack: Array<any>,
|
printStack: Array<any>,
|
||||||
): boolean {
|
): boolean {
|
||||||
return isFirstInStatement(printStack, { considerArrow: true });
|
return isFirstInContext(printStack, {
|
||||||
|
expressionStatement: true,
|
||||||
|
arrowBody: true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DoExpression(
|
export function DoExpression(
|
||||||
@ -83,7 +86,9 @@ export function DoExpression(
|
|||||||
printStack: Array<any>,
|
printStack: Array<any>,
|
||||||
): boolean {
|
): boolean {
|
||||||
// `async do` can start an expression statement
|
// `async do` can start an expression statement
|
||||||
return !node.async && isFirstInStatement(printStack);
|
return (
|
||||||
|
!node.async && isFirstInContext(printStack, { expressionStatement: true })
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Binary(node: any, parent: any): boolean {
|
export function Binary(node: any, parent: any): boolean {
|
||||||
@ -214,7 +219,10 @@ export function ClassExpression(
|
|||||||
parent: any,
|
parent: any,
|
||||||
printStack: Array<any>,
|
printStack: Array<any>,
|
||||||
): boolean {
|
): boolean {
|
||||||
return isFirstInStatement(printStack, { considerDefaultExports: true });
|
return isFirstInContext(printStack, {
|
||||||
|
expressionStatement: true,
|
||||||
|
exportDefault: true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function UnaryLike(node: any, parent: any): boolean {
|
export function UnaryLike(node: any, parent: any): boolean {
|
||||||
@ -230,7 +238,10 @@ export function FunctionExpression(
|
|||||||
parent: any,
|
parent: any,
|
||||||
printStack: Array<any>,
|
printStack: Array<any>,
|
||||||
): boolean {
|
): boolean {
|
||||||
return isFirstInStatement(printStack, { considerDefaultExports: true });
|
return isFirstInContext(printStack, {
|
||||||
|
expressionStatement: true,
|
||||||
|
exportDefault: true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ArrowFunctionExpression(node: any, parent: any): boolean {
|
export function ArrowFunctionExpression(node: any, parent: any): boolean {
|
||||||
@ -281,7 +292,34 @@ export function LogicalExpression(node: any, parent: any): boolean {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Identifier(node: t.Identifier, parent: t.Node): boolean {
|
export function Identifier(
|
||||||
|
node: t.Identifier,
|
||||||
|
parent: t.Node,
|
||||||
|
printStack: Array<t.Node>,
|
||||||
|
): boolean {
|
||||||
|
// Non-strict code allows the identifier `let`, but it cannot occur as-is in
|
||||||
|
// certain contexts to avoid ambiguity with contextual keyword `let`.
|
||||||
|
if (node.name === "let") {
|
||||||
|
// Some contexts only forbid `let [`, so check if the next token would
|
||||||
|
// be the left bracket of a computed member expression.
|
||||||
|
const isFollowedByBracket =
|
||||||
|
t.isMemberExpression(parent, {
|
||||||
|
object: node,
|
||||||
|
computed: true,
|
||||||
|
}) ||
|
||||||
|
t.isOptionalMemberExpression(parent, {
|
||||||
|
object: node,
|
||||||
|
computed: true,
|
||||||
|
optional: false,
|
||||||
|
});
|
||||||
|
return isFirstInContext(printStack, {
|
||||||
|
expressionStatement: isFollowedByBracket,
|
||||||
|
forHead: isFollowedByBracket,
|
||||||
|
forInHead: isFollowedByBracket,
|
||||||
|
forOfHead: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// ECMAScript specifically forbids a for-of loop from starting with the
|
// ECMAScript specifically forbids a for-of loop from starting with the
|
||||||
// token sequence `for (async of`, because it would be ambiguous with
|
// token sequence `for (async of`, because it would be ambiguous with
|
||||||
// `for (async of => {};;)`, so we need to add extra parentheses.
|
// `for (async of => {};;)`, so we need to add extra parentheses.
|
||||||
@ -296,10 +334,17 @@ export function Identifier(node: t.Identifier, parent: t.Node): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Walk up the print stack to determine if our node can come first
|
// Walk up the print stack to determine if our node can come first
|
||||||
// in statement.
|
// in a particular context.
|
||||||
function isFirstInStatement(
|
function isFirstInContext(
|
||||||
printStack: Array<any>,
|
printStack: Array<t.Node>,
|
||||||
{ considerArrow = false, considerDefaultExports = false } = {},
|
{
|
||||||
|
expressionStatement = false,
|
||||||
|
arrowBody = false,
|
||||||
|
exportDefault = false,
|
||||||
|
forHead = false,
|
||||||
|
forInHead = false,
|
||||||
|
forOfHead = false,
|
||||||
|
},
|
||||||
): boolean {
|
): boolean {
|
||||||
let i = printStack.length - 1;
|
let i = printStack.length - 1;
|
||||||
let node = printStack[i];
|
let node = printStack[i];
|
||||||
@ -307,10 +352,14 @@ function isFirstInStatement(
|
|||||||
let parent = printStack[i];
|
let parent = printStack[i];
|
||||||
while (i >= 0) {
|
while (i >= 0) {
|
||||||
if (
|
if (
|
||||||
t.isExpressionStatement(parent, { expression: node }) ||
|
(expressionStatement &&
|
||||||
(considerDefaultExports &&
|
t.isExpressionStatement(parent, { expression: node })) ||
|
||||||
|
(exportDefault &&
|
||||||
t.isExportDefaultDeclaration(parent, { declaration: node })) ||
|
t.isExportDefaultDeclaration(parent, { declaration: node })) ||
|
||||||
(considerArrow && t.isArrowFunctionExpression(parent, { body: node }))
|
(arrowBody && t.isArrowFunctionExpression(parent, { body: node })) ||
|
||||||
|
(forHead && t.isForStatement(parent, { init: node })) ||
|
||||||
|
(forInHead && t.isForInStatement(parent, { left: node })) ||
|
||||||
|
(forOfHead && t.isForOfStatement(parent, { left: node }))
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
31
packages/babel-generator/test/fixtures/edgecase/let-identifier/input.js
vendored
Normal file
31
packages/babel-generator/test/fixtures/edgecase/let-identifier/input.js
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/* ExpressionStatement */
|
||||||
|
let;
|
||||||
|
\u006cet[x];
|
||||||
|
(let)[x];
|
||||||
|
a[let[x]];
|
||||||
|
|
||||||
|
/* ForStatement */
|
||||||
|
for (let;;);
|
||||||
|
for (\u006cet[x];;);
|
||||||
|
for ((let)[x];;);
|
||||||
|
for (a[let[x]];;);
|
||||||
|
|
||||||
|
/* ForInStatement */
|
||||||
|
for (let in {});
|
||||||
|
for (\u006cet[x] in {});
|
||||||
|
for ((let)[x] in {});
|
||||||
|
for (a[let[x]] in {});
|
||||||
|
|
||||||
|
/* ForOfStatement { await: false } */
|
||||||
|
for ((let) of []);
|
||||||
|
for (\u006cet of []);
|
||||||
|
for ((let)[x] of []);
|
||||||
|
for (a[let] of []);
|
||||||
|
|
||||||
|
/* ForOfStatement { await: true } */
|
||||||
|
async () => {
|
||||||
|
for await ((let) of []);
|
||||||
|
for await (\u006cet of []);
|
||||||
|
for await ((let)[x] of []);
|
||||||
|
for await (a[let] of []);
|
||||||
|
}
|
||||||
4
packages/babel-generator/test/fixtures/edgecase/let-identifier/options.json
vendored
Normal file
4
packages/babel-generator/test/fixtures/edgecase/let-identifier/options.json
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"sourceType": "script",
|
||||||
|
"strictMode": false
|
||||||
|
}
|
||||||
46
packages/babel-generator/test/fixtures/edgecase/let-identifier/output.js
vendored
Normal file
46
packages/babel-generator/test/fixtures/edgecase/let-identifier/output.js
vendored
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/* ExpressionStatement */
|
||||||
|
let;
|
||||||
|
(let)[x];
|
||||||
|
(let)[x];
|
||||||
|
a[let[x]];
|
||||||
|
/* ForStatement */
|
||||||
|
|
||||||
|
for (let;;);
|
||||||
|
|
||||||
|
for ((let)[x];;);
|
||||||
|
|
||||||
|
for ((let)[x];;);
|
||||||
|
|
||||||
|
for (a[let[x]];;);
|
||||||
|
/* ForInStatement */
|
||||||
|
|
||||||
|
|
||||||
|
for (let in {});
|
||||||
|
|
||||||
|
for ((let)[x] in {});
|
||||||
|
|
||||||
|
for ((let)[x] in {});
|
||||||
|
|
||||||
|
for (a[let[x]] in {});
|
||||||
|
/* ForOfStatement { await: false } */
|
||||||
|
|
||||||
|
|
||||||
|
for ((let) of []);
|
||||||
|
|
||||||
|
for ((let) of []);
|
||||||
|
|
||||||
|
for ((let)[x] of []);
|
||||||
|
|
||||||
|
for (a[let] of []);
|
||||||
|
/* ForOfStatement { await: true } */
|
||||||
|
|
||||||
|
|
||||||
|
async () => {
|
||||||
|
for await ((let) of []);
|
||||||
|
|
||||||
|
for await ((let) of []);
|
||||||
|
|
||||||
|
for await ((let)[x] of []);
|
||||||
|
|
||||||
|
for await (a[let] of []);
|
||||||
|
};
|
||||||
@ -763,6 +763,18 @@ describe("programmatic generation", function () {
|
|||||||
expect(output).toBe("interface A {}");
|
expect(output).toBe("interface A {}");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("identifier let", () => {
|
||||||
|
it("detects open bracket from non-optional OptionalMemberExpression", () => {
|
||||||
|
const ast = parse(`for (let?.[x];;);`, {
|
||||||
|
sourceType: "script",
|
||||||
|
strictMode: "false",
|
||||||
|
});
|
||||||
|
ast.program.body[0].init.optional = false;
|
||||||
|
const output = generate(ast).code;
|
||||||
|
expect(output).toBe("for ((let)[x];;);");
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("CodeGenerator", function () {
|
describe("CodeGenerator", function () {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user