1. In `compilation-error-properties’,
`compilation-transform-file-match-alist’ is matched against a nil
filename, since the definition of that filename looks like (note
the bufferp):
(let ((file-name
(and (consp file)
(not (bufferp (car file)))
(if (cdr file)
(expand-file-name (car file) (cdr file))
(car file)))))
This seems to have been fixed using a when-let in the Emacs 28 branch (unconfirmed).
2. `compilation-get-file-structure’ is called with the file list, but
this function does not expect a (buffer), and calls (e.g.)
file-name-absolute-p(#<buffer boo.py>), resulting in an error.
Any chance you can provide a recipe or a backtrace?Also it might be useful to provide some context, such as which packageuses this ability to return a buffer.
Thanks. This is for an as yet unreleased packaged, which uses compilation scanning for uniform traceback navigation for both code files on disk as well as regions of code sent from buffers (which may have no associated file). Hence the interest in using a buffer as the compile error “file” target.
Reproducing is fairly straightforward. First, evaluate this simple “error line” pattern:
(defun my/filename-function () (list (get-buffer (match-string 1))))
(defvar my/compilation-error-regexp
"Buffer \"" (group (+ (not "\""))) "\", " ; 1: buffer name
"line" (+ space) (group (+ digit)) ; 2: line
Then, in a buffer like *compile* put the following fake error for it to match:
Buffer "*scratch*", line 2
In that buffer, evaluate (e.g. via M-:):
(progn (compilation-setup)
(setq compilation-error-regexp-alist my/compilation-error-regexp)
(compilation-parse-errors (point-min) (point-max)))
You should see error 1.) pertaining to searching the (nil) filename for /bin/sh (which may have been fixed in Emacs 28 already).
Now, let’s eliminate that error by evaluating (again in *compile*):
(setq compilation-transform-file-match-alist nil)
To stop the transform check. Now evaluate again:
(compilation-parse-errors (point-min) (point-max))
and you will see a different error (2.) from `compilation-get-file-structure', which does not appreciate being handed a buffer instead of a file, despite the documented validity of this input:
Debugger entered--Lisp error: (wrong-type-argument stringp #<buffer *scratch*>)
file-name-absolute-p(#<buffer *scratch*>)
(if (file-name-absolute-p filename) (setq filename (concat comint-file-name-prefix filename)))
(let ((filename (car file)) (spec-directory (if (cdr file) (file-truename (concat comint-file-name-prefix (cdr file)))))) (if (file-name-absolute-p filename) (setq filename (concat comint-file-name-prefix filename))) (unless (memq compilation-parse-errors-filename-function '(nil identity)) (save-match-data (setq filename (funcall compilation-parse-errors-filename-function filename)))) (if (stringp filename) (setq filename (command-line-normalize-file-name filename))) (puthash file (or (gethash (cons filename spec-directory) compilation-locs) (puthash (cons filename spec-directory) (compilation--make-file-struct (list filename spec-directory) fmt) compilation-locs)) compilation-locs))
(or (gethash file compilation-locs) (let ((filename (car file)) (spec-directory (if (cdr file) (file-truename (concat comint-file-name-prefix (cdr file)))))) (if (file-name-absolute-p filename) (setq filename (concat comint-file-name-prefix filename))) (unless (memq compilation-parse-errors-filename-function '(nil identity)) (save-match-data (setq filename (funcall compilation-parse-errors-filename-function filename)))) (if (stringp filename) (setq filename (command-line-normalize-file-name filename))) (puthash file (or (gethash (cons filename spec-directory) compilation-locs) (puthash (cons filename spec-directory) (compilation--make-file-struct (list filename spec-directory) fmt) compilation-locs)) compilation-locs)))
compilation-get-file-structure((#<buffer *scratch*>) nil)
compilation-internal-error-properties((#<buffer *scratch*>) 2 nil nil nil 2 nil)
...
If you patch file-name-absolute-p to accept buffers as well, in fact the whole system works. This indicates that compile.el does explicitly accept buffers for file, as the docs advertise, but has a couple of small bugs related to parsing such arrangements, since presumably this path is seldom exercised.
E.g.:
(defalias 'orig-file-name-absolute-p
(symbol-function 'file-name-absolute-p))
(defun my/file-name-absolute-p (file-or-buffer)
(and (not (bufferp file-or-buffer))
(funcall #'orig-file-name-absolute-p file-or-buffer)))
(cl-letf (((symbol-function #'file-name-absolute-p)
#'my/file-name-absolute-p)) (compilation-parse-errors (point-min) (point-max)))