Subject: Re: bug#78097: 30.1; editorconfig can pass nil to file-name-directory

From: Stefan Monnier <[email-masked]>
Date: Sat, 03 May 2025 10:13:53 -0400

> When a function called from
> `editorconfig-get-local-variables-functions' returns an alist, but
> there is no .editorconfig file in the directory tree,

Could you give a concrete example of how this can happen?

> the `editorconfig--get-dir-local-variables' function will pass nil
> to `file-name-directory', causing an error (stringp nil).
> This happens because `editorconfig--get-dir-local-variables' calls
> `editorconfig-core-get-nearest-editorconfig', which returns nil, and
> passes that nil on to `file-name-directory'.

Indeed.

> The following patch fixes the problem:
>
> --- editorconfig.el.orig        2025-04-27 14:52:35.208707072 +0200
> +++ editorconfig.el     2025-04-27 14:53:19.423741999 +0200
> @@ -739,11 +739,10 @@
>        ;; settings from each file individually and let hack-dir-local merge
>        ;; them because hack-dir-local doesn't have the notion of "unset",
>        ;; and because the conversion of `indent_size' depends on `tab_width'.
> -      (when alist
> -        (cons
> -         (file-name-directory (editorconfig-core-get-nearest-editorconfig
> -                               buffer-file-name))
> -         alist)))))
> +      (when-let* ((alist)
> +                  (file (editorconfig-core-get-nearest-editorconfig
> +                         buffer-file-name)))
> +        (cons (file-name-directory file) alist)))))

Without knowing why `editorconfig-core-get-nearest-editorconfig` returns
nil even though `alist` is non-nil, I can't judge if this is a real fix
or just a quick workaround.

Another quick workaround that has the advantage of not throwing away the
`alist` would be something like the patch below.

        Stefan

diff --git a/lisp/editorconfig.el b/lisp/editorconfig.el
index 7cdd79fa3b0..69e95c9a006 100644
--- a/lisp/editorconfig.el
+++ b/lisp/editorconfig.el
@@ -736,8 +736,10 @@ editorconfig--get-dir-local-variables
       ;; and because the conversion of `indent_size' depends on `tab_width'.
       (when alist
         (cons
-         (file-name-directory (editorconfig-core-get-nearest-editorconfig
-                               buffer-file-name))
+         (let ((ecfile (editorconfig-core-get-nearest-editorconfig
+                        buffer-file-name)))
+           ;; `ecfile' can be nil!?!  (bug#78097)
+           (file-name-directory (or ecfile buffer-file-name)))
          alist)))))
 
 ;;;###autoload