emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] master 04f3d4b 110/110: Merge commit '0cda39255827f283e7578cd469a


From: Dmitry Gutov
Subject: [elpa] master 04f3d4b 110/110: Merge commit '0cda39255827f283e7578cd469ae42daad9556a2' from js2-mode
Date: Thu, 23 Jun 2016 01:13:04 +0000 (UTC)

branch: master
commit 04f3d4bb44d7ef30e705b53f3d15e3279ac0d230
Merge: 7a2deff 0cda392
Author: Dmitry Gutov <address@hidden>
Commit: Dmitry Gutov <address@hidden>

    Merge commit '0cda39255827f283e7578cd469ae42daad9556a2' from js2-mode
---
 packages/js2-mode/Makefile           |    9 +-
 packages/js2-mode/NEWS.md            |   18 +
 packages/js2-mode/README.md          |    4 +-
 packages/js2-mode/js2-mode.el        |  954 ++++++++++++++++++++++------------
 packages/js2-mode/js2-old-indent.el  |  259 ++++++++-
 packages/js2-mode/tests/externs.el   |    9 +-
 packages/js2-mode/tests/indent.el    |  151 +++++-
 packages/js2-mode/tests/json-path.el |   64 +++
 packages/js2-mode/tests/parser.el    |  265 ++++++++--
 9 files changed, 1320 insertions(+), 413 deletions(-)

diff --git a/packages/js2-mode/Makefile b/packages/js2-mode/Makefile
index 08b1e48..7777a67 100644
--- a/packages/js2-mode/Makefile
+++ b/packages/js2-mode/Makefile
@@ -10,17 +10,14 @@ SRCS = js2-mode.el js2-imenu-extras.el
 OBJS = $(SRCS:.el=.elc)
 
 %.elc: %.el
-       ${EMACS} $(BATCHFLAGS) -f batch-byte-compile $^
+       ${EMACS} $(BATCHFLAGS) -L . -f batch-byte-compile $^
 
 all: $(OBJS)
 
 clean:
        -rm -f $(OBJS)
 
-# custom build (require loads)
-js2-imenu-extras.elc: js2-mode.elc
-       ${EMACS} $(BATCHFLAGS) -l ./js2-mode.elc -f batch-byte-compile $*.el
-
 test:
        ${EMACS} $(BATCHFLAGS) -L . -l js2-mode.el -l js2-old-indent.el -l 
tests/parser.el\
-         -l tests/indent.el -l tests/externs.el -f ert-run-tests-batch-and-exit
+         -l tests/indent.el -l tests/externs.el -l tests/json-path.el \
+         -l tests/navigation.el -f ert-run-tests-batch-and-exit
diff --git a/packages/js2-mode/NEWS.md b/packages/js2-mode/NEWS.md
index 2984e91..87e9d72 100644
--- a/packages/js2-mode/NEWS.md
+++ b/packages/js2-mode/NEWS.md
@@ -1,5 +1,23 @@
 # History of user-visible changes
 
+## 2016-06-23
+
+* New variable `js2-mode-assume-strict`, for use with ES6 modules.
+* Support for JSDoc @callback, @func and @method tags.
+* Object properties are highlighted using a different face:
+  `js2-object-property`, which has no color by default.
+* Experimental support for object rest/spread ECMAScript proposal.
+* `js2-getter-setter-node` is renamed to `js2-method-node`, together with
+  its related functions.  It already handles generator methods, and we
+  added support for async methods (see below), so the old name would get
+  more confusing.
+* Support for default parameters in destructuring.  It should work for both
+  objects and arrays, in both literals and function arguments.
+* New mode: `js2-jsx-mode`, deriving from `js2-mode`.  Supports indentation of
+  JSXElement expressions wrapped within parentheses or as function arguments.
+  Indentation is customizable via `sgml-attribute-offset`.
+* Experimental support for async/await ECMAScript proposal.
+
 ## 20150909
 
 * `js2-mode` now derives from `js-mode`. That means the former
diff --git a/packages/js2-mode/README.md b/packages/js2-mode/README.md
index b0ee444..cbce9c3 100644
--- a/packages/js2-mode/README.md
+++ b/packages/js2-mode/README.md
@@ -1,4 +1,4 @@
-About [![Build 
Status](https://travis-ci.org/mooz/js2-mode.png?branch=master)](https://travis-ci.org/mooz/js2-mode)
+About [![Build 
Status](https://travis-ci.org/mooz/js2-mode.svg?branch=master)](https://travis-ci.org/mooz/js2-mode)
 
[![MELPA](https://melpa.org/packages/js2-mode-badge.svg)](https://melpa.org/#/js2-mode)
 ======
 
 Improved JavaScript editing mode for GNU Emacs ([description 
here](http://elpa.gnu.org/packages/js2-mode.html)).
@@ -12,7 +12,7 @@ The stable versions are hosted at [GNU 
ELPA](http://elpa.gnu.org/)
 (<kbd>M-x list-packages</kbd>).
 
 You can also install the latest development version from
-[Melpa](http://melpa.milkbox.net/#installing).
+[MELPA](https://melpa.org/#/getting-started).
 
 Emacs 22 and 23
 ===============
diff --git a/packages/js2-mode/js2-mode.el b/packages/js2-mode/js2-mode.el
index 2d6f336..a2e58fa 100644
--- a/packages/js2-mode/js2-mode.el
+++ b/packages/js2-mode/js2-mode.el
@@ -1,13 +1,13 @@
 ;;; js2-mode.el --- Improved JavaScript editing mode
 
-;; Copyright (C) 2009, 2011-2015  Free Software Foundation, Inc.
+;; Copyright (C) 2009, 2011-2016  Free Software Foundation, Inc.
 
 ;; Author: Steve Yegge <address@hidden>
 ;;         mooz <address@hidden>
 ;;         Dmitry Gutov <address@hidden>
 ;; URL:  https://github.com/mooz/js2-mode/
 ;;       http://code.google.com/p/js2-mode/
-;; Version: 20150909
+;; Version: 20160623
 ;; Keywords: languages, javascript
 ;; Package-Requires: ((emacs "24.1") (cl-lib "0.5"))
 
@@ -60,6 +60,12 @@
 
 ;;   (add-to-list 'interpreter-mode-alist '("node" . js2-mode))
 
+;; Support for JSX is available via the derived mode `js2-jsx-mode'.  If you
+;; also want JSX support, use that mode instead:
+
+;;   (add-to-list 'auto-mode-alist '("\\.jsx?\\'" . js2-jsx-mode))
+;;   (add-to-list 'interpreter-mode-alist '("node" . js2-jsx-mode))
+
 ;; To customize how it works:
 ;;   M-x customize-group RET js2-mode RET
 
@@ -95,6 +101,7 @@
       (require 'js2-old-indent)
     (defvaralias 'js2-basic-offset 'js-indent-level nil)
     (defalias 'js2-proper-indentation 'js--proper-indentation)
+    (defalias 'js2-jsx-indent-line 'js-jsx-indent-line)
     (defalias 'js2-indent-line 'js-indent-line)
     (defalias 'js2-re-search-forward 'js--re-search-forward)))
 
@@ -192,7 +199,7 @@ Set `js2-include-rhino-externs' to t to include them.")
   (mapcar 'symbol-name
           '(__dirname __filename Buffer clearInterval clearTimeout require
             console exports global module process setInterval setTimeout
-            querystring))
+            querystring setImmediate clearImmediate))
   "Node.js externs.
 Set `js2-include-node-externs' to t to include them.")
 
@@ -270,6 +277,11 @@ end of the line, otherwise, at the beginning of the next 
line."
   :type 'boolean
   :group 'js2-mode)
 
+(defcustom js2-mode-assume-strict nil
+  "Non-nil to start files in strict mode automatically."
+  :type 'boolean
+  :group 'js2-mode)
+
 (defcustom js2-mode-show-strict-warnings t
   "Non-nil to emit Ecma strict-mode warnings.
 Some of the warnings can be individually disabled by other flags,
@@ -610,7 +622,9 @@ which doesn't seem particularly useful, but Rhino permits 
it."
 (defvar js2-NO_SUBS_TEMPLATE 167) ; template literal without substitutions
 (defvar js2-TAGGED_TEMPLATE 168)  ; tagged template literal
 
-(defconst js2-num-tokens (1+ js2-TAGGED_TEMPLATE))
+(defvar js2-AWAIT 169)  ; await (pseudo keyword)
+
+(defconst js2-num-tokens (1+ js2-AWAIT))
 
 (defconst js2-debug-print-trees nil)
 
@@ -668,6 +682,7 @@ List of chars built up while scanning various tokens.")
   (string "")
   number
   number-base
+  number-legacy-octal-p
   regexp-flags
   comment-type
   follows-eol-p)
@@ -1018,6 +1033,11 @@ in large files.")
   "Face used to highlight function name in calls."
   :group 'js2-mode)
 
+(defface js2-object-property
+  '((t :inherit default))
+  "Face used to highlight named property in object literal."
+  :group 'js2-mode)
+
 (defface js2-instance-member
   '((t :foreground "DarkOrchid"))
   "Face used to highlight instance variables in javascript.
@@ -1061,6 +1081,7 @@ Not currently used."
   "Face used to highlight undeclared variable identifiers.")
 
 (defcustom js2-init-hook nil
+  ;; FIXME: We don't really need this anymore.
   "List of functions to be called after `js2-mode' or
 `js2-minor-mode' has initialized all variables, before parsing
 the buffer for the first time."
@@ -1350,6 +1371,9 @@ the correct number of ARGS must be provided."
 (js2-msg "msg.yield.parenthesized"
          "yield expression must be parenthesized.")
 
+(js2-msg "msg.bad.await"
+         "await must be in async functions.")
+
 ;; NativeGlobal
 (js2-msg "msg.cant.call.indirect"
           "Function '%s' must be called directly, and not by way of a "
@@ -1437,7 +1461,7 @@ the correct number of ARGS must be provided."
          "Compilation produced %s syntax errors.")
 
 (js2-msg "msg.var.redecl"
-         "TypeError: redeclaration of var %s.")
+         "Redeclaration of var %s.")
 
 (js2-msg "msg.const.redecl"
          "TypeError: redeclaration of const %s.")
@@ -1712,6 +1736,9 @@ the correct number of ARGS must be provided."
 (js2-msg "msg.destruct.assign.no.init"
          "Missing = in destructuring declaration")
 
+(js2-msg "msg.init.no.destruct"
+         "Binding initializer not in destructuring assignment")
+
 (js2-msg "msg.no.octal.strict"
          "Octal numbers prohibited in strict mode.")
 
@@ -2530,7 +2557,10 @@ so many of its properties will be nil.
       (js2-print-from-clause from))
      (exports-list
       (js2-print-named-imports exports-list)))
-    (insert ";\n")))
+    (unless (or (and default (not (js2-assign-node-p default)))
+                (and declaration (or (js2-function-node-p declaration)
+                                     (js2-class-node-p declaration))))
+      (insert ";\n"))))
 
 (cl-defstruct (js2-while-node
                (:include js2-loop-node)
@@ -3241,6 +3271,7 @@ a `js2-label-node' or the innermost enclosing loop.")
                                                           params rest-p
                                                           body
                                                           generator-type
+                                                          async
                                                           lp rp)))
   "AST node for a function declaration.
 The `params' field is a Lisp list of nodes.  Each node is either a simple
@@ -3257,6 +3288,7 @@ The `params' field is a Lisp list of nodes.  Each node is 
either a simple
   ignore-dynamic   ; ignore value of the dynamic-scope flag (interpreter only)
   needs-activation ; t if we need an activation object for this frame
   generator-type   ; STAR, LEGACY, COMPREHENSION or nil
+  async            ; t if the function is defined as `async function`
   member-expr)     ; nonstandard Ecma extension from Rhino
 
 (put 'cl-struct-js2-function-node 'js2-visitor 'js2-visit-function-node)
@@ -3270,7 +3302,7 @@ The `params' field is a Lisp list of nodes.  Each node is 
either a simple
 
 (defun js2-print-function-node (n i)
   (let* ((pad (js2-make-pad i))
-         (getter (js2-node-get-prop n 'GETTER_SETTER))
+         (method (js2-node-get-prop n 'METHOD_TYPE))
          (name (or (js2-function-node-name n)
                    (js2-function-node-member-expr n)))
          (params (js2-function-node-params n))
@@ -3278,8 +3310,10 @@ The `params' field is a Lisp list of nodes.  Each node 
is either a simple
          (rest-p (js2-function-node-rest-p n))
          (body (js2-function-node-body n))
          (expr (not (eq (js2-function-node-form n) 'FUNCTION_STATEMENT))))
-    (unless (or getter arrow)
-      (insert pad "function")
+    (unless method
+      (insert pad)
+      (when (js2-function-node-async n) (insert "async "))
+      (unless arrow (insert "function"))
       (when (eq (js2-function-node-generator-type n) 'STAR)
         (insert "*")))
     (when name
@@ -3450,6 +3484,8 @@ The type field inherited from `js2-node' holds the 
operator."
                (cons js2-TYPEOF "typeof")
                (cons js2-INSTANCEOF "instanceof")
                (cons js2-DELPROP "delete")
+               (cons js2-AWAIT "await")
+               (cons js2-VOID "void")
                (cons js2-COMMA ",")
                (cons js2-COLON ":")
                (cons js2-OR "||")
@@ -3530,7 +3566,7 @@ The type field holds the actual assignment operator.")
                                                        len operand)))
   "AST node type for unary operator nodes.
 The type field can be NOT, BITNOT, POS, NEG, INC, DEC,
-TYPEOF, DELPROP or TRIPLEDOT.  For INC or DEC, a 'postfix node
+TYPEOF, DELPROP, TRIPLEDOT or AWAIT.  For INC or DEC, a 'postfix node
 property is added if the operator follows the operand."
   operand)  ; a `js2-node' expression
 
@@ -3550,7 +3586,9 @@ property is added if the operator follows the operand."
     (unless postfix
       (insert op))
     (if (or (= tt js2-TYPEOF)
-            (= tt js2-DELPROP))
+            (= tt js2-DELPROP)
+            (= tt js2-AWAIT)
+            (= tt js2-VOID))
         (insert " "))
     (js2-print-ast (js2-unary-node-operand n) 0)
     (when postfix
@@ -3686,11 +3724,14 @@ Returns 0 if NODE is nil or its identifier field is 
nil."
                                                         (num-value 
(js2-token-number
                                                                     
(js2-current-token)))
                                                         (num-base 
(js2-token-number-base
-                                                                   
(js2-current-token))))))
+                                                                   
(js2-current-token)))
+                                                        (legacy-octal-p 
(js2-token-number-legacy-octal-p
+                                                                         
(js2-current-token))))))
   "AST node for a number literal."
   value      ; the original string, e.g. "6.02e23"
   num-value  ; the parsed number value
-  num-base)  ; the number's base
+  num-base  ; the number's base
+  legacy-octal-p)  ; whether the number is a legacy octal (0123 instead of 
0o123)
 
 (put 'cl-struct-js2-number-node 'js2-visitor 'js2-visit-none)
 (put 'cl-struct-js2-number-node 'js2-printer 'js2-print-number-node)
@@ -3810,9 +3851,32 @@ You can tell the quote type by looking at the first 
character."
       (insert ",")))
   (insert "]"))
 
