Subject: bug#33400: 26.1; Order changed for overriding “nil” mode in dir-locals

From: Neil Roberts <[email-masked]>
Date: Thu, 15 Nov 2018 14:21:27 +0100

There is a project called Piglit with a .dir_locals.el file¹ like this:

((nil . ((indent-tabs-mode . t)))
 (python-mode . ((indent-tabs-mode . nil))))

I think the intention is that most files in the project should use
indent-tabs-mode but Python files should not. This doesn’t seem to work
in 26.1 and the definition for the nil mode overrides the definition for
the Python mode and Emacs enables tab indentation for Python files. If I
swap the order of the two lines then the Python mode overrides the nil
mode and it works as intended. However, this appears to be a change in
26.1. Previously the opposite order gets the right behaviour.

In git master the behaviour is fixed again so that the order shown above
makes it work. I bisected the fix down to 97b7e58c4d34722e8b0. However
it doesn’t look like this is deliberately fixing the bug. It looks like
it’s due to the fact that a call like the following ends up reversing
the order of newvars as a side effect:

  (map-merge-with 'list 'func nil newvars)

That commit just makes it avoid calling map-merge-with when there is
only one file. So presumably the bug still exists in git master when
there are multiple files.

Perhaps you could argue that this isn’t really a bug and that having
multiple values for a directory local variable is just undefined
behaviour. However I think the previous behaviour was quite useful and
it would be nice to maintain it. Or maybe ideally it could even just say
that any more specific mode overrides any less specific mode.

This was discussed on StackExchange here:

https://emacs.stackexchange.com/questions/45998

- Neil

1. https://cgit.freedesktop.org/piglit/tree/.dir-locals.el

In GNU Emacs 26.1 (build 1, x86_64-redhat-linux-gnu, GTK+ Version 3.22.30)
 of 2018-06-26 built on buildhw-10.phx2.fedoraproject.org
Windowing system distributor 'Fedora Project', version 11.0.11906000
System Description:	Fedora release 28 (Twenty Eight)

Configured using:
 'configure --build=x86_64-redhat-linux-gnu
 --host=x86_64-redhat-linux-gnu --program-prefix=
 --disable-dependency-tracking --prefix=/usr --exec-prefix=/usr
 --bindir=/usr/bin --sbindir=/usr/sbin --sysconfdir=/etc
 --datadir=/usr/share --includedir=/usr/include --libdir=/usr/lib64
 --libexecdir=/usr/libexec --localstatedir=/var
 --sharedstatedir=/var/lib --mandir=/usr/share/man
 --infodir=/usr/share/info --with-dbus --with-gif --with-jpeg --with-png
 --with-rsvg --with-tiff --with-xft --with-xpm --with-x-toolkit=gtk3
 --with-gpm=no --with-xwidgets --with-modules
 build_alias=x86_64-redhat-linux-gnu host_alias=x86_64-redhat-linux-gnu
 'CFLAGS=-DMAIL_USE_LOCKF -O2 -g -pipe -Wall -Werror=format-security
 -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -fexceptions
 -fstack-protector-strong -grecord-gcc-switches
 -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1
 -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic
 -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection'
 LDFLAGS=-Wl,-z,relro
 PKG_CONFIG_PATH=:/usr/lib64/pkgconfig:/usr/share/pkgconfig'

Configured features:
XPM JPEG TIFF GIF PNG RSVG IMAGEMAGICK SOUND DBUS GSETTINGS NOTIFY ACL
LIBSELINUX GNUTLS LIBXML2 FREETYPE M17N_FLT LIBOTF XFT ZLIB
TOOLKIT_SCROLL_BARS GTK3 X11 MODULES THREADS XWIDGETS LCMS2

Important settings:
  value of $LANG: fr_FR.UTF-8
  value of $XMODIFIERS: @im=ibus
  locale-coding-system: utf-8-unix

Major mode: GNUmakefile

Minor modes in effect:
  shell-dirtrack-mode: t
  whitespace-mode: t
  show-paren-mode: t
  diff-auto-refine-mode: t
  tooltip-mode: t
  global-eldoc-mode: t
  electric-indent-mode: t
  mouse-wheel-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

Load-path shadows:
None found.

