Repository: prettier/prettier (JavaScript)
Date: 2022-01-18 13:11:35+00:00
Task type: Bug Fix

Issue:
[vue] v-for incorrectly removing commas from left hand side
**Prettier 2.5.1**
[Playground link](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEAeeBbADgGwIbwB8AOlAARmoAmAlgG5l0C0AZhAE4C8xIAFADR5+AIwCUZCCzJgehVAHpadElAWZcBOCpD8QELDBrQAzslB527CAHcAChYSmUeHNbwBPU7uHs8YANZwMADKeBhwADI0UHDILC7GcN6+AUHBWH7RAObIMOwArkkgcBjCcFRU5RF4UFn5eFlwAGIcGASGtcggePkwEDogABYwGDgA6oM08MYZYHDBjlP0U+5dYMZeINGJ7DC2vlltcQlFAFbGAB7B2ThwAIr5EPDHOIm6Gew7XXSFA1js0RgYxoVBgg2QAA4AAzvKyJMa+LBdf5wHZ0WK6ACOj3g+30Tm6xiYMXK5QG7Dg2JoFP2DSOSHiryKiQwNFyBWZN3uONiDJOuhgeGEwNB4KQACYBb4aDhsgBhCAYenFYwAVgG+USABUhU5GW8QD84ABJKCVWDBMAAgwAQTNwRg7luL0SAF9XUA)

**Input:**
<!-- prettier-ignore -->
```vue
<template>
  <div v-for="(,a,b) of c"></div>
</template>

```

**Output:**
<!-- prettier-ignore -->
```vue
<template>
  <div v-for="(a, b) of c"></div>
</template>

```

**Expected behavior:**

the `(,a,b)` is converted to `(a, b)` silently, which is wrong in semantics. It should be left untouched. Since the vue template compiler actually accepts it and correctly ignoring the `b`, while assigning `a` the index number.


Issue discussion:

Solution patch:
diff --git a/changelog_unreleased/vue/12113.md b/changelog_unreleased/vue/12113.md
new file mode 100644
index 000000000000..314b5db6da0c
--- /dev/null
+++ b/changelog_unreleased/vue/12113.md
@@ -0,0 +1,21 @@
+#### Fix hangs on invalid `v-for` (#12113 by @fisker)
+
+<!-- prettier-ignore -->
+```jsx
+// Input
+<template>
+  <div>
+    <div v-for=" a in ">aaaaa</div>
+  </div>
+</template>
+
+// Prettier stable
+// Hangs
+
+// Prettier main
+<template>
+  <div>
+    <div v-for=" a in ">aaaaa</div>
+  </div>
+</template>;
+```
diff --git a/src/language-html/syntax-vue.js b/src/language-html/syntax-vue.js
index 4bad8ceb3da6..d26e89c9927a 100644
--- a/src/language-html/syntax-vue.js
+++ b/src/language-html/syntax-vue.js
@@ -40,8 +40,13 @@ function parseVueFor(value) {
   if (!inMatch) {
     return;
   }
+
   const res = {};
   res.for = inMatch[3].trim();
+  if (!res.for) {
+    return;
+  }
+
   const alias = inMatch[1].trim().replace(stripParensRE, "");
   const iteratorMatch = alias.match(forIteratorRE);
   if (iteratorMatch) {
@@ -54,10 +59,18 @@ function parseVueFor(value) {
     res.alias = alias;
   }
 
+  const left = [res.alias, res.iterator1, res.iterator2];
+  if (
+    left.some(
+      (part, index) =>
+        !part && (index === 0 || left.slice(index + 1).some(Boolean))
+    )
+  ) {
+    return;
+  }
+
   return {
-    left: `${[res.alias, res.iterator1, res.iterator2]
-      .filter(Boolean)
-      .join(",")}`,
+    left: left.filter(Boolean).join(","),
     operator: inMatch[2],
     right: res.for,
   };


Test patch:
diff --git a/tests/format/vue/invalid/__snapshots__/jsfmt.spec.js.snap b/tests/format/vue/invalid/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000000..cda9412cdec3
--- /dev/null
+++ b/tests/format/vue/invalid/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,52 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`v-for.vue format 1`] = `
+====================================options=====================================
+parsers: ["vue"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+<template>
+  <div>
+    <div
+      v-for="    _       in          "
+      v-for="            in _        "
+      v-for="            in          "
+      v-for="    _,      in a        "
+      v-for="    ,_      in a        "
+
+      v-for="    a, b,      in a        "
+      v-for="    a,  , c     in a        "
+
+      v-for="     , b, c     in a        "
+      v-for="    a, b,       in a        "
+
+      v-for="     , b, c     in a        "
+      v-for="    a,  , c     in a        "
+      v-for="    (,a,b) of 'abcd'        "
+    ></div>
+  </div>
+</template>
+
+=====================================output=====================================
+<template>
+  <div>
+    <div
+      v-for="    _       in          "
+      v-for="            in _        "
+      v-for="            in          "
+      v-for="_ in a"
+      v-for="    ,_      in a        "
+      v-for="(a, b) in a"
+      v-for="    a,  , c     in a        "
+      v-for="     , b, c     in a        "
+      v-for="(a, b) in a"
+      v-for="     , b, c     in a        "
+      v-for="    a,  , c     in a        "
+      v-for="    (,a,b) of 'abcd'        "
+    ></div>
+  </div>
+</template>
+
+================================================================================
+`;
diff --git a/tests/format/vue/invalid/jsfmt.spec.js b/tests/format/vue/invalid/jsfmt.spec.js
new file mode 100644
index 000000000000..2fd7eede986d
--- /dev/null
+++ b/tests/format/vue/invalid/jsfmt.spec.js
@@ -0,0 +1,1 @@
+run_spec(__dirname, ["vue"]);
diff --git a/tests/format/vue/invalid/v-for.vue b/tests/format/vue/invalid/v-for.vue
new file mode 100644
index 000000000000..2678f1cab151
--- /dev/null
+++ b/tests/format/vue/invalid/v-for.vue
@@ -0,0 +1,21 @@
+<template>
+  <div>
+    <div
+      v-for="    _       in          "
+      v-for="            in _        "
+      v-for="            in          "
+      v-for="    _,      in a        "
+      v-for="    ,_      in a        "
+
+      v-for="    a, b,      in a        "
+      v-for="    a,  , c     in a        "
+
+      v-for="     , b, c     in a        "
+      v-for="    a, b,       in a        "
+
+      v-for="     , b, c     in a        "
+      v-for="    a,  , c     in a        "
+      v-for="    (,a,b) of 'abcd'        "
+    ></div>
+  </div>
+</template>