]> code.delx.au - gnu-emacs-elpa/blobdiff - js2-mode.el
Remove js2-current-indent
[gnu-emacs-elpa] / js2-mode.el
index 12801bb17316afbf8437df6c2e5d63a506ee50cf..348b0d1e853e0f99c0b6cc434ff8f5d090fba118 100644 (file)
@@ -736,6 +736,7 @@ List of chars built up while scanning various tokens.")
   (end -1)
   (string "")
   number
+  number-base
   regexp-flags
   comment-type
   follows-eol-p)
@@ -1671,6 +1672,9 @@ the correct number of ARGS must be provided."
 (js2-msg "msg.no.paren.after.with"
          "missing ) after with-statement object")
 
+(js2-msg "msg.no.with.strict"
+         "with statements not allowed in strict mode")
+
 (js2-msg "msg.no.paren.after.let"
          "missing ( after let")
 
@@ -1786,6 +1790,12 @@ the correct number of ARGS must be provided."
 (js2-msg "msg.destruct.assign.no.init"
          "Missing = in destructuring declaration")
 
+(js2-msg "msg.no.octal.strict"
+         "Octal numbers prohibited in strict mode.")
+
+(js2-msg "msg.dup.obj.lit.prop.strict"
+         "Property '%s' already defined in this object literal.")
+
 (js2-msg "msg.dup.param.strict"
          "Parameter '%s' already declared in this function.")
 
@@ -2455,12 +2465,11 @@ NAME can be a Lisp symbol or string.  SYMBOL is a `js2-symbol'."
                                                      len
                                                      buffer)))
   "The root node of a js2 AST."
-  buffer           ; the source buffer from which the code was parsed
-  comments         ; a Lisp list of comments, ordered by start position
-  errors           ; a Lisp list of errors found during parsing
-  warnings         ; a Lisp list of warnings found during parsing
-  node-count       ; number of nodes in the tree, including the root
-  in-strict-mode)  ; t if the script is running under strict mode
+  buffer         ; the source buffer from which the code was parsed
+  comments       ; a Lisp list of comments, ordered by start position
+  errors         ; a Lisp list of errors found during parsing
+  warnings       ; a Lisp list of warnings found during parsing
+  node-count)    ; number of nodes in the tree, including the root
 
 (put 'cl-struct-js2-ast-root 'js2-visitor 'js2-visit-ast-root)
 (put 'cl-struct-js2-ast-root 'js2-printer 'js2-print-script)
@@ -3337,8 +3346,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
-  member-expr      ; nonstandard Ecma extension from Rhino
-  in-strict-mode)  ; t if the function is running under strict mode
+  member-expr)     ; nonstandard Ecma extension from Rhino
 
 (put 'cl-struct-js2-function-node 'js2-visitor 'js2-visit-function-node)
 (put 'cl-struct-js2-function-node 'js2-printer 'js2-print-function-node)
@@ -3765,10 +3773,13 @@ Returns 0 if NODE is nil or its identifier field is nil."
                                                                 (js2-current-token-beg)))
                                                         (value (js2-current-token-string))
                                                         (num-value (js2-token-number
-                                                                    (js2-current-token))))))
+                                                                    (js2-current-token)))
+                                                        (num-base (js2-token-number-base
+                                                                   (js2-current-token))))))
   "AST node for a number literal."
   value      ; the original string, e.g. "6.02e23"
-  num-value) ; the parsed number value
+  num-value  ; the parsed number value
+  num-base)  ; the number's base
 
 (put 'cl-struct-js2-number-node 'js2-visitor 'js2-visit-none)
 (put 'cl-struct-js2-number-node 'js2-printer 'js2-print-number-node)
@@ -3965,10 +3976,11 @@ optional `js2-expr-node'"
                                                              len left
                                                              right op-pos)))
   "AST node for an object literal prop:value entry.