-(cl-defstruct (js2-class-node
+(cl-defstruct (js2-object-node
                (:include js2-node)
                (:constructor nil)
+               (:constructor make-js2-object-node (&key (type js2-OBJECTLIT)
+                                                        (pos js2-ts-cursor)
+                                                        len
+                                                        elems)))
+  "AST node for an object literal expression.
+`elems' is a list of `js2-object-prop-node'."
+  elems)
+
+(put 'cl-struct-js2-object-node 'js2-visitor 'js2-visit-object-node)
+(put 'cl-struct-js2-object-node 'js2-printer 'js2-print-object-node)
+
+(defun js2-visit-object-node (n v)
+  (dolist (e (js2-object-node-elems n))
+    (js2-visit-ast e v)))
+
+(defun js2-print-object-node (n i)
+  (insert (js2-make-pad i) "{")
+  (js2-print-list (js2-object-node-elems n))
+  (insert "}"))
+
+(cl-defstruct (js2-class-node
+               (:include js2-object-node)
+               (:constructor nil)
                (:constructor make-js2-class-node (&key (type js2-CLASS)
                                                        (pos js2-ts-cursor)
                                                        (form 'CLASS_STATEMENT)
@@ -3824,7 +3888,7 @@ optional `js2-expr-node'"
   form             ; CLASS_{STATEMENT|EXPRESSION}
   name             ; class name (a `js2-node-name', or nil if anonymous)
   extends          ; class heritage (a `js2-expr-node', or nil if none)
-  elems)
+  )
 
 (put 'cl-struct-js2-class-node 'js2-visitor 'js2-visit-class-node)
 (put 'cl-struct-js2-class-node 'js2-printer 'js2-print-class-node)
@@ -3856,28 +3920,29 @@ optional `js2-expr-node'"
         (js2-print-ast elem (1+ i))))
     (insert "\n" pad "}")))
 
-(cl-defstruct (js2-object-node
+(cl-defstruct (js2-computed-prop-name-node
                (:include js2-node)
                (:constructor nil)
-               (:constructor make-js2-object-node (&key (type js2-OBJECTLIT)
-                                                        (pos js2-ts-cursor)
-                                                        len
-                                                        elems)))
-  "AST node for an object literal expression.
-`elems' is a list of `js2-object-prop-node'."
-  elems)
+               (:constructor make-js2-computed-prop-name-node
+                             (&key
+                              (type js2-LB)
+                              expr
+                              (pos (js2-current-token-beg))
+                              (len (- js2-ts-cursor
+                                      (js2-current-token-beg))))))
+  "AST node for a `ComputedPropertyName'."
+  expr)
 
-(put 'cl-struct-js2-object-node 'js2-visitor 'js2-visit-object-node)
-(put 'cl-struct-js2-object-node 'js2-printer 'js2-print-object-node)
+(put 'cl-struct-js2-computed-prop-name-node 'js2-visitor 
'js2-visit-computed-prop-name-node)
+(put 'cl-struct-js2-computed-prop-name-node 'js2-printer 
'js2-print-computed-prop-name-node)
 
-(defun js2-visit-object-node (n v)
-  (dolist (e (js2-object-node-elems n))
-    (js2-visit-ast e v)))
+(defun js2-visit-computed-prop-name-node (n v)
+  (js2-visit-ast (js2-computed-prop-name-node-expr n) v))
 
-(defun js2-print-object-node (n i)
-  (insert (js2-make-pad i) "{")
-  (js2-print-list (js2-object-node-elems n))
-  (insert "}"))
+(defun js2-print-computed-prop-name-node (n i)
+  (insert (js2-make-pad i) "[")
+  (js2-print-ast (js2-computed-prop-name-node-expr n) 0)
+  (insert "]"))
 
 (cl-defstruct (js2-object-prop-node
                (:include js2-infix-node)
@@ -3898,53 +3963,41 @@ both fields have the same value.")
 
 (defun js2-print-object-prop-node (n i)
   (let* ((left (js2-object-prop-node-left n))
-         (right (js2-object-prop-node-right n))
-         (computed (not (or (js2-string-node-p left)
-                            (js2-number-node-p left)
-                            (js2-name-node-p left)))))
-    (insert (js2-make-pad i))
-    (if computed
-        (insert "["))
-    (js2-print-ast left 0)
-    (if computed
-        (insert "]"))
+         (right (js2-object-prop-node-right n)))
+    (js2-print-ast left i)
     (if (not (js2-node-get-prop n 'SHORTHAND))
         (progn
           (insert ": ")
           (js2-print-ast right 0)))))
 
-(cl-defstruct (js2-getter-setter-node
+(cl-defstruct (js2-method-node
                (:include js2-infix-node)
                (:constructor nil)
-               (:constructor make-js2-getter-setter-node (&key type ; GET, 
SET, or FUNCTION
-                                                               (pos 
js2-ts-cursor)
-                                                               len left 
right)))
-  "AST node for a getter/setter property in an object literal.
-The `left' field is the `js2-name-node' naming the getter/setter prop.
+               (:constructor make-js2-method-node (&key (pos js2-ts-cursor)
+                                                        len left right)))
+  "AST node for a method in an object literal or a class body.
+The `left' field is the `js2-name-node' naming the method.
 The `right' field is always an anonymous `js2-function-node' with a node
-property `GETTER_SETTER' set to js2-GET, js2-SET, or js2-FUNCTION. ")
+property `METHOD_TYPE' set to 'GET or 'SET. ")
 
-(put 'cl-struct-js2-getter-setter-node 'js2-visitor 'js2-visit-infix-node)
-(put 'cl-struct-js2-getter-setter-node 'js2-printer 'js2-print-getter-setter)
+(put 'cl-struct-js2-method-node 'js2-visitor 'js2-visit-infix-node)
+(put 'cl-struct-js2-method-node 'js2-printer 'js2-print-method)
 
-(defun js2-print-getter-setter (n i)
+(defun js2-print-method (n i)
   (let* ((pad (js2-make-pad i))
-         (left (js2-getter-setter-node-left n))
-         (right (js2-getter-setter-node-right n))
-         (computed (not (or (js2-string-node-p left)
-                            (js2-number-node-p left)
-                            (js2-name-node-p left)))))
+         (left (js2-method-node-left n))
+         (right (js2-method-node-right n))
+         (type (js2-node-get-prop right 'METHOD_TYPE)))
     (insert pad)
-    (if (/= (js2-node-type n) js2-FUNCTION)
-        (insert (if (= (js2-node-type n) js2-GET) "get " "set ")))
+    (when type
+      (insert (cdr (assoc type '((GET . "get ")
+                                 (SET . "set ")
+                                 (ASYNC . "async ")
+                                 (FUNCTION . ""))))))
     (when (and (js2-function-node-p right)
                (eq 'STAR (js2-function-node-generator-type right)))
       (insert "*"))
-    (when computed
-      (insert "["))
     (js2-print-ast left 0)
-    (when computed
-      (insert "]"))
     (js2-print-ast right 0)))
 
 (cl-defstruct (js2-prop-get-node
@@ -5106,6 +5159,8 @@ You should use `js2-print-tree' instead of this function."
           (or (js2-node-has-side-effects expr)
               (when (js2-string-node-p expr)
                 (member (js2-string-node-value expr) '("use strict" "use 
asm"))))))
+       ((= tt js2-AWAIT)
+        (js2-node-has-side-effects (js2-unary-node-operand node)))
        ((= tt js2-COMMA)
         (js2-node-has-side-effects (js2-infix-node-right node)))
        ((or (= tt js2-AND)
@@ -5855,7 +5910,7 @@ its relevant fields and puts it into `js2-ti-tokens'."
   (let (identifier-start
         is-unicode-escape-start c
         contains-escape escape-val str result base
-        look-for-slash continue tt
+        look-for-slash continue tt legacy-octal
         (token (js2-new-token 0)))
     (setq
      tt
@@ -5977,6 +6032,7 @@ its relevant fields and puts it into `js2-ti-tokens'."
               ((and (or (eq c ?o) (eq c ?O))
                     (>= js2-language-version 200))
                (setq base 8)
+               (setq legacy-octal nil)
                (setq c (js2-get-char)))
               ((js2-digit-p c)
                (setq base 'maybe-8))
@@ -6014,7 +6070,8 @@ its relevant fields and puts it into `js2-ti-tokens'."
                (js2-add-to-string c)
                (setq c (js2-get-char)))
              (when (eq base 'maybe-8)
-               (setq base 8))))
+               (setq base 8
+                     legacy-octal t))))
            (when (and (eq base 10) (memq c '(?. ?e ?E)))
              (when (eq c ?.)
                (cl-loop do
@@ -6036,7 +6093,8 @@ its relevant fields and puts it into `js2-ti-tokens'."
            (js2-unget-char)
            (let ((str (js2-set-string-from-buffer token)))
              (setf (js2-token-number token) (js2-string-to-number str base)
-                   (js2-token-number-base token) base))
+                   (js2-token-number-base token) base
+                   (js2-token-number-legacy-octal-p token) (and (= base 8) 
legacy-octal)))
            (throw 'return js2-NUMBER))
          ;; is it a string?
          (when (or (memq c '(?\" ?\'))
@@ -6753,6 +6811,8 @@ Shown at or above `js2-highlight-level' 3.")
                 (prop
                  (if (string-match js2-ecma-object-props prop-name)
                      'font-lock-constant-face))))))
+        (when (and (not face) target (not call-p) prop-name)
+          (setq face 'js2-object-property))
         (when face
           (let ((pos (+ (js2-node-pos parent)  ; absolute
                         (js2-node-pos prop)))) ; relative
@@ -6852,15 +6912,18 @@ of a simple name.  Called before EXPR has a parent 
node."
            '("alias"
              "augments"
              "borrows"
+             "callback"
              "bug"
              "base"
              "config"
              "default"
              "define"
              "exception"
+             "func"
              "function"
              "member"
              "memberOf"
+             "method"
              "name"
              "namespace"
              "since"
@@ -6891,6 +6954,7 @@ of a simple name.  Called before EXPR has a parent node."
              "export"
              "fileoverview"
              "final"
+             "func"
              "function"
              "hidden"
              "ignore"
@@ -6899,6 +6963,7 @@ of a simple name.  Called before EXPR has a parent node."
              "inner"
              "interface"
              "license"
+             "method"
              "noalias"
              "noshadow"
              "notypecheck"
@@ -7199,11 +7264,12 @@ are ignored."
                js2-additional-externs)))
 
 (defun js2-get-jslint-globals ()
+  (js2-reparse)
   (cl-loop for node in (js2-ast-root-comments js2-mode-ast)
            when (and (eq 'block (js2-comment-node-format node))
                      (save-excursion
                        (goto-char (js2-node-abs-pos node))
-                       (looking-at "/\\*global ")))
+                       (looking-at "/\\* *global ")))
            append (js2-get-jslint-globals-in
                    (match-end 0)
                    (js2-node-abs-end node))))
@@ -7395,22 +7461,23 @@ We do a depth-first traversal of NODE.  For any 
functions we find,
 we append the property name to QNAME, then call `js2-record-imenu-entry'."
   (let (right)
     (dolist (e (js2-object-node-elems node))  ; e is a `js2-object-prop-node'
-      (let ((left (js2-infix-node-left e))
-            ;; Element positions are relative to the parent position.
-            (pos (+ pos (js2-node-pos e))))
-        (cond
-         ;; foo: function() {...}
-         ((js2-function-node-p (setq right (js2-infix-node-right e)))
-          (when (js2-prop-node-name left)
-            ;; As a policy decision, we record the position of the property,
-            ;; not the position of the `function' keyword, since the property
-            ;; is effectively the name of the function.
-            (js2-record-imenu-entry right (append qname (list left)) pos)))
-         ;; foo: {object-literal} -- add foo to qname, offset position, and 
recurse
-         ((js2-object-node-p right)
-          (js2-record-object-literal right
-                                     (append qname (list (js2-infix-node-left 
e)))
-                                     (+ pos (js2-node-pos right)))))))))
+      (when (js2-infix-node-p e)
+        (let ((left (js2-infix-node-left e))
+              ;; Element positions are relative to the parent position.
+              (pos (+ pos (js2-node-pos e))))
+          (cond
+           ;; foo: function() {...}
+           ((js2-function-node-p (setq right (js2-infix-node-right e)))
+            (when (js2-prop-node-name left)
+              ;; As a policy decision, we record the position of the property,
+              ;; not the position of the `function' keyword, since the property
+              ;; is effectively the name of the function.
+              (js2-record-imenu-entry right (append qname (list left)) pos)))
+           ;; foo: {object-literal} -- add foo to qname, offset position, and 
recurse
+           ((js2-object-node-p right)
+            (js2-record-object-literal right
+                                       (append qname (list 
(js2-infix-node-left e)))
+                                       (+ pos (js2-node-pos right))))))))))
 
 (defun js2-node-top-level-decl-p (node)
   "Return t if NODE's name is defined in the top-level scope.
@@ -7452,7 +7519,7 @@ For instance, processing a nested scope requires a parent 
function node."
   (let (result fn parent-qname p elem)
     (dolist (entry js2-imenu-recorder)
       ;; function node goes first
-      (cl-destructuring-bind (current-fn &rest (&whole chain head &rest)) entry
+      (cl-destructuring-bind (current-fn &rest (&whole chain head &rest _)) 
entry
         ;; Examine head's defining scope:
         ;; Pre-processed chain, or top-level/external, keep as-is.
         (if (or (stringp head) (js2-node-top-level-decl-p head))
@@ -7676,14 +7743,67 @@ Returns nil and consumes nothing if MATCH is not the 
next token."
 (defun js2-match-contextual-kwd (name)
   "Consume and return t if next token is `js2-NAME', and its
 string is NAME.  Returns nil and keeps current token otherwise."
-  (if (or (/= (js2-get-token) js2-NAME)
-          (not (string= (js2-current-token-string) name)))
-      (progn
-        (js2-unget-token)
-        nil)
+  (if (js2-contextual-kwd-p (progn (js2-get-token)
+                                   (js2-current-token))
+                            name)
+      (progn (js2-record-face 'font-lock-keyword-face) t)
+    (js2-unget-token)
+    nil))
+
+(defun js2-contextual-kwd-p (token name)
+  "Return t if TOKEN is `js2-NAME', and its string is NAME."
+  (and (= (js2-token-type token) js2-NAME)
+       (string= (js2-token-string token) name)))
+
+(defun js2-match-async-function ()
+  (when (and (js2-contextual-kwd-p (js2-current-token) "async")
+             (= (js2-peek-token) js2-FUNCTION))
     (js2-record-face 'font-lock-keyword-face)
+    (js2-get-token)
     t))
 
+(defun js2-match-async-arrow-function ()
+  (and (js2-contextual-kwd-p (js2-current-token) "async")
+       (/= (js2-peek-token) js2-FUNCTION)))
+
+(defsubst js2-inside-function ()
+  (cl-plusp js2-nesting-of-function))
+
+(defsubst js2-inside-async-function ()
+  (and (js2-inside-function)
+       (js2-function-node-async js2-current-script-or-fn)))
+
+(defun js2-parse-await-maybe (tt)
+  "Parse \"await\" as an AwaitExpression, if it is one."
+  (and (= tt js2-NAME)
+       (js2-contextual-kwd-p (js2-current-token) "await")
+       ;; Per the proposal, AwaitExpression consists of "await"
+       ;; followed by a UnaryExpression.  So look ahead for one.
+       (let ((ts-state (make-js2-ts-state))
+             (recorded-identifiers js2-recorded-identifiers)
+             (parsed-errors js2-parsed-errors)
+             (current-token (js2-current-token))
+             (beg (js2-current-token-beg))
+             (end (js2-current-token-end))
+             pn)
+         (js2-get-token)
+         (setq pn (js2-make-unary js2-AWAIT 'js2-parse-unary-expr))
+         (if (= (js2-node-type (js2-unary-node-operand pn)) js2-ERROR)
+             ;; The parse failed, so pretend like nothing happened and restore
+             ;; the previous parsing state.
+             (progn
+               (js2-ts-seek ts-state)
+               (setq js2-recorded-identifiers recorded-identifiers
+                     js2-parsed-errors parsed-errors)
+               ;; And ensure the caller knows about the failure.
+               nil)
+           ;; The parse was successful, so process and return the "await".
+           (js2-record-face 'font-lock-keyword-face current-token)
+           (unless (js2-inside-async-function)
+             (js2-report-error "msg.bad.await" nil
+                               beg (- end beg)))
+           pn))))
+
 (defun js2-get-prop-name-token ()
   (js2-get-token (and (>= js2-language-version 170) 'KEYWORD_IS_NAME)))
 
@@ -7734,9 +7854,6 @@ Returns t on match, nil if no match."
       (js2-unget-token))
     nil))
 
-(defsubst js2-inside-function ()
-  (cl-plusp js2-nesting-of-function))
-
 (defun js2-set-requires-activation ()
   (if (js2-function-node-p js2-current-script-or-fn)
       (setf (js2-function-node-needs-activation js2-current-script-or-fn) t)))
@@ -7876,7 +7993,7 @@ Scanner should be initialized."
           js2-nesting-of-function 0
           js2-labeled-stmt nil
           js2-recorded-identifiers nil  ; for js2-highlight
-          js2-in-use-strict-directive nil)
+          js2-in-use-strict-directive js2-mode-assume-strict)
     (while (/= (setq tt (js2-get-token)) js2-EOF)
       (if (= tt js2-FUNCTION)
           (progn
@@ -7987,16 +8104,34 @@ declared; probably to check them for errors."
         (list node)))
      ((js2-object-node-p node)
       (dolist (elem (js2-object-node-elems node))
-        (when (js2-object-prop-node-p elem)
-          (push (js2-define-destruct-symbols
-                 ;; In abbreviated destructuring {a, b}, right == left.
-                 (js2-object-prop-node-right elem)
-                 decl-type face ignore-not-in-block)
-                name-nodes)))
+        (let ((subexpr (cond
+                        ((and (js2-infix-node-p elem)
+                              (= js2-ASSIGN (js2-infix-node-type elem)))
+                         ;; Destructuring with default argument.
+                         (js2-infix-node-left elem))
+                        ((and (js2-infix-node-p elem)
+                              (= js2-COLON (js2-infix-node-type elem)))
+                         ;; In regular destructuring {a: aa, b: bb},
+                         ;; the var is on the right.  In abbreviated
+                         ;; destructuring {a, b}, right == left.
+                         (js2-infix-node-right elem))
+                        ((and (js2-unary-node-p elem)
+                              (= js2-TRIPLEDOT (js2-unary-node-type elem)))
+                         ;; Destructuring with spread.
+                         (js2-unary-node-operand elem)))))
+          (when subexpr
+            (push (js2-define-destruct-symbols
+                   subexpr decl-type face ignore-not-in-block)
+                  name-nodes))))
       (apply #'append (nreverse name-nodes)))
      ((js2-array-node-p node)
       (dolist (elem (js2-array-node-elems node))
         (when elem
+          (setq elem (cond ((js2-infix-node-p elem) ;; default (=)
+                            (js2-infix-node-left elem))
+                           ((js2-unary-node-p elem) ;; rest (...)
+                            (js2-unary-node-operand elem))
+                           (t elem)))
           (push (js2-define-destruct-symbols
                  elem decl-type face ignore-not-in-block)
                 name-nodes)))
@@ -8053,8 +8188,7 @@ represented by FN-NODE at POS."
                        new-param-name-nodes (js2-define-destruct-symbols
                                              param js2-LP 'js2-function-param))
                  (js2-check-strict-function-params param-name-nodes 
new-param-name-nodes)
-                 (setq param-name-nodes (append param-name-nodes 
new-param-name-nodes))
-                 (push param params))
+                 (setq param-name-nodes (append param-name-nodes 
new-param-name-nodes)))
                 ;; variable name
                 (t
                  (when (and (>= js2-language-version 200)
@@ -8068,22 +8202,23 @@ represented by FN-NODE at POS."
                  (setq param (js2-create-name-node))
                  (js2-define-symbol js2-LP (js2-current-token-string) param)
                  (js2-check-strict-function-params param-name-nodes (list 
param))
-                 (setq param-name-nodes (append param-name-nodes (list param)))
-                 ;; default parameter value
-                 (when (and (>= js2-language-version 200)
-                            (js2-match-token js2-ASSIGN))
-                   (cl-assert (not paren-free-arrow))
-                   (let* ((pos (js2-node-pos param))
-                          (tt (js2-current-token-type))
-                          (op-pos (- (js2-current-token-beg) pos))
-                          (left param)
-                          (right (js2-parse-assign-expr))
-                          (len (- (js2-node-end right) pos)))
-                     (setq param (make-js2-assign-node
-                                  :type tt :pos pos :len len :op-pos op-pos
-                                  :left left :right right))
-                     (js2-node-add-children param left right)))
-                 (push param params)))
+                 (setq param-name-nodes (append param-name-nodes (list 
param)))))
+               ;; default parameter value
+               (when (and (not rest-param-at)
+                          (>= js2-language-version 200)
+                          (js2-match-token js2-ASSIGN))
+                 (cl-assert (not paren-free-arrow))
+                 (let* ((pos (js2-node-pos param))
+                        (tt (js2-current-token-type))
+                        (op-pos (- (js2-current-token-beg) pos))
+                        (left param)
+                        (right (js2-parse-assign-expr))
+                        (len (- (js2-node-end right) pos)))
+                   (setq param (make-js2-assign-node
+                                :type tt :pos pos :len len :op-pos op-pos
+                                :left left :right right))
+                   (js2-node-add-children param left right)))
+               (push param params)
                (when (and rest-param-at (> (length params) (1+ rest-param-at)))
                  (js2-report-error "msg.param.after.rest" nil
                                    (js2-node-pos param) (js2-node-len param)))
@@ -8116,7 +8251,7 @@ Last token scanned is the close-curly for the function 
body."
                                   (js2-name-node-name name) pos end)
         (js2-add-strict-warning "msg.anon.no.return.value" nil pos end)))))
 
-(defun js2-parse-function-stmt ()
+(defun js2-parse-function-stmt (&optional async-p)
   (let ((pos (js2-current-token-beg))
         (star-p (js2-match-token js2-MUL)))
     (js2-must-match-name "msg.unnamed.function.stmt")
@@ -8124,28 +8259,31 @@ Last token scanned is the close-curly for the function 
body."
           pn member-expr)
       (cond
        ((js2-match-token js2-LP)
-        (js2-parse-function 'FUNCTION_STATEMENT pos star-p name))
+        (js2-parse-function 'FUNCTION_STATEMENT pos star-p async-p name))
        (js2-allow-member-expr-as-function-name
         (setq member-expr (js2-parse-member-expr-tail nil name))
         (js2-parse-highlight-member-expr-fn-name member-expr)
         (js2-must-match js2-LP "msg.no.paren.parms")
-        (setf pn (js2-parse-function 'FUNCTION_STATEMENT pos star-p)
+        (setf pn (js2-parse-function 'FUNCTION_STATEMENT pos star-p async-p)
               (js2-function-node-member-expr pn) member-expr)
         pn)
        (t
         (js2-report-error "msg.no.paren.parms")
         (make-js2-error-node))))))
 
-(defun js2-parse-function-expr ()
+(defun js2-parse-async-function-stmt ()
+  (js2-parse-function-stmt t))
+
+(defun js2-parse-function-expr (&optional async-p)
   (let ((pos (js2-current-token-beg))
         (star-p (js2-match-token js2-MUL))
         name)
     (when (js2-match-token js2-NAME)
       (setq name (js2-create-name-node t)))
     (js2-must-match js2-LP "msg.no.paren.parms")
-    (js2-parse-function 'FUNCTION_EXPRESSION pos star-p name)))
+    (js2-parse-function 'FUNCTION_EXPRESSION pos star-p async-p name)))
 
-(defun js2-parse-function-internal (function-type pos star-p &optional name)
+(defun js2-parse-function-internal (function-type pos star-p &optional async-p 
name)
   (let (fn-node lp)
     (if (= (js2-current-token-type) js2-LP) ; eventually matched LP?
         (setq lp (js2-current-token-beg)))
@@ -8153,7 +8291,8 @@ Last token scanned is the close-curly for the function 
body."
                                           :name name
                                           :form function-type
                                           :lp (if lp (- lp pos))
-                                          :generator-type (and star-p 'STAR)))
+                                          :generator-type (and star-p 'STAR)
+                                          :async async-p))
     (when name
       (js2-set-face (js2-node-pos name) (js2-node-end name)
                     'font-lock-function-name-face 'record)
@@ -8208,7 +8347,7 @@ Last token scanned is the close-curly for the function 
body."
     (setf (js2-scope-parent-scope fn-node) js2-current-scope)
     fn-node))
 
-(defun js2-parse-function (function-type pos star-p &optional name)
+(defun js2-parse-function (function-type pos star-p &optional async-p name)
   "Function parser.  FUNCTION-TYPE is a symbol, POS is the
 beginning of the first token (function keyword, unless it's an
 arrow function), NAME is js2-name-node."
@@ -8224,7 +8363,7 @@ arrow function), NAME is js2-name-node."
       (setq ts-state (make-js2-ts-state))
       (setq continue (catch 'reparse
                        (setq fn-node (js2-parse-function-internal
-                                      function-type pos star-p name))
+                                      function-type pos star-p async-p name))
                        ;; Don't continue.
                        nil))
       (when continue
@@ -8334,9 +8473,12 @@ node are given relative start positions and correct 
lengths."
 (defun js2-statement-helper ()
   (let* ((tt (js2-get-token))
          (first-tt tt)
+         (async-stmt (js2-match-async-function))
          (parser (if (= tt js2-ERROR)
                      #'js2-parse-semi
-                   (aref js2-parsers tt)))
+                   (if async-stmt
+                       #'js2-parse-async-function-stmt
+                     (aref js2-parsers tt))))
          pn)
     ;; If the statement is set, then it's been told its label by now.
     (and js2-labeled-stmt
@@ -8345,7 +8487,8 @@ node are given relative start positions and correct 
lengths."
     (setq pn (funcall parser))
     ;; Don't do auto semi insertion for certain statement types.
     (unless (or (memq first-tt js2-no-semi-insertion)
-                (js2-labeled-stmt-node-p pn))
+                (js2-labeled-stmt-node-p pn)
+                async-stmt)
       (js2-auto-insert-semicolon pn))
     pn))
 
@@ -8474,7 +8617,7 @@ imports or a namespace import that follows it.
               (js2-define-symbol
                js2-LET (js2-name-node-name name-node) name-node t))))))
      ((= (js2-peek-token) js2-NAME)
-      (let ((binding (js2-maybe-parse-export-binding)))
+      (let ((binding (js2-maybe-parse-export-binding t)))
         (let ((node-name (js2-export-binding-node-local-name binding)))
           (js2-define-symbol js2-LET (js2-name-node-name node-name) node-name 
t))
         (setf (js2-import-clause-node-default-binding clause) binding)
@@ -8507,50 +8650,47 @@ imports or a namespace import that follows it.
   "Parse a namespace import expression such as  '* as bar'.
 The current token must be js2-MUL."
   (let ((beg (js2-current-token-beg)))
-    (when (js2-must-match js2-NAME "msg.syntax")
-        (if (equal "as" (js2-current-token-string))
-            (when (js2-must-match-prop-name "msg.syntax")
-              (let ((node (make-js2-namespace-import-node
-                           :pos beg
-                           :len (- (js2-current-token-end) beg)
-                           :name (make-js2-name-node
-                                  :pos (js2-current-token-beg)
-                                  :len (js2-current-token-end)
-                                  :name (js2-current-token-string)))))
-                (js2-node-add-children node (js2-namespace-import-node-name 
node))
-                node))
-          (js2-unget-token)
-          (js2-report-error "msg.syntax")))))
+    (if (js2-match-contextual-kwd "as")
+        (when (js2-must-match-prop-name "msg.syntax")
+          (let ((node (make-js2-namespace-import-node
+                       :pos beg
+                       :len (- (js2-current-token-end) beg)
+                       :name (make-js2-name-node
+                              :pos (js2-current-token-beg)
+                              :len (js2-current-token-end)
+                              :name (js2-current-token-string)))))
+            (js2-node-add-children node (js2-namespace-import-node-name node))
+            node))
+      (js2-unget-token)
+      (js2-report-error "msg.syntax"))))
 
 
 (defun js2-parse-from-clause ()
   "Parse the from clause in an import or export statement. E.g. from 'src/lib'"
-  (when (js2-must-match-name "msg.mod.from.after.import.spec.set")
-    (let ((beg (js2-current-token-beg)))
-      (if (equal "from" (js2-current-token-string))
-          (cond
-           ((js2-match-token js2-STRING)
-            (make-js2-from-clause-node
-             :pos beg
-             :len (- (js2-current-token-end) beg)
-             :module-id (js2-current-token-string)
-             :metadata-p nil))
-           ((js2-match-token js2-THIS)
-            (when (js2-must-match-name "msg.mod.spec.after.from")
-              (if (equal "module" (js2-current-token-string))
-                  (make-js2-from-clause-node
-                   :pos beg
-                   :len (- (js2-current-token-end) beg)
-                   :module-id "this"
-                   :metadata-p t)
-                (js2-unget-token)
-                (js2-unget-token)
-                (js2-report-error "msg.mod.spec.after.from")
-                nil)))
-           (t (js2-report-error "msg.mod.spec.after.from") nil))
-        (js2-unget-token)
-        (js2-report-error "msg.mod.from.after.import.spec.set")
-        nil))))
+  (if (js2-match-contextual-kwd "from")
+      (let ((beg (js2-current-token-beg)))
+        (cond
+         ((js2-match-token js2-STRING)
+          (make-js2-from-clause-node
+           :pos beg
+           :len (- (js2-current-token-end) beg)
+           :module-id (js2-current-token-string)
+           :metadata-p nil))
+         ((js2-match-token js2-THIS)
+          (when (js2-must-match-name "msg.mod.spec.after.from")
+            (if (equal "module" (js2-current-token-string))
+                (make-js2-from-clause-node
+                 :pos beg
+                 :len (- (js2-current-token-end) beg)
+                 :module-id "this"
+                 :metadata-p t)
+              (js2-unget-token)
+              (js2-unget-token)
+              (js2-report-error "msg.mod.spec.after.from")
+              nil)))
+         (t (js2-report-error "msg.mod.spec.after.from") nil)))
+    (js2-report-error "msg.mod.from.after.import.spec.set")
+    nil))
 
 (defun js2-parse-export-bindings (&optional import-p)
   "Parse a list of export binding expressions such as {}, {foo, bar}, and
@@ -8558,7 +8698,7 @@ The current token must be js2-MUL."
 js2-LC. Return a lisp list of js2-export-binding-node"
   (let ((bindings (list)))
     (while
-        (let ((binding (js2-maybe-parse-export-binding)))
+        (let ((binding (js2-maybe-parse-export-binding import-p)))
           (when binding
             (push binding bindings))
           (js2-match-token js2-COMMA)))
@@ -8567,7 +8707,7 @@ js2-LC. Return a lisp list of js2-export-binding-node"
                                    "msg.mod.rc.after.export.spec.list"))
       (reverse bindings))))
 
-(defun js2-maybe-parse-export-binding ()
+(defun js2-maybe-parse-export-binding (&optional import-p)
   "Attempt to parse a binding expression found inside an import/export 
statement.
 This can take the form of either as single js2-NAME token as in 'foo' or as in 
a
 rebinding expression 'bar as foo'. If it matches, it will return an instance of
@@ -8579,45 +8719,49 @@ consumes no tokens."
         (is-reserved-name (or (= (js2-current-token-type) js2-RESERVED)
                               (aref js2-kwd-tokens (js2-current-token-type)))))
     (if extern-name
-        (let ((as (and (js2-match-token js2-NAME) (js2-current-token-string))))
-          (if (and as (equal "as" (js2-current-token-string)))
-              (let ((name
-                     (or
-                      (and (js2-match-token js2-DEFAULT) "default")
-                      (and (js2-match-token js2-NAME) 
(js2-current-token-string)))))
-                (if name
-                    (let ((node (make-js2-export-binding-node
-                                 :pos beg
-                                 :len (- (js2-current-token-end) beg)
-                                 :local-name (make-js2-name-node
-                                              :name name
-                                              :pos (js2-current-token-beg)
-                                              :len (js2-current-token-len))
-                                 :extern-name (make-js2-name-node
-                                               :name extern-name
-                                               :pos beg
-                                               :len extern-name-len))))
-                      (js2-node-add-children
-                       node
-                       (js2-export-binding-node-local-name node)
-                       (js2-export-binding-node-extern-name node))
-                      node)
-                  (js2-unget-token)
-                  nil))
-            (when as (js2-unget-token))
-            (let* ((name-node (make-js2-name-node
-                              :name (js2-current-token-string)
-                              :pos (js2-current-token-beg)
-                              :len (js2-current-token-len)))
-                  (node (make-js2-export-binding-node
-                         :pos (js2-current-token-beg)
-                         :len (js2-current-token-len)
-                         :local-name name-node
-                         :extern-name name-node)))
-              (when is-reserved-name
-                (js2-report-error "msg.mod.as.after.reserved.word" 
extern-name))
-              (js2-node-add-children node name-node)
-              node)))
+        (if (js2-match-contextual-kwd "as")
+            (let ((name
+                   (or
+                    (and (js2-match-token js2-DEFAULT) "default")
+                    (and (js2-match-token js2-NAME) 
(js2-current-token-string)))))
+              (if name
+                  (let ((node (make-js2-export-binding-node
+                               :pos beg
+                               :len (- (js2-current-token-end) beg)
+                               :local-name (make-js2-name-node
+                                            :name name
+                                            :pos (js2-current-token-beg)
+                                            :len (js2-current-token-len))
+                               :extern-name (make-js2-name-node
+                                             :name extern-name
+                                             :pos beg
+                                             :len extern-name-len))))
+                    (js2-node-add-children
+                     node
+                     (js2-export-binding-node-local-name node)
+                     (js2-export-binding-node-extern-name node))
+                    (if import-p
+                        (js2-set-face (js2-current-token-beg) 
(js2-current-token-end)
+                                      'font-lock-variable-name-face 'record))
+                    node)
+                (js2-unget-token)
+                nil))
+          (let* ((name-node (make-js2-name-node
+                             :name (js2-current-token-string)
+                             :pos (js2-current-token-beg)
+                             :len (js2-current-token-len)))
+                 (node (make-js2-export-binding-node
+                        :pos (js2-current-token-beg)
+                        :len (js2-current-token-len)
+                        :local-name name-node
+                        :extern-name name-node)))
+            (when is-reserved-name
+              (js2-report-error "msg.mod.as.after.reserved.word" extern-name))
+            (js2-node-add-children node name-node)
+            (if import-p
+                (js2-set-face (js2-current-token-beg) (js2-current-token-end)
+                              'font-lock-variable-name-face 'record))
+            node))
       nil)))
 
 (defun js2-parse-switch ()
@@ -8745,27 +8889,45 @@ invalid export statements."
       (when exports-list
         (dolist (export exports-list)
           (push export children)))
-      (when (js2-match-token js2-NAME)
-        (if (equal "from" (js2-current-token-string))
-            (progn
-              (js2-unget-token)
-              (setq from-clause (js2-parse-from-clause)))
-          (js2-unget-token))))
+      (when (js2-match-contextual-kwd "from")
+        (js2-unget-token)
+        (setq from-clause (js2-parse-from-clause))))
      ((js2-match-token js2-DEFAULT)
-      (setq default (js2-parse-expr)))
+      (setq default (cond ((js2-match-token js2-CLASS)
+                           (js2-parse-class-stmt))
+                          ((js2-match-token js2-NAME)
+                           (if (js2-match-async-function)
+                               (js2-parse-async-function-stmt)
+                             (js2-unget-token)
+                             (js2-parse-expr)))
+                          ((js2-match-token js2-FUNCTION)
+                           (js2-parse-function-stmt))
+                          (t (js2-parse-expr)))))
      ((or (js2-match-token js2-VAR) (js2-match-token js2-CONST) 
(js2-match-token js2-LET))
       (setq declaration (js2-parse-variables (js2-current-token-type) 
(js2-current-token-beg))))
+     ((js2-match-token js2-CLASS)
+      (setq declaration (js2-parse-class-stmt)))
+     ((js2-match-token js2-NAME)
+      (setq declaration
+            (if (js2-match-async-function)
+                (js2-parse-async-function-stmt)
+              (js2-unget-token)
+              (js2-parse-expr))))
+     ((js2-match-token js2-FUNCTION)
+      (setq declaration (js2-parse-function-stmt)))
      (t
       (setq declaration (js2-parse-expr))))
     (when from-clause
       (push from-clause children))
     (when declaration
       (push declaration children)
-      (when (not (js2-function-node-p declaration))
+      (when (not (or (js2-function-node-p declaration)
+                     (js2-class-node-p declaration)))
         (js2-auto-insert-semicolon declaration)))
     (when default
       (push default children)
-      (when (not (js2-function-node-p default))
+      (when (not (or (js2-function-node-p default)
+                     (js2-class-node-p default)))
         (js2-auto-insert-semicolon default)))
     (let ((node (make-js2-export-node
                   :pos beg
@@ -8810,7 +8972,7 @@ Last matched token must be js2-FOR."
              ((= tt js2-SEMI)
               (js2-unget-token)
               (setq init (make-js2-empty-expr-node)))
-             ((or (= tt js2-VAR) (= tt js2-LET))
+             ((or (= tt js2-VAR) (= tt js2-LET) (= tt js2-CONST))
               (setq init (js2-parse-variables tt (js2-current-token-beg))))
              (t
               (js2-unget-token)
@@ -9448,16 +9610,10 @@ If NODE is non-nil, it is the AST node associated with 
the symbol."
          (pos (if node (js2-node-abs-pos node)))
          (len (if node (js2-node-len node))))
     (cond
-     ((and symbol ; already defined
-           (or (if js2-in-use-strict-directive
-                   ;; two const-bound vars in this block have same name
-                   (and (= sdt js2-CONST)
-                        (eq defining-scope js2-current-scope))
-                 (or (= sdt js2-CONST)          ; old version is const
-                     (= decl-type js2-CONST)))  ; new version is const
-               ;; two let-bound vars in this block have same name
-               (and (= sdt js2-LET)
-                    (eq defining-scope js2-current-scope))))
+     ((and symbol ; already defined in this block
+           (or (= sdt js2-LET)
+               (= sdt js2-CONST))
+           (eq defining-scope js2-current-scope))
       (js2-report-error
        (cond
         ((= sdt js2-CONST) "msg.const.redecl")
@@ -9467,9 +9623,7 @@ If NODE is non-nil, it is the AST node associated with 
the symbol."
         (t "msg.parm.redecl"))
        name pos len))
      ((or (= decl-type js2-LET)
-          ;; strict mode const is scoped to the current LexicalEnvironment
-          (and js2-in-use-strict-directive
-               (= decl-type js2-CONST)))
+          (= decl-type js2-CONST))
       (if (and (= decl-type js2-LET)
                (not ignore-not-in-block)
                (or (= (js2-node-type js2-current-scope) js2-IF)
@@ -9477,10 +9631,7 @@ If NODE is non-nil, it is the AST node associated with 
the symbol."
           (js2-report-error "msg.let.decl.not.in.block")
         (js2-define-new-symbol decl-type name node)))
      ((or (= decl-type js2-VAR)
-          (= decl-type js2-FUNCTION)
-          ;; sloppy mode const is scoped to the current VariableEnvironment
-          (and (not js2-in-use-strict-directive)
-               (= decl-type js2-CONST)))
+          (= decl-type js2-FUNCTION))
       (if symbol
           (if (and js2-strict-var-redeclaration-warning (= sdt js2-VAR))
               (js2-add-strict-warning "msg.var.redecl" name)
@@ -9548,9 +9699,20 @@ If NODE is non-nil, it is the AST node associated with 
the symbol."
   (let ((tt (js2-get-token))
         (pos (js2-current-token-beg))
         pn left right op-pos
-        ts-state recorded-identifiers parsed-errors)
+        ts-state recorded-identifiers parsed-errors
+        async-p)
     (if (= tt js2-YIELD)
         (js2-parse-return-or-yield tt t)
+      ;; TODO(mooz): Bit confusing.
+      ;; If we meet `async` token and it's not part of `async
+      ;; function`, then this `async` is for a succeeding async arrow
+      ;; function.
+      ;; Since arrow function parsing doesn't rely on neither
+      ;; `js2-parse-function-stmt' nor `js2-parse-function-expr' that
+      ;; interpret `async` token, we trash `async` and just remember
+      ;; we met `async` keyword to `async-p'.
+      (when (js2-match-async-arrow-function)
+        (setq async-p t))
       ;; Save the tokenizer state in case we find an arrow function
       ;; and have to rewind.
       (setq ts-state (make-js2-ts-state)
@@ -9584,9 +9746,12 @@ If NODE is non-nil, it is the AST node associated with 
the symbol."
        ((and (= tt js2-ARROW)
              (>= js2-language-version 200))
         (js2-ts-seek ts-state)
+        (when async-p
+          (js2-record-face 'font-lock-keyword-face)
+          (js2-get-token))
         (setq js2-recorded-identifiers recorded-identifiers
               js2-parsed-errors parsed-errors)
-        (setq pn (js2-parse-function 'FUNCTION_ARROW (js2-current-token-beg) 
nil)))
+        (setq pn (js2-parse-function 'FUNCTION_ARROW (js2-current-token-beg) 
nil async-p)))
        (t
         (js2-unget-token)))
       pn)))
@@ -9616,7 +9781,7 @@ If NODE is non-nil, it is the AST node associated with 
the symbol."
       (js2-node-add-children pn test-expr if-true if-false))
     pn))
 
-(defun js2-make-binary (type left parser)
+(defun js2-make-binary (type left parser &optional no-get)
   "Helper for constructing a binary-operator AST node.
 LEFT is the left-side-expression, already parsed, and the
 binary operator should have just been matched.
@@ -9627,7 +9792,7 @@ FIXME: The latter option is unused?"
          (op-pos (- (js2-current-token-beg) pos))
          (right (if (js2-node-p parser)
                     parser
-                  (js2-get-token)
+                  (unless no-get (js2-get-token))
                   (funcall parser)))
          (pn (make-js2-infix-node :type type
                                   :pos pos
@@ -9816,6 +9981,7 @@ to parse the operand (for prefix operators)."
      ((= tt js2-DELPROP)
       (js2-get-token)
       (js2-make-unary js2-DELPROP 'js2-parse-unary-expr))
+     ((js2-parse-await-maybe tt))
      ((= tt js2-ERROR)
       (js2-get-token)
       (make-js2-error-node))  ; try to continue
@@ -9968,9 +10134,9 @@ Returns an expression tree that includes PN, the parent 
node."
         (setq pn (js2-parse-tagged-template pn (make-js2-string-node :type 
tt))))
        (t
         (js2-unget-token)
-        (setq continue nil))))
-    (if (>= js2-highlight-level 2)
-        (js2-parse-highlight-member-expr-node pn))
+        (setq continue nil)))
+      (if (>= js2-highlight-level 2)
+          (js2-parse-highlight-member-expr-node pn)))
     pn))
 
 (defun js2-parse-tagged-template (tag-node tpl-node)
@@ -10205,6 +10371,8 @@ array-literals, array comprehensions and regular 
expressions."
       (js2-parse-class-expr))
      ((= tt js2-FUNCTION)
       (js2-parse-function-expr))
+     ((js2-match-async-function)
+      (js2-parse-function-expr t))
      ((= tt js2-LB)
       (js2-parse-array-comp-or-literal))
      ((= tt js2-LC)
@@ -10221,7 +10389,8 @@ array-literals, array comprehensions and regular 
expressions."
      ((= tt js2-NUMBER)
       (setq node (make-js2-number-node))
       (when (and js2-in-use-strict-directive
-                 (= (js2-number-node-num-base node) 8))
+                 (= (js2-number-node-num-base node) 8)
+                 (js2-number-node-legacy-octal-p node))
         (js2-report-error "msg.no.octal.strict"))
       node)
      ((or (= tt js2-STRING) (= tt js2-NO_SUBS_TEMPLATE))
@@ -10320,19 +10489,13 @@ array-literals, array comprehensions and regular 
expressions."
 
 (defun js2-parse-array-literal (pos)
   (let ((after-lb-or-comma t)
-        after-comma tt elems pn
+        after-comma tt elems pn was-rest
         (continue t))
     (unless js2-is-in-destructuring
       (js2-push-scope (make-js2-scope))) ; for the legacy array comp
     (while continue
       (setq tt (js2-get-token))
       (cond
-       ;; comma
-       ((= tt js2-COMMA)
-        (setq after-comma (js2-current-token-end))
-        (if (not after-lb-or-comma)
-            (setq after-lb-or-comma t)
-          (push nil elems)))
        ;; end of array
        ((or (= tt js2-RB)
             (= tt js2-EOF))  ; prevent infinite loop
@@ -10346,21 +10509,18 @@ array-literals, array comprehensions and regular 
expressions."
                                       :len (- js2-ts-cursor pos)
                                       :elems (nreverse elems)))
         (apply #'js2-node-add-children pn (js2-array-node-elems pn)))
-       ;; destructuring binding
-       (js2-is-in-destructuring
-        (push (if (or (= tt js2-LC)
-                      (= tt js2-LB)
-                      (= tt js2-NAME))
-                  ;; [a, b, c] | {a, b, c} | {a:x, b:y, c:z} | a
-                  (js2-parse-destruct-primary-expr)
-                ;; invalid pattern
-                (js2-report-error "msg.bad.var")
-                (make-js2-error-node))
-              elems)
-        (setq after-lb-or-comma nil
-              after-comma nil))
+       ;; anything after rest element (...foo)
+       (was-rest
+        (js2-report-error "msg.param.after.rest"))
+       ;; comma
+       ((= tt js2-COMMA)
+        (setq after-comma (js2-current-token-end))
+        (if (not after-lb-or-comma)
+            (setq after-lb-or-comma t)
+          (push nil elems)))
        ;; array comp
        ((and (>= js2-language-version 170)
+             (not js2-is-in-destructuring)
              (= tt js2-FOR)          ; check for array comprehension
              (not after-lb-or-comma) ; "for" can't follow a comma
              elems                   ; must have at least 1 element
@@ -10374,9 +10534,12 @@ array-literals, array comprehensions and regular 
expressions."
           (js2-report-error "msg.no.bracket.arg"))
         (if (and (= tt js2-TRIPLEDOT)
                  (>= js2-language-version 200))
-            ;; spread operator
-            (push (js2-make-unary tt 'js2-parse-assign-expr)
-                  elems)
+            ;; rest/spread operator
+            (progn
+              (push (js2-make-unary tt 'js2-parse-assign-expr)
+                    elems)
+              (if js2-is-in-destructuring
+                  (setq was-rest t)))
           (js2-unget-token)
           (push (js2-parse-assign-expr) elems))
         (setq after-lb-or-comma nil
@@ -10526,6 +10689,7 @@ If ONLY-OF-P is non-nil, only the 'for (foo of bar)' 
form is allowed."
     (js2-set-face (js2-node-pos name) (js2-node-end name)
                   'font-lock-function-name-face 'record)
     (let ((node (js2-parse-class pos 'CLASS_STATEMENT name)))
+      (js2-record-imenu-functions node name)
       (js2-define-symbol js2-FUNCTION
                          (js2-name-node-name name)
                          node)
@@ -10570,17 +10734,22 @@ If ONLY-OF-P is non-nil, only the 'for (foo of bar)' 
form is allowed."
 
 (defun js2-property-key-string (property-node)
   "Return the key of PROPERTY-NODE (a `js2-object-prop-node' or
-`js2-getter-setter-node') as a string, or nil if it can't be
+`js2-method-node') as a string, or nil if it can't be
 represented as a string (e.g., the key is computed by an
 expression)."
-  (let ((key (js2-infix-node-left property-node)))
-    (cond
-     ((js2-name-node-p key)
-      (js2-name-node-name key))
-     ((js2-string-node-p key)
-      (js2-string-node-value key))
-     ((js2-number-node-p key)
-      (js2-number-node-value key)))))
+  (cond
+   ((js2-unary-node-p property-node) nil) ;; {...foo}
+   (t
+    (let ((key (js2-infix-node-left property-node)))
+      (when (js2-computed-prop-name-node-p key)
+        (setq key (js2-computed-prop-name-node-expr key)))
+      (cond
+       ((js2-name-node-p key)
+        (js2-name-node-name key))
+       ((js2-string-node-p key)
+        (js2-string-node-value key))
+       ((js2-number-node-p key)
+        (js2-number-node-value key)))))))
 
 (defun js2-parse-object-literal-elems (&optional class-p)
   (let ((pos (js2-current-token-beg))
@@ -10605,21 +10774,26 @@ expression)."
                  (= js2-MUL tt))
         (setq previous-token (js2-current-token)
               tt (js2-get-prop-name-token)))
-      ;; Handle 'get' or 'set' keywords
+      ;; Handle getter, setter and async methods
       (let ((prop (js2-current-token-string)))
         (when (and (>= js2-language-version 200)
                    (= js2-NAME tt)
-                   (or (string= prop "get")
-                       (string= prop "set"))
+                   (member prop '("get" "set" "async"))
                    (member (js2-peek-token)
                            (list js2-NAME js2-STRING js2-NUMBER js2-LB)))
           (setq previous-token (js2-current-token)
                 tt (js2-get-prop-name-token))))
       (cond
-       ;; Found a property (of any sort)
+       ;; Rest/spread (...expr)
+       ((and (>= js2-language-version 200)
+             (not class-p) (not static) (not previous-token)
+             (= js2-TRIPLEDOT tt))
+        (setq after-comma nil
+              elem (js2-make-unary js2-TRIPLEDOT 'js2-parse-assign-expr)))
+       ;; Found a key/value property (of any sort)
        ((member tt (list js2-NAME js2-STRING js2-NUMBER js2-LB))
         (setq after-comma nil
-              elem (js2-parse-named-prop tt pos previous-token))
+              elem (js2-parse-named-prop tt previous-token))
         (if (and (null elem)
                  (not js2-recover-from-parse-errors))
             (setq continue nil)))
@@ -10631,6 +10805,10 @@ expression)."
         (if after-comma
             (js2-parse-warn-trailing-comma "msg.extra.trailing.comma"
                                            pos elems after-comma)))
+       ;; Skip semicolons in a class body
+       ((and class-p
+             (= tt js2-SEMI))
+        nil)
        (t
         (js2-report-error "msg.bad.prop")
         (unless js2-recover-from-parse-errors
@@ -10654,7 +10832,16 @@ expression)."
                     (lambda (previous-elem)
                       (and (setq previous-elem-key-string
                                  (js2-property-key-string previous-elem))
-                           (string= previous-elem-key-string elem-key-string)))
+                           ;; Check if the property is a duplicate.
+                           (string= previous-elem-key-string elem-key-string)
+                           ;; But make an exception for getter / setter pairs.
+                           (not (and (js2-method-node-p elem)
+                                     (js2-method-node-p previous-elem)
+                                     (let ((type (js2-node-get-prop 
(js2-method-node-right elem) 'METHOD_TYPE))
+                                           (previous-type (js2-node-get-prop 
(js2-method-node-right previous-elem) 'METHOD_TYPE)))
+                                       (and (member type '(GET SET))
+                                            (member previous-type '(GET SET))
+                                            (not (eq type previous-type))))))))
                     elems))
           (js2-report-error "msg.dup.obj.lit.prop.strict"
                             elem-key-string
@@ -10665,44 +10852,34 @@ expression)."
     (js2-must-match js2-RC "msg.no.brace.prop")
     (nreverse elems)))
 
-(defun js2-parse-named-prop (tt pos previous-token)
+(defun js2-parse-named-prop (tt previous-token)
   "Parse a name, string, or getter/setter object property.
 When `js2-is-in-destructuring' is t, forms like {a, b, c} will be permitted."
-  (let ((key (cond
-              ;; Literal string keys: {'foo': 'bar'}
-              ((= tt js2-STRING)
-               (make-js2-string-node))
-              ;; Handle computed keys: {[Symbol.iterator]: ...}, *[1+2]() 
{...}},
-              ;; {[foo + bar]() { ... }}, {[get ['x' + 1]() {...}}
-              ((and (= tt js2-LB)
-                    (>= js2-language-version 200))
-               (prog1 (js2-parse-expr)
-                 (js2-must-match js2-RB "msg.missing.computed.rb")))
-              ;; Numeric keys: {12: 'foo'}, {10.7: 'bar'}
-              ((= tt js2-NUMBER)
-               (make-js2-number-node))
-              ;; Unquoted names: {foo: 12}
-              ((= tt js2-NAME)
-               (js2-create-name-node))
-              ;; Anything else is an error
-              (t (js2-report-error "msg.bad.prop"))))
+  (let ((key (js2-parse-prop-name tt))
         (prop (and previous-token (js2-token-string previous-token)))
         (property-type (when previous-token
                              (if (= (js2-token-type previous-token) js2-MUL)
                                  "*"
-                               (js2-token-string previous-token)))))
-    (when (or (string= prop "get")
-              (string= prop "set"))
+                               (js2-token-string previous-token))))
+        pos)
+    (when (member prop '("get" "set" "async"))
+      (setq pos (js2-token-beg previous-token))
       (js2-set-face (js2-token-beg previous-token)
                     (js2-token-end previous-token)
-                    'font-lock-keyword-face 'record))  ; get/set
+                    'font-lock-keyword-face 'record))  ; get/set/async
     (cond
      ;; method definition: {f() {...}}
      ((and (= (js2-peek-token) js2-LP)
            (>= js2-language-version 200))
       (when (js2-name-node-p key)  ; highlight function name properties
         (js2-record-face 'font-lock-function-name-face))
-      (js2-parse-getter-setter-prop pos key property-type))
+      (js2-parse-method-prop pos key property-type))
+     ;; binding element with initializer
+     ((and (= (js2-peek-token) js2-ASSIGN)
+           (>= js2-language-version 200))
+      (if (not js2-is-in-destructuring)
+          (js2-report-error "msg.init.no.destruct"))
+      (js2-parse-initialized-binding key))
      ;; regular prop
      (t
       (let ((beg (js2-current-token-beg))
@@ -10717,10 +10894,38 @@ When `js2-is-in-destructuring' is t, forms like {a, 
b, c} will be permitted."
                       (if (js2-function-node-p
                            (js2-object-prop-node-right expr))
                           'font-lock-function-name-face
-                        'font-lock-variable-name-face)
+                        'js2-object-property)
                       'record)
         expr)))))
 
+(defun js2-parse-initialized-binding (name)
+  "Parse a `SingleNameBinding' with initializer.
+
+`name' is the `BindingIdentifier'."
+  (when (js2-match-token js2-ASSIGN)
+    (js2-make-binary js2-ASSIGN name 'js2-parse-assign-expr t)))
+
+(defun js2-parse-prop-name (tt)
+  (cond
+   ;; Literal string keys: {'foo': 'bar'}
+   ((= tt js2-STRING)
+    (make-js2-string-node))
+   ;; Handle computed keys: {[Symbol.iterator]: ...}, *[1+2]() {...}},
+   ;; {[foo + bar]() { ... }}, {[get ['x' + 1]() {...}}
+   ((and (= tt js2-LB)
+         (>= js2-language-version 200))
+    (make-js2-computed-prop-name-node
+     :expr (prog1 (js2-parse-assign-expr)
+             (js2-must-match js2-RB "msg.missing.computed.rb"))))
+   ;; Numeric keys: {12: 'foo'}, {10.7: 'bar'}
+   ((= tt js2-NUMBER)
+    (make-js2-number-node))
+   ;; Unquoted names: {foo: 12}
+   ((= tt js2-NAME)
+    (js2-create-name-node))
+   ;; Anything else is an error
+   (t (js2-report-error "msg.bad.prop"))))
+
 (defun js2-parse-plain-property (prop)
   "Parse a non-getter/setter property in an object literal.
 PROP is the node representing the property: a number, name,
@@ -10762,11 +10967,12 @@ string or expression."
       (js2-node-add-children result prop expr)
       result))))
 
-(defun js2-parse-getter-setter-prop (pos prop type-string)
-  "Parse getter or setter property in an object literal.
+(defun js2-parse-method-prop (pos prop type-string)
+  "Parse method property in an object literal or a class body.
 JavaScript syntax is:
 
-  { get foo() {...}, set foo(x) {...} }
+  { foo(...) {...}, get foo() {...}, set foo(x) {...}, *foo(...) {...},
+    async foo(...) {...} }
 
 and expression closure style is also supported
 
@@ -10775,26 +10981,24 @@ and expression closure style is also supported
 POS is the start position of the `get' or `set' keyword.
 PROP is the `js2-name-node' representing the property name.
 TYPE-STRING is a string `get', `set', `*', or nil, indicating a found keyword."
-  (let ((type (cond
-               ((string= "get" type-string) js2-GET)
-               ((string= "set" type-string) js2-SET)
-               (t js2-FUNCTION)))
-        result end
-        (fn (js2-parse-function-expr)))
-    ;; it has to be an anonymous function, as we already parsed the name
-    (if (/= (js2-node-type fn) js2-FUNCTION)
-        (js2-report-error "msg.bad.prop")
-      (if (cl-plusp (length (js2-function-name fn)))
-          (js2-report-error "msg.bad.prop")))
-    (js2-node-set-prop fn 'GETTER_SETTER type)  ; for codegen
-    (when (string= type-string "*")
-      (setf (js2-function-node-generator-type fn) 'STAR))
+  (let* ((type (or (cdr (assoc type-string '(("get" . GET)
+                                             ("set" . SET)
+                                             ("async" . ASYNC))))
+                   'FUNCTION))
+         result end
+         (pos (js2-current-token-beg))
+         (_ (js2-must-match js2-LP "msg.no.paren.parms"))
+         (fn (js2-parse-function 'FUNCTION_EXPRESSION pos
+                                 (string= type-string "*")
+                                 (eq type 'ASYNC)
+                                 nil)))
+    (js2-node-set-prop fn 'METHOD_TYPE type)  ; for codegen
+    (unless pos (setq pos (js2-node-pos prop)))
     (setq end (js2-node-end fn)
-          result (make-js2-getter-setter-node :type type
-                                              :pos pos
-                                              :len (- end pos)
-                                              :left prop
-                                              :right fn))
+          result (make-js2-method-node :pos pos
+                                       :len (- end pos)
+                                       :left prop
+                                       :right fn))
     (js2-node-add-children result prop fn)
     result))
 
@@ -10815,6 +11019,68 @@ And, if CHECK-ACTIVATION-P is non-nil, use the value 
of TOKEN."
         (js2-check-activation-name s (or token js2-NAME)))
     name))
 
+;;; Use AST to extract semantic information
+
+(defun js2-get-element-index-from-array-node (elem array-node &optional 
hardcoded-array-index)
+  "Get index of ELEM from ARRAY-NODE or 0 and return it as string."
+  (let ((idx 0) elems (rlt hardcoded-array-index))
+    (setq elems (js2-array-node-elems array-node))
+    (if (and elem (not hardcoded-array-index))
+        (setq rlt (catch 'nth-elt
+                    (dolist (x elems)
+                      ;; We know the ELEM does belong to ARRAY-NODE,
+                      (if (eq elem x) (throw 'nth-elt idx))
+                      (setq idx (1+ idx)))
+                    0)))
+    (format "[%s]" rlt)))
+
+(defun js2-print-json-path (&optional hardcoded-array-index)
+  "Print the path to the JSON value under point, and save it in the kill ring.
+If HARDCODED-ARRAY-INDEX provided, array index in JSON path is replaced with 
it."
+  (interactive "P")
+  (js2-reparse)
+  (let (previous-node current-node
+        key-name
+        rlt)
+
+    ;; The `js2-node-at-point' starts scanning from AST root node.
+    ;; So there is no way to optimize it.
+    (setq current-node (js2-node-at-point))
+
+    (while (not (js2-ast-root-p current-node))
+      (cond
+       ;; JSON property node
+       ((js2-object-prop-node-p current-node)
+        (setq key-name (js2-prop-node-name (js2-object-prop-node-left 
current-node)))
+        (if rlt (setq rlt (concat "." key-name rlt))
+          (setq rlt (concat "." key-name))))
+
+       ;; Array node
+       ((or (js2-array-node-p current-node))
+        (setq rlt (concat (js2-get-element-index-from-array-node previous-node
+                                                                 current-node
+                                                                 
hardcoded-array-index)
+                          rlt)))
+
+       ;; Other nodes are ignored
+       (t))
+
+      ;; current node is archived
+      (setq previous-node current-node)
+      ;; Get parent node and continue the loop
+      (setq current-node (js2-node-parent current-node)))
+
+    (cond
+     (rlt
+      ;; Clean the final result
+      (setq rlt (replace-regexp-in-string "^\\." "" rlt))
+      (kill-new rlt)
+      (message "%s => kill-ring" rlt))
+     (t
+      (message "No JSON path found!")))
+
+    rlt))
+
 ;;; Indentation support (bouncing)
 
 ;; In recent-enough Emacs, we reuse the indentation code from
@@ -11310,7 +11576,23 @@ Selecting an error will jump it to the corresponding 
source-buffer error.
 
   (run-hooks 'js2-init-hook)
 
-  (js2-reparse))
+  (let ((js2-idle-timer-delay 0))
+    ;; Schedule parsing for after when the mode hooks run.
+    (js2-mode-reset-timer)))
+
+;; We may eventually want js2-jsx-mode to derive from js-jsx-mode, but that'd 
be
+;; a bit more complicated and it doesn't net us much yet.
+;;;###autoload
+(define-derived-mode js2-jsx-mode js2-mode "JSX-IDE"
+  "Major mode for editing JSX code.
+
+To customize the indentation for this mode, set the SGML offset
+variables (`sgml-basic-offset' et al) locally, like so:
+
+  (defun set-jsx-indentation ()
+    (setq-local sgml-basic-offset js2-basic-offset))
+  (add-hook 'js2-jsx-mode-hook #'set-jsx-indentation)"
+  (set (make-local-variable 'indent-line-function) #'js2-jsx-indent-line))
 
 (defun js2-mode-exit ()
   "Exit `js2-mode' and clean up."
@@ -11647,10 +11929,7 @@ PARSE-STATUS is as documented in `parse-partial-sexp'."
             (insert "\n")
             (indent-to col)
             (insert "*/"))))
-     ((and single
-           (save-excursion
-              (and (zerop (forward-line 1))
-                   (looking-at "\\s-*//"))))
+     (single
       (indent-to col)
       (insert "// ")))
     ;; Don't need to extend the comment after all.
@@ -12297,7 +12576,10 @@ it marks the next defun after the ones already marked."
 (defun js2-jump-to-definition (&optional arg)
   "Jump to the definition of an object's property, variable or function."
   (interactive "P")
-  (ring-insert find-tag-marker-ring (point-marker))
+  (if (eval-when-compile (fboundp 'xref-push-marker-stack))
+      (xref-push-marker-stack)
+    (ring-insert find-tag-marker-ring (point-marker)))
+  (js2-reparse)
   (let* ((node (js2-node-at-point))
          (parent (js2-node-parent node))
          (names (if (js2-prop-get-node-p parent)
diff --git a/packages/js2-mode/js2-old-indent.el 
b/packages/js2-mode/js2-old-indent.el
index efc9053..f336005 100644
--- a/packages/js2-mode/js2-old-indent.el
+++ b/packages/js2-mode/js2-old-indent.el
@@ -54,6 +54,8 @@
 
 ;;; Code:
 
+(require 'sgml-mode)
+
 (defvar js2-language-version)
 
 (declare-function js2-mark-safe-local "js2-mode")
@@ -129,13 +131,13 @@ switch statement body are indented one additional level."
 followed by an opening brace.")
 
 (defconst js2-indent-operator-re
-  (concat "[-+*/%<>&^|?:.]\\([^-+*/]\\|$\\)\\|!?=\\|"
-          (regexp-opt '("in" "instanceof") 'words))
+  (concat "[-+*/%<>&^|?:.]\\([^-+*/.]\\|$\\)\\|!?=\\|"
+          (regexp-opt '("in" "instanceof") 'symbols))
   "Regular expression matching operators that affect indentation
 of continued expressions.")
 
 (defconst js2-declaration-keyword-re
-  (regexp-opt '("var" "let" "const") 'words)
+  (regexp-opt '("var" "let" "const") 'symbols)
   "Regular expression matching variable declaration keywords.")
 
 (defun js2-re-search-forward-inner (regexp &optional bound count)
@@ -215,30 +217,38 @@ and comments have been removed."
 
 (defun js2-looking-at-operator-p ()
   "Return non-nil if text after point is a non-comma operator."
+  (defvar js2-mode-identifier-re)
   (and (looking-at js2-indent-operator-re)
-       (or (not (looking-at ":"))
+       (or (not (eq (char-after) ?:))
            (save-excursion
              (and (js2-re-search-backward "[?:{]\\|\\_<case\\_>" nil t)
-                  (looking-at "?"))))))
+                  (eq (char-after) ??))))
+       (not (and
+             (eq (char-after) ?*)
+             ;; Generator method (possibly using computed property).
+             (looking-at (concat "\\* *\\(?:\\[\\|"
+                                 js2-mode-identifier-re
+                                 " *(\\)"))
+             (save-excursion
+               (js2-backward-sws)
+               ;; We might misindent some expressions that would
+               ;; return NaN anyway.  Shouldn't be a problem.
+               (memq (char-before) '(?, ?} ?{)))))))
 
 (defun js2-continued-expression-p ()
   "Return non-nil if the current line continues an expression."
   (save-excursion
     (back-to-indentation)
-    (or (js2-looking-at-operator-p)
-        (when (catch 'found
-                (while (and (re-search-backward "\n" nil t)
-                            (let ((state (syntax-ppss)))
-                              (when (nth 4 state)
-                                (goto-char (nth 8 state))) ;; skip comments
-                              (skip-chars-backward " \t")
-                              (if (bolp)
-                                  t
-                                (throw 'found t))))))
-          (backward-char)
-          (when (js2-looking-at-operator-p)
-            (backward-char)
-            (not (looking-at "\\*\\|\\+\\+\\|--\\|/[/*]")))))))
+    (if (js2-looking-at-operator-p)
+        (or (not (memq (char-after) '(?- ?+)))
+            (progn
+              (forward-comment (- (point)))
+              (not (memq (char-before) '(?, ?\[ ?\()))))
+      (forward-comment (- (point)))
+      (or (bobp) (backward-char))
+      (when (js2-looking-at-operator-p)
+        (backward-char)
+        (not (looking-at "\\*\\|\\+\\+\\|--\\|/[/*]"))))))
 
 (defun js2-end-of-do-while-loop-p ()
   "Return non-nil if word after point is `while' of a do-while
@@ -431,7 +441,7 @@ indentation is aligned to that column."
         (goto-char bracket)
         (cond
          ((looking-at "[({[][ \t]*\\(/[/*]\\|$\\)")
-          (when (save-excursion (skip-chars-backward " \t)")
+          (when (save-excursion (skip-chars-backward " \t\n)")
                                 (looking-at ")"))
             (backward-list))
           (back-to-indentation)
@@ -484,6 +494,215 @@ indentation is aligned to that column."
       (when (cl-plusp offset)
         (forward-char offset)))))
 
+;;; JSX Indentation
+
+;; The following JSX indentation code is copied basically verbatim from js.el 
at
+;; 958da7f, except that the prefixes on the functions/variables are changed.
+
+(defsubst js2--jsx-find-before-tag ()
+  "Find where JSX starts.
+
+Assume JSX appears in the following instances:
+- Inside parentheses, when returned or as the first argument
+  to a function, and after a newline
+- When assigned to variables or object properties, but only
+  on a single line
+- As the N+1th argument to a function
+
+This is an optimized version of (re-search-backward \"[(,]\n\"
+nil t), except set point to the end of the match.  This logic
+executes up to the number of lines in the file, so it should be
+really fast to reduce that impact."
+  (let (pos)
+    (while (and (> (point) (point-min))
+                (not (progn
+                       (end-of-line 0)
+                       (when (or (eq (char-before) 40)   ; (
+                                 (eq (char-before) 44))  ; ,
+                         (setq pos (1- (point))))))))
+    pos))
+
+(defconst js2--jsx-end-tag-re
+  (concat "</" sgml-name-re ">\\|/>")
+  "Find the end of a JSX element.")
+
+(defconst js2--jsx-after-tag-re "[),]"
+  "Find where JSX ends.
+This complements the assumption of where JSX appears from
+`js--jsx-before-tag-re', which see.")
+
+(defun js2--jsx-indented-element-p ()
+  "Determine if/how the current line should be indented as JSX.
+
+Return `first' for the first JSXElement on its own line.
+Return `nth' for subsequent lines of the first JSXElement.
+Return `expression' for an embedded JS expression.
+Return `after' for anything after the last JSXElement.
+Return nil for non-JSX lines.
+
+Currently, JSX indentation supports the following styles:
+
+- Single-line elements (indented like normal JS):
+
+  var element = <div></div>;
+
+- Multi-line elements (enclosed in parentheses):
+
+  function () {
+    return (
+      <div>
+        <div></div>
+      </div>
+    );
+ }
+
+- Function arguments:
+
+  React.render(
+    <div></div>,
+    document.querySelector('.root')
+  );"
+  (let ((current-pos (point))
+        (current-line (line-number-at-pos))
+        last-pos
+        before-tag-pos before-tag-line
+        tag-start-pos tag-start-line
+        tag-end-pos tag-end-line
+        after-tag-line
+        parens paren type)
+    (save-excursion
+      (and
+       ;; Determine if we're inside a jsx element
+       (progn
+         (end-of-line)
+         (while (and (not tag-start-pos)
+                     (setq last-pos (js2--jsx-find-before-tag)))
+           (while (forward-comment 1))
+           (when (= (char-after) 60) ; <
+             (setq before-tag-pos last-pos
+                   tag-start-pos (point)))
+           (goto-char last-pos))
+         tag-start-pos)
+       (progn
+         (setq before-tag-line (line-number-at-pos before-tag-pos)
+               tag-start-line (line-number-at-pos tag-start-pos))
+         (and
+          ;; A "before" line which also starts an element begins with js, so
+          ;; indent it like js
+          (> current-line before-tag-line)
+          ;; Only indent the jsx lines like jsx
+          (>= current-line tag-start-line)))
+       (cond
+        ;; Analyze bounds if there are any
+        ((progn
+           (while (and (not tag-end-pos)
+                       (setq last-pos (re-search-forward js2--jsx-end-tag-re 
nil t)))
+             (while (forward-comment 1))
+             (when (looking-at js2--jsx-after-tag-re)
+               (setq tag-end-pos last-pos)))
+           tag-end-pos)
+         (setq tag-end-line (line-number-at-pos tag-end-pos)
+               after-tag-line (line-number-at-pos after-tag-line))
+         (or (and
+              ;; Ensure we're actually within the bounds of the jsx
+              (<= current-line tag-end-line)
+              ;; An "after" line which does not end an element begins with
+              ;; js, so indent it like js
+              (<= current-line after-tag-line))
+             (and
+              ;; Handle another case where there could be e.g. comments after
+              ;; the element
+              (> current-line tag-end-line)
+              (< current-line after-tag-line)
+              (setq type 'after))))
+        ;; They may not be any bounds (yet)
+        (t))
+       ;; Check if we're inside an embedded multi-line js expression
+       (cond
+        ((not type)
+         (goto-char current-pos)
+         (end-of-line)
+         (setq parens (nth 9 (syntax-ppss)))
+         (while (and parens (not type))
+           (setq paren (car parens))
+           (cond
+            ((and (>= paren tag-start-pos)
+                  ;; Curly bracket indicates the start of an embedded 
expression
+                  (= (char-after paren) 123) ; {
+                  ;; The first line of the expression is indented like sgml
+                  (> current-line (line-number-at-pos paren))
+                  ;; Check if within a closing curly bracket (if any)
+                  ;; (exclusive, as the closing bracket is indented like sgml)
+                  (cond
+                   ((progn
+                      (goto-char paren)
+                      (ignore-errors (let (forward-sexp-function)
+                                       (forward-sexp))))
+                    (< current-line (line-number-at-pos)))
+                   (t)))
+             ;; Indicate this guy will be indented specially
+             (setq type 'expression))
+            (t (setq parens (cdr parens)))))
+         t)
+        (t))
+       (cond
+        (type)
+        ;; Indent the first jsx thing like js so we can indent future jsx 
things
+        ;; like sgml relative to the first thing
+        ((= current-line tag-start-line) 'first)
+        ('nth))))))
+
+(defmacro js2--as-sgml (&rest body)
+  "Execute BODY as if in sgml-mode."
+  `(with-syntax-table sgml-mode-syntax-table
+     (let (forward-sexp-function
+           parse-sexp-lookup-properties)
+       ,@body)))
+
+(defun js2--expression-in-sgml-indent-line ()
+  "Indent the current line as JavaScript or SGML (whichever is farther)."
+  (let* (indent-col
+         (savep (point))
+         ;; Don't whine about errors/warnings when we're indenting.
+         ;; This has to be set before calling parse-partial-sexp below.
+         (inhibit-point-motion-hooks t)
+         (parse-status (save-excursion
+                         (syntax-ppss (point-at-bol)))))
+    ;; Don't touch multiline strings.
+    (unless (nth 3 parse-status)
+      (setq indent-col (save-excursion
+                         (back-to-indentation)
+                         (if (>= (point) savep) (setq savep nil))
+                         (js2--as-sgml (sgml-calculate-indent))))
+      (if (null indent-col)
+          'noindent
+        ;; Use whichever indentation column is greater, such that the sgml
+        ;; column is effectively a minimum
+        (setq indent-col (max (js2-proper-indentation parse-status)
+                              (+ indent-col js2-basic-offset)))
+        (if savep
+            (save-excursion (indent-line-to indent-col))
+          (indent-line-to indent-col))))))
+
+(defun js2-jsx-indent-line ()
+  "Indent the current line as JSX (with SGML offsets).
+i.e., customize JSX element indentation with `sgml-basic-offset'
+et al."
+  (interactive)
+  (let ((indentation-type (js2--jsx-indented-element-p)))
+    (cond
+     ((eq indentation-type 'expression)
+      (js2--expression-in-sgml-indent-line))
+     ((or (eq indentation-type 'first)
+          (eq indentation-type 'after))
+      ;; Don't treat this first thing as a continued expression (often a "<" or
+      ;; ">" causes this misinterpretation)
+      (cl-letf (((symbol-function #'js2-continued-expression-p) 'ignore))
+        (js2-indent-line)))
+     ((eq indentation-type 'nth)
+      (js2--as-sgml (sgml-indent-line)))
+     (t (js2-indent-line)))))
+
 (provide 'js2-old-indent)
 
 ;;; js2-old-indent.el ends here
diff --git a/packages/js2-mode/tests/externs.el 
b/packages/js2-mode/tests/externs.el
index 40957f6..75b93cd 100644
--- a/packages/js2-mode/tests/externs.el
+++ b/packages/js2-mode/tests/externs.el
@@ -1,6 +1,6 @@
 ;;; tests/externs.el --- Some tests for js2-mode.
 
-;; Copyright (C) 2009, 2011-2013  Free Software Foundation, Inc.
+;; Copyright (C) 2009, 2011-2014, 2016  Free Software Foundation, Inc.
 
 ;; This file is part of GNU Emacs.
 
@@ -42,6 +42,13 @@
     (should (equal (js2-get-jslint-globals)
                    '("quux" "tee" "$")))))
 
+(ert-deftest js2-finds-jslint-globals-with-space ()
+  (with-temp-buffer
+    (insert "/* global foo, bar:false, baz:true")
+    (js2-mode)
+    (should (equal (js2-get-jslint-globals)
+                   '("foo" "bar" "baz")))))
+
 ;;;TODO
 ;; ensure that any symbols bound with the import syntax are added to the 
extern list
 ;; ensure that any symbols bound with the export syntax exist in the file scope
diff --git a/packages/js2-mode/tests/indent.el 
b/packages/js2-mode/tests/indent.el
index 27b6c5a..0fabe95 100644
--- a/packages/js2-mode/tests/indent.el
+++ b/packages/js2-mode/tests/indent.el
@@ -1,6 +1,6 @@
 ;;; tests/indent.el --- Some tests for js2-mode.
 
-;; Copyright (C) 2009, 2011-2013  Free Software Foundation, Inc.
+;; Copyright (C) 2009, 2011-2016  Free Software Foundation, Inc.
 
 ;; This file is part of GNU Emacs.
 
@@ -31,14 +31,16 @@
        (if keep-indent
            s
          (replace-regexp-in-string "^ *" "" s)))
-      (js2-mode)
+      (js2-jsx-mode)
+      (js2-reparse) ; solely for js2-jsx-self-closing, for some reason
       (indent-region (point-min) (point-max))
       (should (string= s (buffer-substring-no-properties
                           (point-min) (point)))))))
 
 (cl-defmacro js2-deftest-indent (name content &key bind keep-indent)
   `(ert-deftest ,(intern (format "js2-%s" name)) ()
-     (let ,(append '((js2-basic-offset 2)
+     (let ,(append '(indent-tabs-mode
+                     (js2-basic-offset 2)
                      (js2-pretty-multiline-declarations t)
                      (inhibit-point-motion-hooks t))
                    bind)
@@ -140,3 +142,146 @@
   |  ,
   |  bar = 8;"
   :bind ((js2-pretty-multiline-declarations 'dynamic)))
+
+(js2-deftest-indent indent-generator-method
+  "class A {
+  |  * x() {
+  |    return 1
+  |      * a(2);
+  |  }
+  |}")
+
+(js2-deftest-indent indent-generator-computed-method
+  "class A {
+  |  *[Symbol.iterator]() {
+  |    yield 'Foo';
+  |    yield 'Bar';
+  |  }
+  |}")
+
+(js2-deftest-indent case-inside-switch
+  "switch(true) {
+  |case 'true':
+  |  return 1;
+  |}")
+
+(js2-deftest-indent case-inside-switch-with-extra-indent
+  "switch(true) {
+  |  case 'true':
+  |    return 1;
+  |}"
+  :bind ((js2-indent-switch-body t)))
+
+(js2-deftest-indent case-inside-switch-with-extra-indent-curly-after-newline
+  "switch(true)
+  |{
+  |  case 'true':
+  |    return 1;
+  |}"
+  :bind ((js2-indent-switch-body t)))
+
+(js2-deftest-indent continued-expression-vs-unary-minus
+  "var arr = [
+  |  -1, 2,
+  |  -3, 4 +
+  |    -5
+  |];")
+
+(js2-deftest-indent spread-inside-array
+  "var z = [
+  |  ...iterableObj,
+  |  4,
+  |  5
+  |]")
+
+(js2-deftest-indent jsx-one-line
+  "var foo = <div></div>;")
+
+(js2-deftest-indent jsx-children-parentheses
+  "return (
+  |  <div>
+  |  </div>
+  |  <div>
+  |    <div></div>
+  |    <div>
+  |      <div></div>
+  |    </div>
+  |  </div>
+  |);")
+
+(js2-deftest-indent jsx-children-unclosed
+  "return (
+  |  <div>
+  |    <div>")
+
+(js2-deftest-indent jsx-argument
+  "React.render(
+  |  <div>
+  |    <div></div>
+  |  </div>,
+  |  {
+  |    a: 1
+  |  },
+  |  <div>
+  |    <div></div>
+  |  </div>
+  |);")
+
+(js2-deftest-indent jsx-leading-comment
+  "return (
+  |  // Sneaky!
+  |  <div></div>
+  |);")
+
+(js2-deftest-indent jsx-trailing-comment
+  "return (
+  |  <div></div>
+  |  // Sneaky!
+  |);")
+
+(js2-deftest-indent jsx-self-closing
+  ;; This ensures we know the bounds of a self-closing element
+  "React.render(
+  |  <input
+  |     />,
+  |  {
+  |    a: 1
+  |  }
+  |);"
+  :bind ((sgml-attribute-offset 1))) ; Emacs 24.5 -> 25 compat
+
+(js2-deftest-indent jsx-embedded-js-content
+  "return (
+  |  <div>
+  |    {array.map(function () {
+  |      return {
+  |        a: 1
+  |      };
+  |    })}
+  |  </div>
+  |);")
+
+(js2-deftest-indent jsx-embedded-js-unclosed
+  "return (
+  |  <div>
+  |    {array.map(function () {
+  |      return {
+  |        a: 1")
+
+(js2-deftest-indent jsx-embedded-js-attribute
+  "return (
+  |  <div attribute={array.map(function () {
+  |         return {
+  |           a: 1
+  |         };
+  |
+  |         return {
+  |           a: 1
+  |         };
+  |
+  |         return {
+  |           a: 1
+  |         };
+  |       })}>
+  |  </div>
+  |);")
diff --git a/packages/js2-mode/tests/json-path.el 
b/packages/js2-mode/tests/json-path.el
new file mode 100644
index 0000000..70aecef
--- /dev/null
+++ b/packages/js2-mode/tests/json-path.el
@@ -0,0 +1,64 @@
+;;; tests/json-path.el --- Test of using js2-mode AST to print JSON path.
+
+;; Copyright (C) 2015  Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Code:
+
+(require 'ert)
+(require 'js2-mode)
+
+(ert-deftest js2-json-path-with-actual-array-index ()
+  (with-temp-buffer
+    (insert "var a = { hello: [1, 2, [1,3,3,4, { world: { hell: { yes: [1,2, 
'test'] } } }]] };")
+    (js2-mode)
+    (goto-char 0)
+    (search-forward "test")
+    (should (string= (js2-print-json-path) "hello[2][4].world.hell.yes[2]"))))
+
+(ert-deftest js2-json-path-pure-arrays ()
+  (with-temp-buffer
+    (insert "var a = [5, 1, 4, [ 4, [1, 2, [1, 3.9, 4, [1, 2, 'test',3]]]], 9, 
9];")
+    (js2-mode)
+    (goto-char 0)
+    (search-forward "test")
+    (should (string= (js2-print-json-path) "[3][1][2][3][2]"))))
+
+(ert-deftest js2-json-path-key-is-numeric ()
+  (with-temp-buffer
+    (insert "var b = {hello: {3 : {world: {2: 'test'}}}};")
+    (js2-mode)
+    (goto-char 0)
+    (search-forward "test")
+    (should (string= (js2-print-json-path) "hello.3.world.2"))))
+
+(ert-deftest js2-json-path-not-found ()
+  (with-temp-buffer
+    (insert "console.log('test');")
+    (js2-mode)
+    (goto-char 0)
+    (search-forward "test")
+    (should (eq (js2-print-json-path) nil))))
+
+(ert-deftest js2-json-path-with-array-index-hardcoded ()
+  (with-temp-buffer
+    (insert "var a = { hello: [1, 2, [1,3,3,4, { world: { hell: { yes: [1,2, 
'test'] } } }]] };")
+    (js2-mode)
+    (goto-char 0)
+    (search-forward "test")
+    (should (string= (js2-print-json-path 1) "hello[1][1].world.hell.yes[1]"))
+    (should (string= (js2-print-json-path 0) 
"hello[0][0].world.hell.yes[0]"))))
diff --git a/packages/js2-mode/tests/parser.el 
b/packages/js2-mode/tests/parser.el
index dc8c001..b4aa683 100644
--- a/packages/js2-mode/tests/parser.el
+++ b/packages/js2-mode/tests/parser.el
@@ -1,6 +1,6 @@
 ;;; tests/parser.el --- Some tests for js2-mode.
 
-;; Copyright (C) 2009, 2011-2013  Free Software Foundation, Inc.
+;; Copyright (C) 2009, 2011-2016  Free Software Foundation, Inc.
 
 ;; This file is part of GNU Emacs.
 
@@ -35,14 +35,18 @@
              ,@body)
          (fundamental-mode)))))
 
+(defun js2-mode--and-parse ()
+  (js2-mode)
+  (js2-reparse))
+
 (defun js2-test-string-to-ast (s)
   (insert s)
-  (js2-mode)
+  (js2-mode--and-parse)
   (should (null js2-mode-buffer-dirty-p))
   js2-mode-ast)
 
 (cl-defun js2-test-parse-string (code-string &key syntax-error errors-count
-                                             reference)
+                                             reference warnings-count)
   (ert-with-test-buffer (:name 'origin)
     (let ((ast (js2-test-string-to-ast code-string)))
       (if syntax-error
@@ -57,10 +61,13 @@
           (skip-chars-backward " \t\n")
           (should (string= (or reference code-string)
                            (buffer-substring-no-properties
-                            (point-min) (point)))))))))
+                            (point-min) (point)))))
+        (when warnings-count
+          (should (= warnings-count
+                     (length (js2-ast-root-warnings ast)))))))))
 
 (cl-defmacro js2-deftest-parse (name code-string &key bind syntax-error 
errors-count
-                                     reference)
+                                     reference warnings-count)
   "Parse CODE-STRING.  If SYNTAX-ERROR is nil, print syntax tree
 with `js2-print-tree' and assert the result to be equal to
 REFERENCE, if present, or the original string.  If SYNTAX-ERROR
@@ -73,6 +80,7 @@ the test."
        (js2-test-parse-string ,code-string
                               :syntax-error ,syntax-error
                               :errors-count ,errors-count
+                              :warnings-count ,warnings-count
                               :reference ,reference))))
 
 ;;; Basics
@@ -126,6 +134,9 @@ the test."
 (js2-deftest-parse let-expression-statement
   "let (x = 42) x;")
 
+(js2-deftest-parse void
+  "void 0;")
+
 ;;; Callers of `js2-valid-prop-name-token'
 
 (js2-deftest-parse parse-property-access-when-not-keyword
@@ -158,6 +169,12 @@ the test."
 (js2-deftest-parse parse-for-of
   "for (var a of []) {\n}")
 
+(js2-deftest-parse of-can-be-name
+  "void of;")
+
+(js2-deftest-parse of-can-be-object-name
+  "of.z;")
+
 (js2-deftest-parse of-can-be-var-name
   "var of = 3;")
 
@@ -167,16 +184,49 @@ the test."
 ;;; Destructuring binding
 
 (js2-deftest-parse destruct-in-declaration
-  "var {a, b} = {a: 1, b: 2};")
+  "var {a, b} = {a: 1, b: 2};"
+  :warnings-count 0)
 
 (js2-deftest-parse destruct-in-arguments
-  "function f({a: aa, b: bb}) {\n}")
+  "function f({a: aa, b: bb}) {\n}"
+  :warnings-count 0)
 
 (js2-deftest-parse destruct-in-array-comp-loop
   "[a + b for ([a, b] in [[0, 1], [1, 2]])];")
 
 (js2-deftest-parse destruct-in-catch-clause
-  "try {\n} catch ({a, b}) {\n  a + b;\n}")
+  "try {\n} catch ({a, b}) {\n  a + b;\n}"
+  :warnings-count 0)
+
+(js2-deftest-parse destruct-with-initializer-in-object
+  "var {a, b = 2, c} = {};\nb;"
+  :warnings-count 0)
+
+(js2-deftest-parse destruct-with-initializer-in-array
+  "var [a, b = 2, c] = [];\nb;"
+  :warnings-count 0)
+
+(js2-deftest-parse destruct-non-name-target-is-error
+  "var {1=1} = {};" :syntax-error "1" :errors-count 1)
+
+(js2-deftest-parse destruct-with-initializer-in-function-params
+  "function f({a, b = 1, c}, [d, e = 1, f]) {\n}")
+
+(js2-deftest-parse destruct-with-default-in-function-params
+  "function f({x = 1, y = 2} = {}, [x, y] = [1, 2]) {\n}")
+
+(js2-deftest-parse destruct-name-conflict-is-error-in-object
+  "\"use strict\";\nvar {a=1,a=2} = {};" :syntax-error "a" :errors-count 1)
+
+(js2-deftest destruct-name-conflict-is-warning-in-array "\"use strict\";\nvar 
[a=1,a=2] = [];"
+  (js2-mode--and-parse)
+  (should (equal '("msg.var.redecl" "a")
+                 (caar js2-parsed-warnings))))
+
+(js2-deftest initializer-outside-destruct-is-error "({a=1});"
+  (js2-mode--and-parse)
+  (should (equal "msg.init.no.destruct"
+                 (car (caar js2-parsed-errors)))))
 
 ;;; Object literals
 
@@ -189,6 +239,11 @@ the test."
 (js2-deftest-parse object-literal-method
   "var x = {f(y) {  return y;\n}};")
 
+(js2-deftest object-literal-method-own-name-in-scope "({f(){f();}});"
+  (js2-mode--and-parse)
+  (should (equal '("msg.undeclared.variable" "f")
+                 (caar js2-parsed-warnings))))
+
 (js2-deftest-parse object-literal-getter-method
   "var x = {get f() {  return 42;\n}};")
 
@@ -205,7 +260,7 @@ the test."
   "var x = {get [foo + bar]() {  return 42;\n}};")
 
 (js2-deftest-parse object-literal-generator
-  "var x = {*foo() {  yield 42;\n}};")
+  "var x = {*foo() {  yield* 42;\n}};")
 
 (js2-deftest-parse object-literal-computed-generator-key
   "var x = {*[foo + bar]() {  yield 42;\n}};")
@@ -213,11 +268,11 @@ the test."
 ;;; Function definition
 
 (js2-deftest function-redeclaring-var "var gen = 3; function gen() {};"
-  (js2-mode)
+  (js2-mode--and-parse)
   (should (= (length (js2-ast-root-warnings js2-mode-ast)) 1)))
 
 (js2-deftest function-expression-var-same-name "var gen = function gen() {};"
-  (js2-mode)
+  (js2-mode--and-parse)
   (should (null (js2-ast-root-warnings js2-mode-ast))))
 
 ;;; Function parameters
@@ -290,14 +345,29 @@ the test."
   "'use strict';\nvar number = 0644;"
   :syntax-error "0644" :errors-count 1)
 
+(js2-deftest-parse function-strict-octal-allow-0o
+  "'use strict';\n0o644;" :reference "'use strict';\n420;")
+
 (js2-deftest-parse function-strict-duplicate-keys
   "'use strict';\nvar object = {a: 1, a: 2, 'a': 3, ['a']: 4, 1: 5, '1': 6, [1 
+ 1]: 7};"
   :syntax-error "a" :errors-count 4) ; "a" has 3 dupes, "1" has 1 dupe.
 
-;; errors... or lackthereof.
+(js2-deftest-parse function-strict-duplicate-getter
+  "'use strict';\nvar a = {get x() {}, get x() {}};"
+  :syntax-error "x" :errors-count 1)
+
+(js2-deftest-parse function-strict-duplicate-setter
+  "'use strict';\nvar a = {set x() {}, set x() {}};"
+  :syntax-error "x" :errors-count 1)
+
+;;; Lack of errors in strict mode
+
 (js2-deftest-parse function-strict-const-scope
   "'use strict';\nconst a;\nif (1) {\n  const a;\n}")
 
+(js2-deftest-parse function-strict-no-getter-setter-duplicate
+  "'use strict';\nvar a = {get x() {}, set x() {}};")
+
 ;;; Spread operator
 
 (js2-deftest-parse spread-in-array-literal
@@ -306,6 +376,26 @@ the test."
 (js2-deftest-parse spread-in-function-call
   "f(3, ...[t(2), t(3)], 42, ...[t(4)]);")
 
+(js2-deftest-parse rest-in-array-destructure
+  "let [x, y, z, ...w] = [1, ...a, ...b, c];")
+
+(js2-deftest-parse comma-after-rest-in-array
+  "let [...x,] = [1, 2, 3];"
+  :syntax-error "," :errors-count 1)
+
+(js2-deftest-parse elem-after-rest-in-array
+  "let [...x, y] = [1, 2, 3];"
+  :syntax-error "," :errors-count 2)
+
+(js2-deftest-parse array-destructure-expr-default
+  "let [[x] = [3]] = y;")
+
+(js2-deftest-parse spread-in-object-literal
+  "f({x, y, ...z});")
+
+(js2-deftest-parse rest-in-object-literal
+  "const {x, y, ...z} = f();")
+
 ;;; Arrow functions
 
 (js2-deftest-parse arrow-function-with-empty-args-and-no-curlies
@@ -344,7 +434,7 @@ the test."
 
 (js2-deftest no-label-node-inside-expr "x = y:"
   (let (js2-parse-interruptable-p)
-    (js2-mode))
+    (js2-mode--and-parse))
   (let ((assignment (js2-expr-stmt-node-expr (car (js2-scope-kids 
js2-mode-ast)))))
     (should (js2-name-node-p (js2-assign-node-right assignment)))))
 
@@ -387,6 +477,79 @@ the test."
 (js2-deftest-parse parse-generator-comp-with-yield-inside-function-is-ok
   "(for (x of []) function*() {  yield x;\n});")
 
+;;; Async
+
+(js2-deftest-parse async-function-statement
+  "async function foo() {\n}")
+
+(js2-deftest-parse async-function-statement-inside-block
+  "if (true) {\n  async function foo() {\n  }\n}")
+
+(js2-deftest-parse async-function-expression-statements-are-verboten
+  "async function() {}" :syntax-error "(")
+
+(js2-deftest-parse async-named-function-expression
+  "a = async function b() {};")
+
+(js2-deftest-parse async-arrow-function-expression
+  "a = async (b) => {  b;\n};")
+
+(js2-deftest-parse async-method-in-object-literal
+  "({async f() {}});")
+
+(js2-deftest-parse async-method-in-class-body
+  "class C {\n  async foo() {}\n}")
+
+(js2-deftest-parse static-async-method-in-class-body
+  "class C {\n  static async foo() {}\n}")
+
+(js2-deftest-parse async-method-allow-await
+  "({async f() {  await x;\n}});")
+
+;;; Await
+
+(js2-deftest-parse await-is-ok "async function foo() {\n  await bar();\n}")
+
+(js2-deftest-parse await-inside-assignment-is-ok
+                   "async function foo() {\n  var result = await bar();\n}")
+
+(js2-deftest-parse await-inside-array-is-ok
+                   "async function foo() {\n  var results = [await bar(), 
await baz()];\n}")
+
+(js2-deftest-parse await-inside-non-async-function-is-not-ok
+                   "function foo() {\n  await bar();\n}"
+                   :syntax-error "await")
+
+(js2-deftest-parse await-inside-non-async-arrow-function-is-not-ok
+                   "a = () => {  await bar();\n}"
+                   :syntax-error "await")
+
+;;; 'async' and 'await' are contextual keywords
+
+(js2-deftest-parse async-can-be-name
+  "void async;")
+
+(js2-deftest-parse async-can-be-object-name
+  "async.z;")
+
+(js2-deftest-parse async-can-be-var-name
+  "var async = 3;")
+
+(js2-deftest-parse async-can-be-function-name
+  "function async() {\n}")
+
+(js2-deftest-parse await-can-be-name
+  "void await;")
+
+(js2-deftest-parse await-can-be-object-name
+  "await.z;")
+
+(js2-deftest-parse await-can-be-var-name
+  "var await = 3;")
+
+(js2-deftest-parse await-can-be-function-name
+  "function await() {\n}")
+
 ;;; Numbers
 
 (js2-deftest-parse decimal-starting-with-zero "081;" :reference "81;")
@@ -639,7 +802,7 @@ the test."
     (should export-node)
     (should (js2-var-decl-node-p (js2-export-node-declaration export-node)))))
 
-(js2-deftest export-class-declaration "export class Foo {};"
+(js2-deftest export-class-declaration "export class Foo {}"
   (js2-init-scanner)
   (js2-push-scope (make-js2-scope :pos 0))
   (should (js2-match-token js2-EXPORT))
@@ -647,7 +810,7 @@ the test."
     (should export-node)
     (should (js2-class-node-p (js2-export-node-declaration export-node)))))
 
-(js2-deftest export-function-declaration "export default function doStuff() 
{};"
+(js2-deftest export-function-declaration "export default function doStuff() {}"
   (js2-init-scanner)
   (js2-push-scope (make-js2-scope :pos 0))
   (should (js2-match-token js2-EXPORT))
@@ -655,7 +818,7 @@ the test."
     (should export-node)
     (should (js2-export-node-default export-node))))
 
-(js2-deftest export-generator-declaration "export default function* one() {};"
+(js2-deftest export-generator-declaration "export default function* one() {}"
   (js2-init-scanner)
   (js2-push-scope (make-js2-scope :pos 0))
   (should (js2-match-token js2-EXPORT))
@@ -672,24 +835,43 @@ the test."
     (should (js2-export-node-default export-node))))
 
 (js2-deftest export-function-no-semicolon "export default function foo() {}"
-  (js2-mode)
+  (js2-mode--and-parse)
   (should (equal nil js2-parsed-warnings)))
 (js2-deftest export-default-function-no-semicolon "export function foo() {}"
-  (js2-mode)
+  (js2-mode--and-parse)
   (should (equal nil js2-parsed-warnings)))
 (js2-deftest export-anything-else-does-require-a-semicolon "export var obj = 
{}"
-  (js2-mode)
+  (js2-mode--and-parse)
   (should (not (equal nil js2-parsed-warnings))))
 
+(js2-deftest export-default-async-function-no-semicolon "export default async 
function foo() {}"
+  (js2-mode--and-parse)
+  (should (equal nil js2-parsed-warnings)))
+(js2-deftest export-async-function-no-semicolon "export async function foo() 
{}"
+  (js2-mode--and-parse)
+  (should (equal nil js2-parsed-warnings)))
+
 (js2-deftest-parse parse-export-rexport "export * from 'other/lib';")
 (js2-deftest-parse parse-export-export-named-list "export {foo, bar as bang};")
 (js2-deftest-parse parse-re-export-named-list "export {foo, bar as bang} from 
'other/lib';")
 (js2-deftest-parse parse-export-const-declaration "export const PI = Math.PI;")
 (js2-deftest-parse parse-export-let-declaration "export let foo = [1];")
-(js2-deftest-parse parse-export-function-declaration "export default function 
doStuff() {};")
-(js2-deftest-parse parse-export-generator-declaration "export default 
function* one() {};")
+(js2-deftest-parse parse-export-function-declaration "export default function 
doStuff() {\n}")
+(js2-deftest-parse parse-export-generator-declaration "export default 
function* one() {\n}")
 (js2-deftest-parse parse-export-assignment-expression "export default a = b;")
 
+(js2-deftest-parse parse-export-function-declaration-no-semi
+  "export function f() {\n}")
+
+(js2-deftest-parse parse-export-class-declaration-no-semi
+  "export class C {\n}")
+
+(js2-deftest-parse parse-export-async-function-allow-await
+  "export async function f() {\n  await f();\n}")
+
+(js2-deftest-parse parse-export-default-async-function-allow-await
+  "export default async function f() {\n  await f();\n}")
+
 ;;; Strings
 
 (js2-deftest-parse string-literal
@@ -749,10 +931,13 @@ the test."
 (js2-deftest-parse parse-class-keywordlike-method
   "class C {\n  delete() {}\n  if() {}\n}")
 
+(js2-deftest-parse parse-harmony-class-allow-semicolon-element
+  "class Foo {;}" :reference "class Foo {\n}")
+
 ;;; Scopes
 
 (js2-deftest ast-symbol-table-includes-fn-node "function foo() {}"
-  (js2-mode)
+  (js2-mode--and-parse)
   (let ((entry (js2-scope-get-symbol js2-mode-ast 'foo)))
     (should (= (js2-symbol-decl-type entry) js2-FUNCTION))
     (should (equal (js2-symbol-name entry) "foo"))
@@ -762,7 +947,7 @@ the test."
   function bar() {}
   var x;
 }"
-  (js2-mode)
+  (js2-mode--and-parse)
   (let* ((scope (js2-node-at-point (point-min)))
          (fn-entry (js2-scope-get-symbol scope 'bar))
          (var-entry (js2-scope-get-symbol scope 'x)))
@@ -780,36 +965,26 @@ the test."
     (should (funcall predicate (js2-get-defining-scope scope variable)))))
 
 (js2-deftest for-node-is-declaration-scope "for (let i = 0; i; ++i) {};"
-  (js2-mode)
+  (js2-mode--and-parse)
   (js2-test-scope-of-nth-variable-satisifies-predicate "i" 0 #'js2-for-node-p))
 
-(js2-deftest const-scope-sloppy-script "{const a;} a;"
-  (js2-mode)
-  (js2-test-scope-of-nth-variable-satisifies-predicate "a" 0 
#'js2-script-node-p)
-  (js2-test-scope-of-nth-variable-satisifies-predicate "a" 1 
#'js2-script-node-p))
-
-(js2-deftest const-scope-strict-script "'use strict'; { const a; } a;"
-  (js2-mode)
+(js2-deftest const-scope-inside-script "{ const a; } a;"
+  (js2-mode--and-parse)
   (js2-test-scope-of-nth-variable-satisifies-predicate "a" 0 
#'js2-block-node-p)
   (js2-test-scope-of-nth-variable-satisifies-predicate "a" 1 #'null))
 
-(js2-deftest const-scope-sloppy-function "function f() { { const a; } a; }"
-  (js2-mode)
-  (js2-test-scope-of-nth-variable-satisifies-predicate "a" 0 
#'js2-function-node-p)
-  (js2-test-scope-of-nth-variable-satisifies-predicate "a" 1 
#'js2-function-node-p))
-
-(js2-deftest const-scope-strict-function "function f() { 'use strict'; { const 
a; } a; }"
-  (js2-mode)
+(js2-deftest const-scope-inside-function "function f() { { const a; } a; }"
+  (js2-mode--and-parse)
   (js2-test-scope-of-nth-variable-satisifies-predicate "a" 0 
#'js2-block-node-p)
   (js2-test-scope-of-nth-variable-satisifies-predicate "a" 1 #'null))
 
 (js2-deftest array-comp-is-result-scope "[x * 2 for (x in y)];"
-  (js2-mode)
+  (js2-mode--and-parse)
   (js2-test-scope-of-nth-variable-satisifies-predicate "x" 0 
#'js2-comp-loop-node-p))
 
 (js2-deftest array-comp-has-parent-scope
              "var a,b=[for (i of [[1,2]]) for (j of i) j * a];"
-  (js2-mode)
+  (js2-mode--and-parse)
   (search-forward "for")
   (forward-char -3)
   (let ((node (js2-node-at-point)))
@@ -879,23 +1054,23 @@ the test."
 ;;; Error handling
 
 (js2-deftest for-node-with-error-len "for "
-  (js2-mode)
+  (js2-mode--and-parse)
   (let ((node (js2-node-at-point (point-min))))
     (should (= (js2-node-len (js2-node-parent node)) 4))))
 
 (js2-deftest function-without-parens-error "function b {}"
   ;; Should finish the parse.
-  (js2-mode))
+  (js2-mode--and-parse))
 
 ;;; Comments
 
 (js2-deftest comment-node-length "//"
-  (js2-mode)
+  (js2-mode--and-parse)
   (let ((node (js2-node-at-point (point-min))))
     (should (= (js2-node-len node) 2))))
 
 (js2-deftest comment-node-length-newline "//\n"
-  (js2-mode)
+  (js2-mode--and-parse)
   (let ((node (js2-node-at-point (point-min))))
     (should (= (js2-node-len node) 3))))
 
@@ -934,7 +1109,7 @@ the test."
          (insert ,buffer-contents))
        (unwind-protect
            (progn
-             (js2-mode)
+             (js2-mode--and-parse)
              (should (equal ,summary (js2--variables-summary
                                       (js2--classify-variables)))))
          (fundamental-mode)))))



reply via email to

[Prev in Thread] Current Thread [Next in Thread]