GNU bug report logs - #37045
lisp-indent-region hangs in lisp-indent-calc-next when indenting the three characters "|#\n"

Previous Next

Package: emacs;

Reported by: eschulte <at> grammatech.com

Date: Fri, 16 Aug 2019 04:50:03 UTC

Severity: normal

Tags: fixed, patch

Found in version 26.1

Fixed in version 26.3

Done: Noam Postavsky <npostavs <at> gmail.com>

Bug is archived. No further changes may be made.

Full log


Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: eschulte <at> grammatech.com
To: bug-gnu-emacs <at> gnu.org
Subject: lisp-indent-region hangs in lisp-indent-calc-next when indenting the
 three characters "|#\n"
Date: Thu, 15 Aug 2019 23:46:06 -0400
[Message part 1 (text/plain, inline)]
Hi,

Indent-region sometimes hangs when indenting a lisp file.  A minimal
example is the file just containing the characters "|#" followed by a
newline.  This hang is due to an infinite loop in the while loop in the
`lisp-indent-calc-next' function.  The following version of this
function fixes this problem, but is certainly not the appropriate
long-term fix.

[lisp-indent-calc-next-workaround.lisp (text/x-lisp, inline)]
;;; Work around a bug in `lisp-indent-calc-next' in which it can hang
;;; on a dangling '|#'.  The change in the code below against the
;;; original is the addition of a block and a throw to this block when
;;; we're infinite looping (when the newly added last-last-sexp
;;; matches last-sexp).  On the off chance it is possible in normal
;;; execution for last-sexp to stay the same for some number of loop
;;; iterations this patch only aborts if they're equal 100 times in a
;;; row.
(defun lisp-indent-calc-next (state)
  "Move to next line and return calculated indent for it.
STATE is updated by side effect, the first state should be
created by `lisp-indent-initial-state'.  This function may move
by more than one line to cross a string literal."
  (pcase-let* (((cl-struct lisp-indent-state
                           (stack indent-stack) ppss ppss-point)
                state)
               (indent-depth (car ppss)) ; Corresponding to indent-stack.
               (depth indent-depth)
               (last-last-sexp nil) (counter 0))
    (block nil
      ;; Parse this line so we can learn the state to indent the
      ;; next line.
      (while (let ((last-sexp (nth 2 ppss)))
               (setq ppss (parse-partial-sexp
                           ppss-point (progn (end-of-line) (point))
                           nil nil ppss))
               ;; Preserve last sexp of state (position 2) for
               ;; `calculate-lisp-indent', if we're at the same depth.
               (if (and (not (nth 2 ppss)) (= depth (car ppss)))
                   (setf (nth 2 ppss) last-sexp)
                 (setq last-sexp (nth 2 ppss)))
               (setq depth (car ppss))
               (if (and (>= counter 100) (equal last-sexp last-last-sexp))
                   (progn (setf indent-stack nil) (return))
                 (setq last-last-sexp last-sexp
                       counter (1+ counter)))
               ;; Skip over newlines within strings.
               (nth 3 ppss))
        (let ((string-start (nth 8 ppss)))
          (setq ppss (parse-partial-sexp (point) (point-max)
                                         nil nil ppss 'syntax-table))
          (setf (nth 2 ppss) string-start) ; Finished a complete string.
          (setq depth (car ppss)))
        (setq ppss-point (point)))
      (setq ppss-point (point))
      (let* ((depth-delta (- depth indent-depth)))
        (cond ((< depth-delta 0)
               (setq indent-stack (nthcdr (- depth-delta) indent-stack)))
              ((> depth-delta 0)
               (setq indent-stack (nconc (make-list depth-delta nil)
                                         indent-stack))))))

    (prog1
        (let (indent)
          (cond ((= (forward-line 1) 1) nil)
                ;; Negative depth, probably some kind of syntax error.
                ((null indent-stack)
                 ;; Reset state.
                 (setq ppss (parse-partial-sexp (point) (point))))
                ((car indent-stack))
                ((integerp (setq indent (calculate-lisp-indent ppss)))
                 (setf (car indent-stack) indent))
                ((consp indent)       ; (COLUMN CONTAINING-SEXP-START)
                 (car indent))
                ;; This only happens if we're in a string.
                (t (error "This shouldn't happen"))))
      (setf (lisp-indent-state-stack state) indent-stack)
      (setf (lisp-indent-state-ppss-point state) ppss-point)
      (setf (lisp-indent-state-ppss state) ppss))))
[Message part 3 (text/plain, inline)]
Thanks,
Eric

P.S. I hope this isn't a duplicate report, I tried to search both the
archive https://lists.gnu.org/archive/html/bug-gnu-emacs/ and the
previous bug reports https://debbugs.gnu.org/ but I didn't find anything
relevant.


-- 
Eric Schulte, Ph.D.
Director of Automated Software Engineering
GrammaTech, Inc.
PGP Fingerprint: FA8D C2C3 E8A0 A749 34CD  9DCF 3C1B 8581 614C A05D
Pronouns: he, him, his
[signature.asc (application/pgp-signature, inline)]

This bug report was last modified 5 years and 273 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.