Repository: jshint/jshint
Pull request #3369 (2019-02-23 23:34:07)

[[FIX]] Correct parsing of exported async fns
This is intended to resolve gh-3367.


Patch:
diff --git a/package.json b/package.json
index 6009b1fd..6f789005 100644
--- a/package.json
+++ b/package.json
@@ -43,7 +43,7 @@
     "console-browserify": "1.1.x",
     "exit": "0.1.x",
     "htmlparser2": "3.8.x",
-    "lodash": "~4.17.10",
+    "lodash": "~4.17.11",
     "minimatch": "~3.0.2",
     "shelljs": "0.3.x",
     "strip-json-comments": "1.0.x"
diff --git a/src/jshint.js b/src/jshint.js
index 2c04ca7c..09b06183 100644
--- a/src/jshint.js
+++ b/src/jshint.js
@@ -3069,7 +3069,7 @@ var JSHINT = (function() {
       }
       if (!left.identifier && left.id !== "." && left.id !== "[" && left.id !== "=>" &&
           left.id !== "(" && left.id !== "&&" && left.id !== "||" && left.id !== "?" &&
-          !(state.inES6() && left["(name)"])) {
+          left.id !== "async" && !(state.inES6() && left["(name)"])) {
         warning("W067", that);
       }
     }
@@ -5308,6 +5308,7 @@ var JSHINT = (function() {
 
       context |= prodParams.preAsync;
       this.func = expression(context, rbp);
+      this.identifier = false;
       return this;
     }
 
@@ -5569,6 +5570,11 @@ var JSHINT = (function() {
         this.block = true;
         advance("function");
         state.syntax["function"].fud(context);
+      } else if (exportType === "async" && peek().id === "function") {
+        this.block = true;
+        advance("async");
+        advance("function");
+        state.syntax["function"].fud(context | prodParams.preAsync);
       } else if (exportType === "class") {
         this.block = true;
         advance("class");
@@ -5644,6 +5650,12 @@ var JSHINT = (function() {
       this.block = true;
       advance("function");
       state.syntax["function"].fud(context);
+    } else if (state.tokens.next.id === "async" && peek().id === "function") {
+      // ExportDeclaration :: export Declaration
+      this.block = true;
+      advance("async");
+      advance("function");
+      state.syntax["function"].fud(context | prodParams.preAsync);
     } else if (state.tokens.next.id === "class") {
       // ExportDeclaration :: export Declaration
       this.block = true;


Test patch:
diff --git a/tests/unit/core.js b/tests/unit/core.js
index 54bf218f..6aedbf06 100644
--- a/tests/unit/core.js
+++ b/tests/unit/core.js
@@ -928,6 +928,46 @@ exports.testES6Modules = function (test) {
       "import afterLabelImported from 'elsewhere';"
     ], { esversion: 6 });
 
+  TestRun(test, "async as Identifier")
+    .test([
+      "var async;",
+      "export default async;"
+    ], { esversion: 6, module: true });
+
+  TestRun(test, "async in CallExpression")
+    .test([
+      "var async, x, y, z;",
+      "export default async(x, y, z);"
+    ], { esversion: 6, module: true });
+
+  TestRun(test, "async functions")
+    .test([
+      "export async function f() { await 0; }",
+      "export default async function() { await 0; }",
+    ], { esversion: 8, module: true });
+
+  TestRun(test, "async arrow function - zero arguments")
+    .test([
+      "export default async () => {};"
+    ], { esversion: 8, module: true });
+
+  TestRun(test, "async arrow function - concise argument")
+    .test([
+      "export default async _ => {};"
+    ], { esversion: 8, module: true });
+
+  TestRun(test, "async arrow function - many arguments")
+    .test([
+      "export default async (x, y, z) => {};"
+    ], { esversion: 8, module: true });
+
+
+  TestRun(test, "async generator functions")
+    .test([
+      "export async function * f() { yield 0; await 0; }",
+      "export default async function * () { yield 0; await 0; }",
+    ], { esversion: 9, module: true });
+
   test.done();
 };