Repository: vuejs/eslint-plugin-vue (JavaScript)

Issue:
no-async-in-computed-properties fails on objects containing async functions
<!--
  Before posting the issue, please confirm that the problem you're getting
  is not related with your code editor configuration.
  To make sure it's not, run: yarn eslint src/your-file.vue
-->

This can be seen on the demo page for the rule [here](https://vuejs.github.io/eslint-plugin-vue/rules/no-async-in-computed-properties.html). Adding the following computed property causes an ESLint error.

```vue
test () {
  return {
    callback: async (e) => e
  }
}
```

**What did you expect to happen?**
No error since computing the result of the computed property is not asynchronous.

**What actually happened?**
There is an error on the property.



Discussion:

Patch:
diff --git a/lib/rules/no-async-in-computed-properties.js b/lib/rules/no-async-in-computed-properties.js
index 53dd83e6e..ba34e1f82 100644
--- a/lib/rules/no-async-in-computed-properties.js
+++ b/lib/rules/no-async-in-computed-properties.js
@@ -73,6 +73,7 @@ module.exports = {
 
   create (context) {
     const forbiddenNodes = []
+    const allowedScopes = []
 
     const expressionTypes = {
       promise: 'asynchronous action',
@@ -88,6 +89,8 @@ module.exports = {
           node: node,
           type: 'async'
         })
+      } else if (node.parent.type === 'ReturnStatement') {
+        allowedScopes.push(node)
       }
     }
 
@@ -105,6 +108,8 @@ module.exports = {
               node: node,
               type: 'new'
             })
+          } else if (node.parent.type === 'ReturnStatement') {
+            allowedScopes.push(node)
           }
         },
 
@@ -114,12 +119,13 @@ module.exports = {
               node: node,
               type: 'promise'
             })
-          }
-          if (isTimedFunction(node)) {
+          } else if (isTimedFunction(node)) {
             forbiddenNodes.push({
               node: node,
               type: 'timed'
             })
+          } else if (node.parent.type === 'ReturnStatement') {
+            allowedScopes.push(node)
           }
         },
 
@@ -128,6 +134,15 @@ module.exports = {
             node: node,
             type: 'await'
           })
+        },
+
+        'ReturnStatement' (node) {
+          if (
+            node.argument.type === 'ObjectExpression' ||
+            node.argument.type === 'ArrayExpression'
+          ) {
+            allowedScopes.push(node.argument)
+          }
         }
       },
       utils.executeOnVue(context, (obj) => {
@@ -138,7 +153,11 @@ module.exports = {
             if (
               cp.value &&
               el.node.loc.start.line >= cp.value.loc.start.line &&
-              el.node.loc.end.line <= cp.value.loc.end.line
+              el.node.loc.end.line <= cp.value.loc.end.line &&
+              !allowedScopes.some(scope =>
+                scope.range[0] < el.node.range[0] &&
+                scope.range[1] > el.node.range[1]
+              )
             ) {
               context.report({
                 node: el.node,


Test patch:
diff --git a/tests/lib/rules/no-async-in-computed-properties.js b/tests/lib/rules/no-async-in-computed-properties.js
index 23f59965b..39cef9f9e 100644
--- a/tests/lib/rules/no-async-in-computed-properties.js
+++ b/tests/lib/rules/no-async-in-computed-properties.js
@@ -71,6 +71,103 @@ ruleTester.run('no-async-in-computed-properties', rule, {
         }
       `,
       parserOptions
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        export default {
+          computed: {
+            foo() {
+              return {
+                async bar() {
+                  const data = await baz(this.a)
+                  return data
+                }
+              }
+            }
+          }
+        }
+      `,
+      parserOptions
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        export default {
+          computed: {
+            foo() {
+              const a = 'test'
+              return [
+                async () => {
+                  const baz = await bar(a)
+                  return baz
+                },
+                'b',
+                {}
+              ]
+            }
+          }
+        }
+      `,
+      parserOptions
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        export default {
+          computed: {
+            foo() {
+              return function () {
+                return async () => await bar()
+              }
+            },
+          }
+        }
+      `,
+      parserOptions
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        export default {
+          computed: {
+            foo() {
+              return new Promise.resolve()
+            },
+          }
+        }
+      `,
+      parserOptions
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        export default {
+          computed: {
+            foo() {
+              return new Bar(async () => await baz())
+            },
+          }
+        }
+      `,
+      parserOptions
+    },
+    {
+      filename: 'test.vue',
+      code: `
+        export default {
+          computed: {
+            foo() {
+              return someFunc.doSomething({
+                async bar() {
+                  return await baz()
+                }
+              })
+            },
+          }
+        }
+      `,
+      parserOptions
     }
   ],