-The `left' field is the property:  a name node, string node or number node.
-The `right' field is a `js2-node' representing the initializer value.
-If the property is abbreviated, the node's `SHORTHAND' property is non-nil
-and both fields have the same value.")
+The `left' field is the property: a name node, string node,
+number node or expression node.  The `right' field is a
+`js2-node' representing the initializer value.  If the property
+is abbreviated, the node's `SHORTHAND' property is non-nil and
+both fields have the same value.")
 
 (put 'cl-struct-js2-object-prop-node 'js2-visitor 'js2-visit-infix-node)
 (put 'cl-struct-js2-object-prop-node 'js2-printer 'js2-print-object-prop-node)
@@ -6112,8 +6124,8 @@ its relevant fields and puts it into `js2-ti-tokens'."
                         while (js2-digit-p c))))
            (js2-unget-char)
            (let ((str (js2-set-string-from-buffer token)))
-             (setf (js2-token-number token)
-                   (js2-string-to-number str base)))
+             (setf (js2-token-number token) (js2-string-to-number str base)
+                   (js2-token-number-base token) base))
            (throw 'return js2-NUMBER))
          ;; is it a string?
          (when (or (memq c '(?\" ?\'))
@@ -7945,7 +7957,7 @@ Scanner should be initialized."
         (end js2-ts-cursor)  ; in case file is empty
         root n tt
         (in-directive-prologue t)
-        (saved-strict-mode js2-in-use-strict-directive)
+        (js2-in-use-strict-directive js2-in-use-strict-directive)
         directive)
     ;; initialize buffer-local parsing vars
     (setf root (make-js2-ast-root :buffer (buffer-name) :pos pos)
@@ -7970,12 +7982,10 @@ Scanner should be initialized."
            ((null directive)
             (setq in-directive-prologue nil))
            ((string= directive "use strict")
-            (setq js2-in-use-strict-directive t)
-            (setf (js2-ast-root-in-strict-mode root) t)))))
+            (setq js2-in-use-strict-directive t)))))
       ;; add function or statement to script
       (setq end (js2-node-end n))
       (js2-block-node-push root n))
-    (setq js2-in-use-strict-directive saved-strict-mode)
     ;; add comments to root in lexical order
     (when js2-scanned-comments
       ;; if we find a comment beyond end of normal kids, use its end
@@ -8012,11 +8022,8 @@ Scanner should be initialized."
         tt
         end
         not-in-directive-prologue
-        (saved-strict-mode js2-in-use-strict-directive)
         node
         directive)
-    ;; Inherit strict mode.
-    (setf (js2-function-node-in-strict-mode fn-node) js2-in-use-strict-directive)
     (cl-incf js2-nesting-of-function)
     (unwind-protect
         (while (not (or (= (setq tt (js2-peek-token)) js2-ERROR)
@@ -8033,13 +8040,15 @@ Scanner should be initialized."
                   ((null directive)
                    (setq not-in-directive-prologue t))
                   ((string= directive "use strict")
-                   (setq js2-in-use-strict-directive t)
-                   (setf (js2-function-node-in-strict-mode fn-node) t)))
+                   ;; Back up and reparse the function, because new rules apply
+                   ;; to the function name and parameters.
+                   (when (not js2-in-use-strict-directive)
+                     (setq js2-in-use-strict-directive t)
+                     (throw 'reparse t))))
                  node)
              (js2-get-token)
              (js2-parse-function-stmt))))
-      (cl-decf js2-nesting-of-function)
-      (setq js2-in-use-strict-directive saved-strict-mode))
+      (cl-decf js2-nesting-of-function))
     (setq end (js2-current-token-end))  ; assume no curly and leave at current token
     (if (js2-must-match js2-RC "msg.no.brace.after.body" pos)
         (setq end (js2-current-token-end)))
@@ -8049,13 +8058,10 @@ Scanner should be initialized."
     (js2-node-add-children fn-node pn)
     pn))
 
-(defun js2-define-destruct-symbols
+(defun js2-define-destruct-symbols-internal
     (node decl-type face &optional ignore-not-in-block name-nodes)
-  "Declare and fontify destructuring parameters inside NODE.
-NODE is either `js2-array-node', `js2-object-node', or `js2-name-node'.
-
-Return a list of `js2-name-node' nodes representing the symbols
-declared; probably to check them for errors."
+  "Internal version of `js2-define-destruct-symbols'.  The only
+difference is that NAME-NODES is passed down recursively."
   (cond
    ((js2-name-node-p node)
     (let (leftpos)
@@ -8070,7 +8076,7 @@ declared; probably to check them for errors."
     (dolist (elem (js2-object-node-elems node))
       (setq name-nodes
             (append name-nodes
-                    (js2-define-destruct-symbols
+                    (js2-define-destruct-symbols-internal
                      ;; In abbreviated destructuring {a, b}, right == left.
                      (js2-object-prop-node-right elem)
                      decl-type face ignore-not-in-block name-nodes)))))
@@ -8079,18 +8085,29 @@ declared; probably to check them for errors."
       (when elem
         (setq name-nodes
               (append name-nodes
-                      (js2-define-destruct-symbols
+                      (js2-define-destruct-symbols-internal
                        elem decl-type face ignore-not-in-block name-nodes))))))
    (t (js2-report-error "msg.no.parm" nil (js2-node-abs-pos node)
                         (js2-node-len node))))
   name-nodes)
 
+(defun js2-define-destruct-symbols (node decl-type face &optional ignore-not-in-block)
+  "Declare and fontify destructuring parameters inside NODE.
+NODE is either `js2-array-node', `js2-object-node', or `js2-name-node'.
+
+Return a list of `js2-name-node' nodes representing the symbols
+declared; probably to check them for errors."
+  (js2-define-destruct-symbols-internal node decl-type face ignore-not-in-block))
+
+(defvar js2-illegal-strict-identifiers
+  '("eval" "arguments")
+  "Identifiers not allowed as variables in strict mode.")
+
 (defun js2-check-strict-identifier (name-node)
   "Check that NAME-NODE makes a legal strict mode identifier."
   (when js2-in-use-strict-directive
     (let ((param-name (js2-name-node-name name-node)))
-      (when (or (string= param-name "eval")
-                (string= param-name "arguments"))
+      (when (member param-name js2-illegal-strict-identifiers)
         (js2-report-error "msg.bad.id.strict" param-name
                           (js2-node-abs-pos name-node) (js2-node-len name-node))))))
 
@@ -8102,31 +8119,20 @@ for strict mode errors caused by PARAMS."
       (let ((param-name (js2-name-node-name param)))
         (js2-check-strict-identifier param)
         (when (cl-some (lambda (param)
-                         (string= (js2-name-node-name param)
-                                  param-name))
+                         (string= (js2-name-node-name param) param-name))
                        preceding-params)
           (js2-report-error "msg.dup.param.strict" param-name
                             (js2-node-abs-pos param) (js2-node-len param)))))))
 
 (defun js2-parse-function-params (function-type fn-node pos)
   "Parse the parameters of a function of FUNCTION-TYPE
-represented by FN-NODE at POS.
-
-Return a list of lists of arguments to apply many times to
-`js2-check-strict-function-params' to retroactively check for
-strict mode errors that occurred.  Because the function body is
-parsed after its parameters, and the body might activate strict
-mode for that function, the check has to occur after the body is
-parsed."
+represented by FN-NODE at POS."
   (if (js2-match-token js2-RP)
-      (progn (setf (js2-function-node-rp fn-node) (- (js2-current-token-beg) pos))
-             ;; Return an empty list for consistency.
-             '())
+      (setf (js2-function-node-rp fn-node) (- (js2-current-token-beg) pos))
     (let ((paren-free-arrow (and (eq function-type 'FUNCTION_ARROW)
                                  (eq (js2-current-token-type) js2-NAME)))
           params param
           param-name-nodes new-param-name-nodes
-          deferred-error-checking-arguments
           default-found rest-param-at)
       (when paren-free-arrow
         (js2-unget-token))
@@ -8139,13 +8145,10 @@ parsed."
                  (js2-get-token)
                  (when default-found
                    (js2-report-error "msg.no.default.after.default.param"))
-                 (setq param (js2-parse-destruct-primary-expr))
-                 (setq new-param-name-nodes (js2-define-destruct-symbols
+                 (setq param (js2-parse-destruct-primary-expr)
+                       new-param-name-nodes (js2-define-destruct-symbols
                                              param js2-LP 'js2-function-param))
-                 (setq deferred-error-checking-arguments
-                       (append deferred-error-checking-arguments
-                               (list (list param-name-nodes
-                                           new-param-name-nodes))))
+                 (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))
                 ;; variable name
@@ -8160,10 +8163,7 @@ parsed."
                  (js2-record-face 'js2-function-param)
                  (setq param (js2-create-name-node))
                  (js2-define-symbol js2-LP (js2-current-token-string) param)
-                 (setq deferred-error-checking-arguments
-                       (append deferred-error-checking-arguments
-                               (list (list param-name-nodes
-                                           (list 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 (or (and default-found
@@ -8199,8 +8199,7 @@ parsed."
         (setf (js2-function-node-rest-p fn-node) t))
       (dolist (p params)
         (js2-node-add-children fn-node p)
-        (push p (js2-function-node-params fn-node)))
-      deferred-error-checking-arguments)))
+        (push p (js2-function-node-params fn-node))))))
 
 (defun js2-check-inconsistent-return-warning (fn-node name)
   "Possibly show inconsistent-return warning.
@@ -8249,10 +8248,7 @@ Last token scanned is the close-curly for the function body."
     (js2-must-match js2-LP "msg.no.paren.parms")
     (js2-parse-function 'FUNCTION_EXPRESSION pos star-p name)))
 
-(defun js2-parse-function (function-type pos star-p &optional 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."
+(defun js2-parse-function-internal (function-type pos star-p &optional name)
   (let (fn-node lp)
     (if (= (js2-current-token-type) js2-LP) ; eventually matched LP?
         (setq lp (js2-current-token-beg)))
@@ -8267,7 +8263,9 @@ arrow function), NAME is js2-name-node."
       (when (and (eq function-type 'FUNCTION_STATEMENT)
                  (cl-plusp (js2-name-node-length name)))
         ;; Function statements define a symbol in the enclosing scope
-        (js2-define-symbol js2-FUNCTION (js2-name-node-name name) fn-node)))
+        (js2-define-symbol js2-FUNCTION (js2-name-node-name name) fn-node))
+      (when js2-in-use-strict-directive
+        (js2-check-strict-identifier name)))
     (if (or (js2-inside-function) (cl-plusp js2-nesting-of-with))
         ;; 1. Nested functions are not affected by the dynamic scope flag
         ;;    as dynamic scope is already a parent of their scope.
@@ -8282,24 +8280,14 @@ arrow function), NAME is js2-name-node."
           (js2-end-flags 0)
           js2-label-set
           js2-loop-set
-          js2-loop-and-switch-set
-          deferred-error-checking-arguments)
-      (setq deferred-error-checking-arguments
-            (js2-parse-function-params function-type fn-node pos))
+          js2-loop-and-switch-set)
+      (js2-parse-function-params function-type fn-node pos)
       (when (eq function-type 'FUNCTION_ARROW)
         (js2-must-match js2-ARROW "msg.bad.arrow.args"))
       (if (and (>= js2-language-version 180)
                (/= (js2-peek-token) js2-LC))
           (js2-parse-function-closure-body fn-node)
         (js2-parse-function-body fn-node))
-      (when (js2-function-node-in-strict-mode fn-node)
-        ;; Pretend that we're inside the function again.  It cleans up its
-        ;; `js2-in-use-strict-directive' binding.
-        (let ((js2-in-use-strict-directive t))
-          (when name
-            (js2-check-strict-identifier name))
-          (dolist (arguments deferred-error-checking-arguments)
-            (apply #'js2-check-strict-function-params arguments))))
       (js2-check-inconsistent-return-warning fn-node name)
 
       (when name
@@ -8323,6 +8311,29 @@ arrow function), NAME is js2-name-node."
     (setf (js2-scope-parent-scope fn-node) js2-current-scope)
     fn-node))
 
+(defun js2-parse-function (function-type pos star-p &optional 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."
+  (let ((continue t)
+        ts-state
+        fn-node
+        ;; Preserve strict state outside this function.
+        (js2-in-use-strict-directive js2-in-use-strict-directive))
+    ;; Parse multiple times if a new strict mode directive is discovered in the
+    ;; function body, as new rules will be retroactively applied to the legality
+    ;; of function names and parameters.
+    (while continue
+      (setq ts-state (make-js2-ts-state))
+      (setq continue (catch 'reparse
+                       (setq fn-node (js2-parse-function-internal
+                                      function-type pos star-p name))
+                       ;; Don't continue.
+                       nil))
+      (when continue
+        (js2-ts-seek ts-state)))
+    fn-node))
+
 (defun js2-parse-statements (&optional parent)
   "Parse a statement list.  Last token consumed must be js2-LC.
 
@@ -9141,6 +9152,8 @@ does not match an existing label, reports an error and returns nil."
 
 (defun js2-parse-with ()
   "Parser for with-statement.  Last matched token must be js2-WITH."
+  (when js2-in-use-strict-directive
+    (js2-report-error "msg.no.with.strict"))
   (let ((pos (js2-current-token-beg))
         obj body pn lp rp)
     (if (js2-must-match js2-LP "msg.no.paren.with")
@@ -9539,8 +9552,12 @@ If NODE is non-nil, it is the AST node associated with the symbol."
          (len (if node (js2-node-len node))))
     (cond
      ((and symbol ; already defined
-           (or (= sdt js2-CONST) ; old version is const
-               (= decl-type js2-CONST) ; new version is const
+           (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))))
@@ -9651,7 +9668,10 @@ If NODE is non-nil, it is the AST node associated with the symbol."
         ;; tt express assignment (=, |=, ^=, ..., %=)
         (setq op-pos (- (js2-current-token-beg) pos)  ; relative
               left pn)
-        (js2-check-strict-identifier left)
+        ;; The assigned node could be a js2-prop-get-node (foo.bar = 0), we only
+        ;; care about assignment to strict variable names.
+        (when (js2-name-node-p left)
+          (js2-check-strict-identifier left))
         (setq right (js2-parse-assign-expr)
               pn (make-js2-assign-node :type tt
                                        :pos pos
@@ -10281,7 +10301,7 @@ For instance, @[expr], @*::[expr], or ns::[expr]."
   "Parse a literal (leaf) expression of some sort.
 Includes complex literals such as functions, object-literals,
 array-literals, array comprehensions and regular expressions."
-  (let (tt)
+  (let (tt node)
     (setq tt (js2-current-token-type))
     (cond
      ((= tt js2-CLASS)
@@ -10302,7 +10322,11 @@ array-literals, array comprehensions and regular expressions."
      ((= tt js2-NAME)
       (js2-parse-name tt))
      ((= tt js2-NUMBER)
-      (make-js2-number-node))
+      (setq node (make-js2-number-node))
+      (when (and js2-in-use-strict-directive
+                 (= (js2-number-node-num-base node) 8))
+        (js2-report-error "msg.no.octal.strict"))
+      node)
      ((or (= tt js2-STRING) (= tt js2-NO_SUBS_TEMPLATE))
       (make-js2-string-node :type tt))
      ((= tt js2-TEMPLATE_HEAD)
@@ -10647,11 +10671,27 @@ If ONLY-OF-P is non-nil, only the 'for (foo of bar)' form is allowed."
     (apply #'js2-node-add-children result (js2-object-node-elems result))
     result))
 
+(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
+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)))))
+
 (defun js2-parse-object-literal-elems (&optional class-p)
   (let ((pos (js2-current-token-beg))
         (static nil)
         (continue t)
-        tt elems elem after-comma previous-token)
+        tt elems elem
+        elem-key-string previous-elem-key-string
+        after-comma previous-token)
     (while continue
       (setq tt (js2-get-prop-name-token)
             static nil
@@ -10710,8 +10750,21 @@ If ONLY-OF-P is non-nil, only the 'for (foo of bar)' form is allowed."
               (setq after-comma (js2-current-token-end)))
           (js2-unget-token)
           (unless class-p (setq continue nil))))
-      ;; Append any parsed element.
-      (if elem (push elem elems)))       ; end loop
+      (when elem
+        (when (and js2-in-use-strict-directive
+                   (setq elem-key-string (js2-property-key-string elem))
+                   (cl-some
+                    (lambda (previous-elem)
+                      (and (setq previous-elem-key-string
+                                 (js2-property-key-string previous-elem))
+                           (string= previous-elem-key-string elem-key-string)))
+                    elems))
+          (js2-report-error "msg.dup.obj.lit.prop.strict"
+                            elem-key-string
+                            (js2-node-abs-pos (js2-infix-node-left elem))
+                            (js2-node-len (js2-infix-node-left elem))))
+        ;; Append any parsed element.
+        (push elem elems)))       ; end loop
     (js2-must-match js2-RC "msg.no.brace.prop")
     (nreverse elems)))
 
@@ -10773,7 +10826,8 @@ When `js2-is-in-destructuring' is t, forms like {a, b, c} will be permitted."
 
 (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 or string."
+PROP is the node representing the property: a number, name,
+string or expression."
   (let* ((tt (js2-get-token))
          (pos (js2-node-pos prop))
          colon expr result)
@@ -11251,15 +11305,6 @@ indentation is aligned to that column."
   (interactive)
   (while (forward-comment 1)))
 
-(defun js2-current-indent (&optional pos)
-  "Return column of indentation on current line.
-If POS is non-nil, go to that point and return indentation for that line."
-  (save-excursion
-    (if pos
-        (goto-char pos))
-    (back-to-indentation)
-    (current-column)))
-
 (defun js2-arglist-close ()
   "Return non-nil if we're on a line beginning with a close-paren/brace."
   (save-excursion
@@ -11314,7 +11359,7 @@ of the buffer to the current point.  NORMAL-COL is the indentation
 column computed by the heuristic guesser based on current paren,
 bracket, brace and statement nesting.  If BACKWARDS, cycle positions
 in reverse."
-  (let ((cur-indent (js2-current-indent))
+  (let ((cur-indent (current-indentation))
         (old-buffer-undo-list buffer-undo-list)
         ;; Emacs 21 only has `count-lines', not `line-number-at-pos'
         (current-line (save-excursion