Subject: bug#36552: 25.1; lisp tagbody indentation not working

From: Ryan Brown <[email-masked]>
Date: Mon, 8 Jul 2019 08:50:15 -0600

I'm typing in some old lisp code that makes heavy use of (prog) and (go).
I was trying to figure out how to get  emacs to format the tagbody labels
like the original source listing:
(PROG (X Y Z)
   (...)
 LOOP
   (...)
   (GO LOOP) )

I discovered that the common lisp indentation is supposed to support it,
but it wasn't working.
The first line of the prog gets properly indented as either an expression
or a tag, but then every following line just gets the same indentation as
the previous one.

Sure enough, lisp-indent-259 only only runs the indentation function for
the first item after &rest:

          (cond ((and tail (not (symbolp tem)))
                 ;; indent tail of &rest in same way as first elt of rest
                 (throw 'exit normal-indent))
With this change the tagbodies get properly indented:
          (cond ((and tail (not (or (consp tem) (symbolp tem))))
                 ;; indent tail of &rest in same way as first elt of rest
                 (throw 'exit normal-indent))

Here's the entire patched function.
(defun lisp-indent-259
    (method path state indent-point sexp-column normal-indent)
  (catch 'exit
    (let ((p path)
          (containing-form-start (elt state 1))
          n tem tail)
      ;; Isn't tail-recursion wonderful?
      (while p
        ;; This while loop is for destructuring.
        ;; p is set to (cdr p) each iteration.
        (if (not (consp method)) (lisp-indent-report-bad-format method))
        (setq n (1- (car p))
              p (cdr p)
              tail nil)
        (while n
          ;; This while loop is for advancing along a method
          ;; until the relevant (possibly &rest/&body) pattern
          ;; is reached.
          ;; n is set to (1- n) and method to (cdr method)
          ;; each iteration.
          (setq tem (car method))

          (or (eq tem 'nil)             ;default indentation
              (eq tem '&lambda)         ;lambda list
              (and (eq tem '&body) (null (cdr method)))
              (and (eq tem '&rest)
                   (consp (cdr method))
                   (null (cddr method)))
              (integerp tem)            ;explicit indentation specified
              (and (consp tem)          ;destructuring
                   (eq (car tem) '&whole)
                   (or (symbolp (cadr tem))
                       (integerp (cadr tem))))
              (and (symbolp tem)        ;a function to call to do the work.
                   (null (cdr method)))
              (lisp-indent-report-bad-format method))

          (cond ((and tail (not (or (consp tem) (symbolp tem))))
                 ;; indent tail of &rest in same way as first elt of rest
                 (throw 'exit normal-indent))
                ((eq tem '&body)
                 ;; &body means (&rest <lisp-body-indent>)
                 (throw 'exit
                   (if (and (= n 0)     ;first body form
                            (null p))   ;not in subforms
                       (+ sexp-column
                          lisp-body-indent)
                       normal-indent)))
                ((eq tem '&rest)
                 ;; this pattern holds for all remaining forms
                 (setq tail (> n 0)
                       n 0
                       method (cdr method)))
                ((> n 0)
                 ;; try next element of pattern
                 (setq n (1- n)
                       method (cdr method))
                 (if (< n 0)
                     ;; Too few elements in pattern.
                     (throw 'exit normal-indent)))
                ((eq tem 'nil)
(throw 'exit (if (consp normal-indent)
 normal-indent
(list normal-indent containing-form-start))))
((eq tem '&lambda)
(throw 'exit
(cond ((null p)
      (list (+ sexp-column 4) containing-form-start))
     ((null (cdr p))
                               ;; Indentation within a lambda-list. -- dvl
                               (list (lisp-indent-lambda-list
                                      indent-point
                                      sexp-column
                                      containing-form-start)
                                     containing-form-start))
                              (t
                               normal-indent))))
                ((integerp tem)
                 (throw 'exit
                   (if (null p)         ;not in subforms
                       (list (+ sexp-column tem) containing-form-start)
                       normal-indent)))
                ((symbolp tem)          ;a function to call
                 (throw 'exit
                   (funcall tem path state indent-point
                            sexp-column normal-indent)))
                (t
                 ;; must be a destructing frob
                 (if (not (null p))
                     ;; descend
               (setq method (cddr tem)
                           n nil)
               (setq tem (cadr tem))
                   (throw 'exit
                     (cond (tail
                            normal-indent)
                           ((eq tem 'nil)
                            (list normal-indent
                                  containing-form-start))
                           ((integerp tem)
                            (list (+ sexp-column tem)
                                  containing-form-start))
                           (t
                            (funcall tem path state indent-point
                                     sexp-column normal-indent))))))))))))

In GNU Emacs 25.1.1 (x86_64-pc-linux-gnu)
 of 2017-09-14, modified by Debian built on trouble
System Description: Debian GNU/Linux 9.8 (stretch)

Configured using:
 'configure --build x86_64-linux-gnu --prefix=/usr
 --sharedstatedir=/var/lib --libexecdir=/usr/lib
 --localstatedir=/var/lib --infodir=/usr/share/info
 --mandir=/usr/share/man --with-pop=yes
 --enable-locallisppath=/etc/emacs25:/etc/emacs:/usr/local/share/emacs/25.1/site-lisp:/usr/local/share/emacs/site-lisp:/usr/share/emacs/25.1/site-lisp:/usr/share/emacs/site-lisp
 --with-sound=alsa --build x86_64-linux-gnu --prefix=/usr
 --sharedstatedir=/var/lib --libexecdir=/usr/lib
 --localstatedir=/var/lib --infodir=/usr/share/info
 --mandir=/usr/share/man --with-pop=yes
 --enable-locallisppath=/etc/emacs25:/etc/emacs:/usr/local/share/emacs/25.1/site-lisp:/usr/local/share/emacs/site-lisp:/usr/share/emacs/25.1/site-lisp:/usr/share/emacs/site-lisp
 --with-sound=alsa --with-x=no --without-gconf --without-gsettings
 'CFLAGS=-g -O2
 -fdebug-prefix-map=/build/emacs25-wN2qS3/emacs25-25.1+1=.
-fstack-protector-strong
 -Wformat -Werror=format-security -Wall' 'CPPFLAGS=-Wdate-time
 -D_FORTIFY_SOURCE=2' LDFLAGS=-Wl,-z,relro'

Configured features:
JPEG SOUND GPM DBUS NOTIFY ACL LIBSELINUX GNUTLS LIBXML2 ZLIB

Important settings:
  value of $LANG: en_US.UTF-8
  locale-coding-system: utf-8-unix

Major mode: Lisp

Minor modes in effect:
  tooltip-mode: t
  global-eldoc-mode: t
  electric-indent-mode: t
  menu-bar-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t
  line-number-mode: t
  transient-mark-mode: t

Recent messages:
Auto-saving...
Mark set
Auto-saving...done
Auto-saving...done
next-line: End of buffer
Auto-saving...done
Mark set [2 times]
Auto-saving...done
Saving file /home/ryan/sin.lisp...
Wrote /home/ryan/sin.lisp

Load-path shadows:
/usr/share/emacs/25.1/site-lisp/debian-startup hides
/usr/share/emacs/site-lisp/debian-startup

Features:
(shadow sort mail-extr emacsbug message dired format-spec rfc822 mml
mml-sec password-cache epg epg-config gnus-util mm-decode mm-bodies
mm-encode mail-parse rfc2231 mailabbrev gmm-utils mailheader sendmail
rfc2047 rfc2045 ietf-drums mm-util mail-prsvr mail-utils cl-extra
thingatpt help-fns help-mode tool-bar cus-edit cus-start cus-load
wid-edit regexp-opt gv find-func edebug easymenu cl-loaddefs pcase
cl-lib misearch multi-isearch jka-compr term/xterm xterm time-date
cl-indent mule-util tooltip eldoc electric uniquify ediff-hook vc-hooks
lisp-float-type tabulated-list newcomment elisp-mode lisp-mode prog-mode
register page menu-bar rfn-eshadow timer select mouse jit-lock font-lock
syntax facemenu font-core frame cl-generic cham georgian utf-8-lang
misc-lang vietnamese tibetan thai tai-viet lao korean japanese eucjp-ms
cp51932 hebrew greek romanian slovak czech european ethiopic indian
cyrillic chinese charscript case-table epa-hook jka-cmpr-hook help
simple abbrev minibuffer cl-preloaded nadvice loaddefs button faces
cus-face macroexp files text-properties overlay sha1 md5 base64 format
env code-pages mule custom widget hashtable-print-readable backquote
dbusbind inotify multi-tty make-network-process emacs)

Memory information:
((conses 16 124690 4205)
 (symbols 48 20939 0)
 (miscs 40 122 580)
 (strings 32 20140 4691)
 (string-bytes 1 550849)
 (vectors 16 11774)
 (vector-slots 8 399804 2164)
 (floats 8 172 959)
 (intervals 56 3273 0)
 (buffers 976 25))