diff --git a/README.md b/README.md index 943299e12..4076b3ad5 100644 --- a/README.md +++ b/README.md @@ -38,11 +38,11 @@
-
+
-
+
-
+
diff --git a/changelog_unreleased/javascript/14000.md b/changelog_unreleased/javascript/14000.md
new file mode 100644
index 000000000..914800969
--- /dev/null
+++ b/changelog_unreleased/javascript/14000.md
@@ -0,0 +1,19 @@
+#### Fix missing parentheses when an expression statement starts with `let[` (#14000, #14044 by @fisker, @thorn0)
+
+
+```jsx
+// Input
+(let[0] = 2);
+
+// Prettier stable
+let[0] = 2;
+
+// Prettier stable (second format)
+SyntaxError: Unexpected token (1:5)
+> 1 | let[0] = 2;
+ | ^
+ 2 |
+
+// Prettier main
+(let)[0] = 2;
+```
diff --git a/changelog_unreleased/typescript/14049.md b/changelog_unreleased/typescript/14049.md
new file mode 100644
index 000000000..28448fa96
--- /dev/null
+++ b/changelog_unreleased/typescript/14049.md
@@ -0,0 +1,10 @@
+#### Support type annotations on auto accessors via `babel-ts` (#14049 by @sosukesuzuki)
+
+[The bug that `@babel/parser` cannot parse auto accessors with type annotations](https://github.com/babel/babel/issues/15205) has been fixed. So we now support it via `babel-ts` parser.
+
+
+```tsx
+class Foo {
+ accessor prop: number;
+}
+```
diff --git a/src/language-js/needs-parens.js b/src/language-js/needs-parens.js
index 46210197e..b3d090684 100644
--- a/src/language-js/needs-parens.js
+++ b/src/language-js/needs-parens.js
@@ -50,16 +50,64 @@ function needsParens(path, options) {
return true;
}
- // `for (async of []);` is invalid
+ // `for ((async) of []);` and `for ((let) of []);`
if (
key === "left" &&
- node.name === "async" &&
+ (node.name === "async" || node.name === "let") &&
parent.type === "ForOfStatement" &&
!parent.await
) {
return true;
}
+ // `for ((let.a) of []);`
+ if (node.name === "let") {
+ const expression = path.findAncestor(
+ (node) => node.type === "ForOfStatement"
+ )?.left;
+ if (
+ expression &&
+ startsWithNoLookaheadToken(
+ expression,
+ (leftmostNode) => leftmostNode === node
+ )
+ ) {
+ return true;
+ }
+ }
+
+ // `(let)[a] = 1`
+ if (
+ key === "object" &&
+ node.name === "let" &&
+ parent.type === "MemberExpression" &&
+ parent.computed &&
+ !parent.optional
+ ) {
+ const statement = path.findAncestor(
+ (node) =>
+ node.type === "ExpressionStatement" ||
+ node.type === "ForStatement" ||
+ node.type === "ForInStatement"
+ );
+ const expression = !statement
+ ? undefined
+ : statement.type === "ExpressionStatement"
+ ? statement.expression
+ : statement.type === "ForStatement"
+ ? statement.init
+ : statement.left;
+ if (
+ expression &&
+ startsWithNoLookaheadToken(
+ expression,
+ (leftmostNode) => leftmostNode === node
+ )
+ ) {
+ return true;
+ }
+ }
+
return false;
}
@@ -140,7 +188,11 @@ function needsParens(path, options) {
if (
startsWithNoLookaheadToken(
node,
- /* forbidFunctionClassAndDoExpr */ true
+ (node) =>
+ node.type === "ObjectExpression" ||
+ node.type === "FunctionExpression" ||
+ node.type === "ClassExpression" ||
+ node.type === "DoExpression"
)
) {
return true;
@@ -153,7 +205,7 @@ function needsParens(path, options) {
node.type !== "SequenceExpression" && // these have parens added anyway
startsWithNoLookaheadToken(
node,
- /* forbidFunctionClassAndDoExpr */ false
+ (node) => node.type === "ObjectExpression"
)
) {
return true;
diff --git a/src/language-js/print/function.js b/src/language-js/print/function.js
index 75bb53720..15ab423b4 100644
--- a/src/language-js/print/function.js
+++ b/src/language-js/print/function.js
@@ -373,7 +373,10 @@ function printArrowFunction(path, options, print, args) {
// a <= a ? a : a
const shouldAddParens =
node.body.type === "ConditionalExpression" &&
- !startsWithNoLookaheadToken(node.body, /* forbidFunctionAndClass */ false);
+ !startsWithNoLookaheadToken(
+ node.body,
+ (node) => node.type === "ObjectExpression"
+ );
return group([
...parts,
diff --git a/src/language-js/utils/index.js b/src/language-js/utils/index.js
index c81646aba..d251ae0cd 100644
--- a/src/language-js/utils/index.js
+++ b/src/language-js/utils/index.js
@@ -838,74 +838,59 @@ function shouldPrintComma(options, level = "es5") {
}
/**
- * Tests if an expression starts with `{`, or (if forbidFunctionClassAndDoExpr
- * holds) `function`, `class`, or `do {}`. Will be overzealous if there's
- * already necessary grouping parentheses.
+ * Tests if the leftmost node of the expression matches the predicate. E.g.,
+ * used to check whether an expression statement needs to be wrapped in extra
+ * parentheses because it starts with:
+ *
+ * - `{`
+ * - `function`, `class`, or `do {}`
+ * - `let[`
+ *
+ * Will be overzealous if there already are necessary grouping parentheses.
*
* @param {Node} node
- * @param {boolean} forbidFunctionClassAndDoExpr
+ * @param {(leftmostNode: Node) => boolean} predicate
* @returns {boolean}
*/
-function startsWithNoLookaheadToken(node, forbidFunctionClassAndDoExpr) {
- node = getLeftMost(node);
+function startsWithNoLookaheadToken(node, predicate) {
switch (node.type) {
- case "FunctionExpression":
- case "ClassExpression":
- case "DoExpression":
- return forbidFunctionClassAndDoExpr;
- case "ObjectExpression":
- return true;
+ case "BinaryExpression":
+ case "LogicalExpression":
+ case "AssignmentExpression":
+ case "NGPipeExpression":
+ return startsWithNoLookaheadToken(node.left, predicate);
case "MemberExpression":
case "OptionalMemberExpression":
- return startsWithNoLookaheadToken(
- node.object,
- forbidFunctionClassAndDoExpr
- );
+ return startsWithNoLookaheadToken(node.object, predicate);
case "TaggedTemplateExpression":
if (node.tag.type === "FunctionExpression") {
// IIFEs are always already parenthesized
return false;
}
- return startsWithNoLookaheadToken(node.tag, forbidFunctionClassAndDoExpr);
+ return startsWithNoLookaheadToken(node.tag, predicate);
case "CallExpression":
case "OptionalCallExpression":
if (node.callee.type === "FunctionExpression") {
// IIFEs are always already parenthesized
return false;
}
- return startsWithNoLookaheadToken(
- node.callee,
- forbidFunctionClassAndDoExpr
- );
+ return startsWithNoLookaheadToken(node.callee, predicate);
case "ConditionalExpression":
- return startsWithNoLookaheadToken(
- node.test,
- forbidFunctionClassAndDoExpr
- );
+ return startsWithNoLookaheadToken(node.test, predicate);
case "UpdateExpression":
return (
- !node.prefix &&
- startsWithNoLookaheadToken(node.argument, forbidFunctionClassAndDoExpr)
+ !node.prefix && startsWithNoLookaheadToken(node.argument, predicate)
);
case "BindExpression":
- return (
- node.object &&
- startsWithNoLookaheadToken(node.object, forbidFunctionClassAndDoExpr)
- );
+ return node.object && startsWithNoLookaheadToken(node.object, predicate);
case "SequenceExpression":
- return startsWithNoLookaheadToken(
- node.expressions[0],
- forbidFunctionClassAndDoExpr
- );
- case "TSAsExpression":
+ return startsWithNoLookaheadToken(node.expressions[0], predicate);
case "TSSatisfiesExpression":
+ case "TSAsExpression":
case "TSNonNullExpression":
- return startsWithNoLookaheadToken(
- node.expression,
- forbidFunctionClassAndDoExpr
- );
+ return startsWithNoLookaheadToken(node.expression, predicate);
default:
- return false;
+ return predicate(node);
}
}
@@ -991,13 +976,6 @@ function getPrecedence(operator) {
return PRECEDENCE.get(operator);
}
-function getLeftMost(node) {
- while (node.left) {
- node = node.left;
- }
- return node;
-}
-
function isBitwiseOperator(operator) {
return (
Boolean(bitshiftOperators[operator]) ||
diff --git a/tests/config/format-test.js b/tests/config/format-test.js
index dd7ed067b..00545ed37 100644
--- a/tests/config/format-test.js
+++ b/tests/config/format-test.js
@@ -39,6 +39,7 @@ const unstableTests = new Map(
"js/for/continue-and-break-comment-without-blocks.js",
"js/sequence-expression/parenthesized.js",
"typescript/satisfies-operators/comments-unstable.ts",
+ ["js/identifier/parentheses/let.js", (options) => options.semi === false],
].map((fixture) => {
const [file, isUnstable = () => true] = Array.isArray(fixture)
? fixture
diff --git a/tests/format/js/identifier/for-of/__snapshots__/jsfmt.spec.js.snap b/tests/format/js/identifier/for-of/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000..1ccc4806b
--- /dev/null
+++ b/tests/format/js/identifier/for-of/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,57 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`await.js format 1`] = `
+====================================options=====================================
+parsers: ["babel", "typescript"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+async function a() {
+ for await((let).a of foo);
+ for await((let)[a] of foo);
+ for await((let)()[a] of foo);
+}
+
+=====================================output=====================================
+async function a() {
+ for await ((let).a of foo);
+ for await ((let)[a] of foo);
+ for await ((let)()[a] of foo);
+}
+
+================================================================================
+`;
+
+exports[`let.js format 1`] = `
+====================================options=====================================
+parsers: ["babel", "typescript"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+for ((let) of foo);
+for (foo of let);
+for (foo of let.a);
+for (foo of let[a]);
+for ((let.a) of foo);
+for ((let[a]) of foo);
+for ((let)().a of foo);
+for (letFoo of foo);
+
+for ((let.a) in foo);
+for ((let[a]) in foo);
+
+=====================================output=====================================
+for ((let) of foo);
+for (foo of let);
+for (foo of let.a);
+for (foo of let[a]);
+for ((let).a of foo);
+for ((let)[a] of foo);
+for ((let)().a of foo);
+for (letFoo of foo);
+
+for (let.a in foo);
+for ((let)[a] in foo);
+
+================================================================================
+`;
diff --git a/tests/format/js/identifier/for-of/await.js b/tests/format/js/identifier/for-of/await.js
new file mode 100644
index 000000000..0fa9d0a50
--- /dev/null
+++ b/tests/format/js/identifier/for-of/await.js
@@ -0,0 +1,5 @@
+async function a() {
+ for await((let).a of foo);
+ for await((let)[a] of foo);
+ for await((let)()[a] of foo);
+}
diff --git a/tests/format/js/identifier/for-of/jsfmt.spec.js b/tests/format/js/identifier/for-of/jsfmt.spec.js
new file mode 100644
index 000000000..0e6664ec9
--- /dev/null
+++ b/tests/format/js/identifier/for-of/jsfmt.spec.js
@@ -0,0 +1,5 @@
+run_spec(import.meta, [
+ "babel",
+ // "flow",
+ "typescript",
+]);
diff --git a/tests/format/js/identifier/for-of/let.js b/tests/format/js/identifier/for-of/let.js
new file mode 100644
index 000000000..5c2398ee2
--- /dev/null
+++ b/tests/format/js/identifier/for-of/let.js
@@ -0,0 +1,11 @@
+for ((let) of foo);
+for (foo of let);
+for (foo of let.a);
+for (foo of let[a]);
+for ((let.a) of foo);
+for ((let[a]) of foo);
+for ((let)().a of foo);
+for (letFoo of foo);
+
+for ((let.a) in foo);
+for ((let[a]) in foo);
diff --git a/tests/format/js/identifier/parentheses/__snapshots__/jsfmt.spec.js.snap b/tests/format/js/identifier/parentheses/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000..6d0419713
--- /dev/null
+++ b/tests/format/js/identifier/parentheses/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,481 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`const.js - {"semi":false} format 1`] = `
+====================================options=====================================
+parsers: ["babel", "typescript"]
+printWidth: 80
+semi: false
+ | printWidth
+=====================================input======================================
+const [a = ((let)[0] = 1)] = 2;
+
+=====================================output=====================================
+const [a = (let[0] = 1)] = 2
+
+================================================================================
+`;
+
+exports[`const.js format 1`] = `
+====================================options=====================================
+parsers: ["babel", "typescript"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+const [a = ((let)[0] = 1)] = 2;
+
+=====================================output=====================================
+const [a = (let[0] = 1)] = 2;
+
+================================================================================
+`;
+
+exports[`let.js - {"semi":false} format 1`] = `
+====================================options=====================================
+parsers: ["babel", "typescript"]
+printWidth: 80
+semi: false
+ | printWidth
+=====================================input======================================
+let.a = 1;
+
+let.a[0] = 1;
+
+(let[a] = 1);
+
+(let[a].b.c.e = 1);
+
+foo[let[a]] = 1;
+
+(let)[let[a]] = 1;
+
+(let[a] ??= 1);
+
+foo = let[a];
+
+let()[a] = 1;
+
+foo(let)[a] = 1;
+
+foo(let[a])[a] = 1;
+
+(let[0] = 1);
+
+(let["a"] = 1);
+
+let = 1;
+
+var let = 1;
+
+[let[a]] = 1;
+
+({a: let[a]} = 1);
+
+alert(let[0] = 1);
+
+(let[0] = 1) || 2;
+
+((let[0] = 1), 2);
+
+((let[0] = 1) ? a : b);
+
+if (let[0] = 1);
+
+while (let[0] = 1);
+
+do{} while (let[0] = 1);
+
+var a = (let[0] = 1);
+
+(let[0] = 1) instanceof a;
+
+void (let[0] = 1);
+
+(let[0] = 1)();
+
+new (let[0] = 1)();
+
+((let)[0] = 1)\`\`;
+
+((let)[0] = 1).toString;
+
+((let)[0] = 1)?.toString;
+
+[...(let[0] = 1)];
+
+foo = () => (let[0] = 1);
+
+function * foo() {yield (let[0] = 1)}
+
+async function foo() {await (let[0] = 1)}
+
+function foo() {return (let[0] = 1)}
+
+while (true) (let[0] = 1);
+
+throw (let[0] = 1);
+
+({foo: (let[0] = 1)});
+
+[(let[0] = 1)];
+
+for ((let[0] = 1);;);
+for ((let)[0] in {});
+for ((let)[0] of []);
+
+switch (let[0] = 1) {}
+
+switch (foo) {
+ case let[0] = 1:
+}
+
+with (let[0] = 1);
+
+(let[x]).foo();
+
+let.let[x].foo();
+
+a = let[x].foo();
+
+(let)[2];
+
+a[1] + (let[2] = 2);
+
+=====================================output=====================================
+let.a = 1
+
+let.a[0] = 1
+
+;(let)[a] = 1
+
+;(let)[a].b.c.e = 1
+
+foo[let[a]] = 1
+
+;(let)[let[a]] = 1
+
+;(let)[a] ??= 1
+
+foo = let[a]
+
+let()[a] = 1
+
+foo(let)[a] = 1
+
+foo(let[a])[a] = 1
+
+;(let)[0] = 1
+
+;(let)["a"] = 1
+
+let = 1
+
+var let = 1
+
+;[let[a]] = 1
+
+;({ a: let[a] } = 1)
+
+alert((let[0] = 1))
+
+;((let)[0] = 1) || 2
+
+;((let)[0] = 1), 2
+
+;((let)[0] = 1) ? a : b
+
+if ((let[0] = 1));
+
+while ((let[0] = 1));
+
+do {} while ((let[0] = 1))
+
+var a = (let[0] = 1)
+
+;((let)[0] = 1) instanceof a
+
+void (let[0] = 1)
+
+;((let)[0] = 1)()
+
+new (let[0] = 1)()
+
+;((let)[0] = 1)\`\`
+
+;((let)[0] = 1).toString
+
+;((let)[0] = 1)?.toString
+
+;[...(let[0] = 1)]
+
+foo = () => (let[0] = 1)
+
+function* foo() {
+ yield (let[0] = 1)
+}
+
+async function foo() {
+ await (let[0] = 1)
+}
+
+function foo() {
+ return (let[0] = 1)
+}
+
+while (true) (let)[0] = 1
+
+throw (let[0] = 1)
+
+;({ foo: (let[0] = 1) })
+
+;[(let[0] = 1)]
+
+for ((let)[0] = 1; ; );
+for ((let)[0] in {});
+for ((let)[0] of []);
+
+switch ((let[0] = 1)) {
+}
+
+switch (foo) {
+ case (let[0] = 1):
+}
+
+with ((let[0] = 1));
+
+;(let)[x].foo()
+
+let.let[x].foo()
+
+a = let[x].foo()
+
+;(let)[2]
+
+a[1] + (let[2] = 2)
+
+================================================================================
+`;
+
+exports[`let.js format 1`] = `
+====================================options=====================================
+parsers: ["babel", "typescript"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+let.a = 1;
+
+let.a[0] = 1;
+
+(let[a] = 1);
+
+(let[a].b.c.e = 1);
+
+foo[let[a]] = 1;
+
+(let)[let[a]] = 1;
+
+(let[a] ??= 1);
+
+foo = let[a];
+
+let()[a] = 1;
+
+foo(let)[a] = 1;
+
+foo(let[a])[a] = 1;
+
+(let[0] = 1);
+
+(let["a"] = 1);
+
+let = 1;
+
+var let = 1;
+
+[let[a]] = 1;
+
+({a: let[a]} = 1);
+
+alert(let[0] = 1);
+
+(let[0] = 1) || 2;
+
+((let[0] = 1), 2);
+
+((let[0] = 1) ? a : b);
+
+if (let[0] = 1);
+
+while (let[0] = 1);
+
+do{} while (let[0] = 1);
+
+var a = (let[0] = 1);
+
+(let[0] = 1) instanceof a;
+
+void (let[0] = 1);
+
+(let[0] = 1)();
+
+new (let[0] = 1)();
+
+((let)[0] = 1)\`\`;
+
+((let)[0] = 1).toString;
+
+((let)[0] = 1)?.toString;
+
+[...(let[0] = 1)];
+
+foo = () => (let[0] = 1);
+
+function * foo() {yield (let[0] = 1)}
+
+async function foo() {await (let[0] = 1)}
+
+function foo() {return (let[0] = 1)}
+
+while (true) (let[0] = 1);
+
+throw (let[0] = 1);
+
+({foo: (let[0] = 1)});
+
+[(let[0] = 1)];
+
+for ((let[0] = 1);;);
+for ((let)[0] in {});
+for ((let)[0] of []);
+
+switch (let[0] = 1) {}
+
+switch (foo) {
+ case let[0] = 1:
+}
+
+with (let[0] = 1);
+
+(let[x]).foo();
+
+let.let[x].foo();
+
+a = let[x].foo();
+
+(let)[2];
+
+a[1] + (let[2] = 2);
+
+=====================================output=====================================
+let.a = 1;
+
+let.a[0] = 1;
+
+(let)[a] = 1;
+
+(let)[a].b.c.e = 1;
+
+foo[let[a]] = 1;
+
+(let)[let[a]] = 1;
+
+(let)[a] ??= 1;
+
+foo = let[a];
+
+let()[a] = 1;
+
+foo(let)[a] = 1;
+
+foo(let[a])[a] = 1;
+
+(let)[0] = 1;
+
+(let)["a"] = 1;
+
+let = 1;
+
+var let = 1;
+
+[let[a]] = 1;
+
+({ a: let[a] } = 1);
+
+alert((let[0] = 1));
+
+((let)[0] = 1) || 2;
+
+((let)[0] = 1), 2;
+
+((let)[0] = 1) ? a : b;
+
+if ((let[0] = 1));
+
+while ((let[0] = 1));
+
+do {} while ((let[0] = 1));
+
+var a = (let[0] = 1);
+
+((let)[0] = 1) instanceof a;
+
+void (let[0] = 1);
+
+((let)[0] = 1)();
+
+new (let[0] = 1)();
+
+((let)[0] = 1)\`\`;
+
+((let)[0] = 1).toString;
+
+((let)[0] = 1)?.toString;
+
+[...(let[0] = 1)];
+
+foo = () => (let[0] = 1);
+
+function* foo() {
+ yield (let[0] = 1);
+}
+
+async function foo() {
+ await (let[0] = 1);
+}
+
+function foo() {
+ return (let[0] = 1);
+}
+
+while (true) (let)[0] = 1;
+
+throw (let[0] = 1);
+
+({ foo: (let[0] = 1) });
+
+[(let[0] = 1)];
+
+for ((let)[0] = 1; ; );
+for ((let)[0] in {});
+for ((let)[0] of []);
+
+switch ((let[0] = 1)) {
+}
+
+switch (foo) {
+ case (let[0] = 1):
+}
+
+with ((let[0] = 1));
+
+(let)[x].foo();
+
+let.let[x].foo();
+
+a = let[x].foo();
+
+(let)[2];
+
+a[1] + (let[2] = 2);
+
+================================================================================
+`;
diff --git a/tests/format/js/identifier/parentheses/const.js b/tests/format/js/identifier/parentheses/const.js
new file mode 100644
index 000000000..8fa111711
--- /dev/null
+++ b/tests/format/js/identifier/parentheses/const.js
@@ -0,0 +1 @@
+const [a = ((let)[0] = 1)] = 2;
diff --git a/tests/format/js/identifier/parentheses/jsfmt.spec.js b/tests/format/js/identifier/parentheses/jsfmt.spec.js
new file mode 100644
index 000000000..202ed2340
--- /dev/null
+++ b/tests/format/js/identifier/parentheses/jsfmt.spec.js
@@ -0,0 +1,14 @@
+run_spec(import.meta, [
+ "babel",
+ // "flow",
+ "typescript",
+]);
+run_spec(
+ import.meta,
+ [
+ "babel",
+ // "flow",
+ "typescript",
+ ],
+ { semi: false }
+);
diff --git a/tests/format/js/identifier/parentheses/let.js b/tests/format/js/identifier/parentheses/let.js
new file mode 100644
index 000000000..2f0cc8a37
--- /dev/null
+++ b/tests/format/js/identifier/parentheses/let.js
@@ -0,0 +1,103 @@
+let.a = 1;
+
+let.a[0] = 1;
+
+(let[a] = 1);
+
+(let[a].b.c.e = 1);
+
+foo[let[a]] = 1;
+
+(let)[let[a]] = 1;
+
+(let[a] ??= 1);
+
+foo = let[a];
+
+let()[a] = 1;
+
+foo(let)[a] = 1;
+
+foo(let[a])[a] = 1;
+
+(let[0] = 1);
+
+(let["a"] = 1);
+
+let = 1;
+
+var let = 1;
+
+[let[a]] = 1;
+
+({a: let[a]} = 1);
+
+alert(let[0] = 1);
+
+(let[0] = 1) || 2;
+
+((let[0] = 1), 2);
+
+((let[0] = 1) ? a : b);
+
+if (let[0] = 1);
+
+while (let[0] = 1);
+
+do{} while (let[0] = 1);
+
+var a = (let[0] = 1);
+
+(let[0] = 1) instanceof a;
+
+void (let[0] = 1);
+
+(let[0] = 1)();
+
+new (let[0] = 1)();
+
+((let)[0] = 1)``;
+
+((let)[0] = 1).toString;
+
+((let)[0] = 1)?.toString;
+
+[...(let[0] = 1)];
+
+foo = () => (let[0] = 1);
+
+function * foo() {yield (let[0] = 1)}
+
+async function foo() {await (let[0] = 1)}
+
+function foo() {return (let[0] = 1)}
+
+while (true) (let[0] = 1);
+
+throw (let[0] = 1);
+
+({foo: (let[0] = 1)});
+
+[(let[0] = 1)];
+
+for ((let[0] = 1);;);
+for ((let)[0] in {});
+for ((let)[0] of []);
+
+switch (let[0] = 1) {}
+
+switch (foo) {
+ case let[0] = 1:
+}
+
+with (let[0] = 1);
+
+(let[x]).foo();
+
+let.let[x].foo();
+
+a = let[x].foo();
+
+(let)[2];
+
+a[1] + (let[2] = 2);
diff --git a/tests/format/misc/errors/js/assignment/__snapshots__/jsfmt.spec.js.snap b/tests/format/misc/errors/js/assignment/__snapshots__/jsfmt.spec.js.snap
index 1ece57b5b..f4b45b707 100644
--- a/tests/format/misc/errors/js/assignment/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/format/misc/errors/js/assignment/__snapshots__/jsfmt.spec.js.snap
@@ -6,26 +6,110 @@ exports[`snippet: #0 [acorn] format 1`] = `
| ^"
`;
+exports[`snippet: #0 [acorn] format 2`] = `
+"The keyword 'let' is reserved (1:1)
+> 1 | let?.()[a] =1
+ | ^"
+`;
+
+exports[`snippet: #0 [acorn] format 3`] = `
+"The keyword 'let' is reserved (1:21)
+> 1 | class Foo extends ((let)[0] = 1) {}
+ | ^"
+`;
+
exports[`snippet: #0 [babel] format 1`] = `
"Invalid parenthesized assignment pattern. (1:1)
> 1 | ({}) = x;
| ^"
`;
+exports[`snippet: #0 [babel] format 2`] = `
+"Invalid left-hand side in assignment expression. (1:1)
+> 1 | let?.()[a] =1
+ | ^"
+`;
+
exports[`snippet: #0 [babel-ts] format 1`] = `
"Invalid parenthesized assignment pattern. (1:1)
> 1 | ({}) = x;
| ^"
`;
+exports[`snippet: #0 [babel-ts] format 2`] = `
+"Invalid left-hand side in assignment expression. (1:1)
+> 1 | let?.()[a] =1
+ | ^"
+`;
+
exports[`snippet: #0 [espree] format 1`] = `
"Assigning to rvalue (1:1)
> 1 | ({}) = x;
| ^"
`;
+exports[`snippet: #0 [espree] format 2`] = `
+"The keyword 'let' is reserved (1:1)
+> 1 | let?.()[a] =1
+ | ^"
+`;
+
+exports[`snippet: #0 [espree] format 3`] = `
+"The keyword 'let' is reserved (1:21)
+> 1 | class Foo extends ((let)[0] = 1) {}
+ | ^"
+`;
+
+exports[`snippet: #0 [espree] format 4`] = `
+"The keyword 'let' is reserved (1:18)
+> 1 | export default ((let)[0] = 1);
+ | ^"
+`;
+
exports[`snippet: #0 [meriyah] format 1`] = `
"Invalid left-hand side in assignment (1:6)
> 1 | ({}) = x;
| ^"
`;
+
+exports[`snippet: #0 [meriyah] format 2`] = `
+"The identifier 'let' must not be in expression position in strict mode (1:24)
+> 1 | class Foo extends ((let)[0] = 1) {}
+ | ^"
+`;
+
+exports[`snippet: #0 [meriyah] format 3`] = `
+"The identifier 'let' must not be in expression position in strict mode (1:21)
+> 1 | export default ((let)[0] = 1);
+ | ^"
+`;
+
+exports[`snippet: #1 [acorn] format 1`] = `
+"The keyword 'let' is reserved (1:1)
+> 1 | let?.[a] = 1
+ | ^"
+`;
+
+exports[`snippet: #1 [babel] format 1`] = `
+"Invalid left-hand side in assignment expression. (1:1)
+> 1 | let?.[a] = 1
+ | ^"
+`;
+
+exports[`snippet: #1 [babel-ts] format 1`] = `
+"Invalid left-hand side in assignment expression. (1:1)
+> 1 | let?.[a] = 1
+ | ^"
+`;
+
+exports[`snippet: #1 [espree] format 1`] = `
+"The keyword 'let' is reserved (1:1)
+> 1 | let?.[a] = 1
+ | ^"
+`;
+
+exports[`snippet: #1 [meriyah] format 1`] = `
+"\`let\` declaration not allowed here and \`let\` cannot be a regular var name in strict mode (1:5)
+> 1 | let?.[a] = 1
+ | ^"
+`;
diff --git a/tests/format/misc/errors/js/assignment/jsfmt.spec.js b/tests/format/misc/errors/js/assignment/jsfmt.spec.js
index 2435ed514..b8c223d56 100644
--- a/tests/format/misc/errors/js/assignment/jsfmt.spec.js
+++ b/tests/format/misc/errors/js/assignment/jsfmt.spec.js
@@ -1,7 +1,31 @@
run_spec(
{
importMeta: import.meta,
- snippets: ["({}) = x;"],
+ snippets: ["({}) = x;", "let?.[a] = 1"],
},
["babel", "babel-ts", "acorn", "espree", "meriyah"]
);
+
+run_spec(
+ {
+ importMeta: import.meta,
+ snippets: ["let?.()[a] =1"],
+ },
+ ["babel", "babel-ts", "acorn", "espree"]
+);
+
+run_spec(
+ {
+ importMeta: import.meta,
+ snippets: ["class Foo extends ((let)[0] = 1) {}"],
+ },
+ ["acorn", "espree", "meriyah"]
+);
+
+run_spec(
+ {
+ importMeta: import.meta,
+ snippets: ["export default ((let)[0] = 1);"],
+ },
+ ["espree", "meriyah"]
+);
diff --git a/tests/format/typescript/decorator-auto-accessors/__snapshots__/jsfmt.spec.js.snap b/tests/format/typescript/decorator-auto-accessors/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000..2b9e43887
--- /dev/null
+++ b/tests/format/typescript/decorator-auto-accessors/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,29 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`decorator-auto-accessors-type-annotations.ts format 1`] = `
+====================================options=====================================
+parsers: ["typescript"]
+printWidth: 80
+ | printWidth
+=====================================input======================================
+abstract class Foo {
+ accessor prop: number = 1;
+ static accessor prop2: number = 1;
+ accessor #prop3: number = 1;
+ accessor [prop4]: number = 1;
+ private accessor prop5: number = 1;
+ abstract accessor prop6: number;
+}
+
+=====================================output=====================================
+abstract class Foo {
+ accessor prop: number = 1;
+ static accessor prop2: number = 1;
+ accessor #prop3: number = 1;
+ accessor [prop4]: number = 1;
+ private accessor prop5: number = 1;
+ abstract accessor prop6: number;
+}
+
+================================================================================
+`;
diff --git a/tests/format/misc/typescript-only/decorator-auto-accessors-type-annotations.ts b/tests/format/typescript/decorator-auto-accessors/decorator-auto-accessors-type-annotations.ts
similarity index 100%
rename from tests/format/misc/typescript-only/decorator-auto-accessors-type-annotations.ts
rename to tests/format/typescript/decorator-auto-accessors/decorator-auto-accessors-type-annotations.ts
diff --git a/tests/format/typescript/decorator-auto-accessors/jsfmt.spec.js b/tests/format/typescript/decorator-auto-accessors/jsfmt.spec.js
new file mode 100644
index 000000000..29084a9c8
--- /dev/null
+++ b/tests/format/typescript/decorator-auto-accessors/jsfmt.spec.js
@@ -0,0 +1 @@
+run_spec(import.meta, ["typescript"]);