Features:
(shadow emacsbug completion info ibuf-ext ibuffer ibuffer-loaddefs
bug-reference autoconf autoconf-mode notmuch-jump mhtml-mode css-mode
eww flyspell js pp rng-xsd xsd-regexp rng-cmpct rng-nxml rng-valid
rng-loc rng-uri rng-parse nxml-parse rng-match rng-dt rng-util rng-pttrn
nxml-ns nxml-mode nxml-outln nxml-rap sgml-mode nxml-util nxml-enc
xmltok dired-aux smerge-mode conf-mode view ffap tramp-cmds tabify imenu
man make-mode apropos ediff-merg ediff-wind ediff-diff ediff-mult
ediff-help ediff-init ediff-util ediff python files-x tramp-cache
tramp-sh tramp tramp-compat tramp-loaddefs trampver ucs-normalize
sh-script smie executable cmake-mode rx cl-print debug grep cl-extra
eieio-opt speedbar sb-image ezimage dframe find-func doc-view jka-compr
image-mode shell pcomplete find-dired shr-color color shr svg dom
misearch multi-isearch sendmail qp ispell mm-archive sort mail-extr
whitespace server gtags paren notmuch hl-line notmuch-message
notmuch-hello notmuch-tree notmuch-show notmuch-print notmuch-crypto
notmuch-mua notmuch-draft notmuch-maildir-fcc notmuch-address
notmuch-company notmuch-parser notmuch-wash diff-mode coolj
notmuch-query goto-addr icalendar diary-lib diary-loaddefs cal-menu
calendar cal-loaddefs notmuch-tag edmacro kmacro crm notmuch-lib
notmuch-version notmuch-compat gnus-html url-queue help-fns radix-tree
help-mode browse-url url-cache mm-url url url-proxy url-privacy
url-expand url-methods url-history url-cookie url-domsuf url-util
url-parse auth-source cl-seq eieio eieio-core eieio-loaddefs url-vars
gnus-art mm-uu mml2015 mm-view mml-smime smime dig mailcap gnus-sum
gnus-group gnus-undo gnus-start gnus-cloud nnimap nnmail mail-source tls
gnutls utf7 netrc nnoo parse-time gnus-spec gnus-int gnus-range gnus-win
gnus nnheader javadoc-help thingatpt advice git log-edit easy-mmode
message rmc puny dired dired-loaddefs format-spec rfc822 mml mml-sec
password-cache epa derived epg epg-config gnus-util rmail rmail-loaddefs
mm-decode mm-bodies mm-encode mail-parse rfc2231 rfc2047 rfc2045 mm-util
ietf-drums mail-prsvr mailabbrev mail-utils gmm-utils mailheader
pcvs-util add-log ewoc cus-edit cus-start cus-load iso-transl
color-theme-subdued color-theme-neil-dark cc-mode cc-fonts cc-guess
cc-menus cc-cmds cc-styles cc-align cc-engine cc-vars cc-defs warnings
elec-pair compile comint ansi-color ring color-theme easymenu wid-edit
cl clang-rename clang-include-fixer let-alist json map seq byte-opt
bytecomp byte-compile cconv clang-format cl-macs gv xml cl-loaddefs
cl-lib time-date mule-util tooltip eldoc electric uniquify ediff-hook
vc-hooks lisp-float-type mwheel term/x-win x-win term/common-win x-dnd
tool-bar dnd fontset image regexp-opt fringe tabulated-list replace
newcomment text-mode elisp-mode lisp-mode prog-mode register page
menu-bar rfn-eshadow isearch timer select scroll-bar mouse jit-lock
font-lock syntax facemenu font-core term/tty-colors 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 composite charscript charprop
case-table epa-hook jka-cmpr-hook help simple abbrev obarray 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 lcms2
dynamic-setting system-font-setting font-render-setting xwidget-internal
move-toolbar gtk x-toolkit x multi-tty make-network-process emacs)

Memory information:
((conses 16 3743848 478859)
 (symbols 48 51709 1)
 (miscs 40 98163 24087)
 (strings 32 331746 46857)
 (string-bytes 1 25016091)
 (vectors 16 79464)
 (vector-slots 8 2261743 505600)
 (floats 8 665 2273)
 (intervals 56 503332 20703)
 (buffers 992 695))