Subject: bug#71115: [PATCH] Fix usage of cons cells in grep-find-ignored-files

From: Spencer Baugh <[email-masked]>
Date: Thu, 23 May 2024 11:04:39 -0400

Dmitry Gutov <[email-masked]> writes:

> Hi Spencer,
>
> Thanks a lot.
>
> On 22/05/2024 15:28, Spencer Baugh wrote:
>> --- a/lisp/progmodes/project.el
>> +++ b/lisp/progmodes/project.el
>> @@ -295,7 +295,7 @@ project-name
>>   Nominally unique, but not enforced."
>>     (file-name-nondirectory (directory-file-name (project-root project))))
>>   -(cl-defgeneric project-ignores (_project _dir)
>> +(cl-defgeneric project-ignores (_project dir)
>>     "Return the list of glob patterns to ignore inside DIR.
>>   Patterns can match both regular files and directories.
>>   To root an entry, start it with `./'.  To match directories only,
>> @@ -304,13 +304,13 @@ project-ignores
>>     ;; TODO: Document and support regexp ignores as used by Hg.
>>     ;; TODO: Support whitelist entries.
>>     (require 'grep)
>> -  (defvar grep-find-ignored-files)
>> +  (declare-function grep-find-ignored-files "grep" (dir))
>>     (nconc
>>      (mapcar
>>       (lambda (dir)
>>         (concat dir "/"))
>>       vc-directory-exclusion-list)
>> -   grep-find-ignored-files))
>> +   (grep-find-ignored-files dir)))
>
> There is just one problem that project.el is supposed to be usable in
> Emacs 26+.
>
> I suppose that a little fboundp check will get us around the problem.

Fixed:

>From 5c9312f47e5a4286c84d01e30133edecb5e8a648 Mon Sep 17 00:00:00 2001
From: Spencer Baugh <[email-masked]>
Date: Wed, 22 May 2024 08:28:07 -0400
Subject: [PATCH] Fix usage of cons cells in grep-find-ignored-files

grep-find-ignored-files is documented to also include cons
cells, not just globs, but there were two places outside grep.el
where we were using it as if it was only a string list.

To fix this, add a helper function named grep-find-ignored-files
which handles grep-find-ignored-files properly and returns the
list of globs, and use it everywhere.

* lisp/progmodes/grep.el (grep--filter-list-by-dir)
(grep-find-ignored-files): Add.
(rgrep-find-ignored-directories): Use grep--filter-list-by-dir.
(lgrep, rgrep-default-command): Use grep-find-ignored-files
function.
* lisp/dired-aux.el (dired-do-find-regexp): Use
grep-find-ignored-files function.
* lisp/progmodes/project.el (project-ignores): Use
grep-find-ignored-files function, if bound. (bug#71115)
---
 lisp/dired-aux.el         |  4 +-
 lisp/progmodes/grep.el    | 99 +++++++++++++++++++--------------------
 lisp/progmodes/project.el |  8 +++-
 3 files changed, 55 insertions(+), 56 deletions(-)

diff --git a/lisp/dired-aux.el b/lisp/dired-aux.el
index a2ce3083cfe..813c14a8661 100644
--- a/lisp/dired-aux.el
+++ b/lisp/dired-aux.el
@@ -3797,13 +3797,13 @@ dired-do-find-regexp
   (interactive "sSearch marked files (regexp): " dired-mode)
   (require 'grep)
   (require 'xref)
-  (defvar grep-find-ignored-files)
   (declare-function rgrep-find-ignored-directories "grep" (dir))
+  (declare-function grep-find-ignored-files "grep" (dir))
   (let* ((marks (dired-get-marked-files nil nil nil nil t))
          (ignores (nconc (mapcar
                           #'file-name-as-directory
                           (rgrep-find-ignored-directories default-directory))
-                         grep-find-ignored-files))
+                         (grep-find-ignored-files default-directory)))
          (fetcher
           (lambda ()
             (let (files xrefs)
diff --git a/lisp/progmodes/grep.el b/lisp/progmodes/grep.el
index 657349cbdff..0a9de04fce1 100644
--- a/lisp/progmodes/grep.el
+++ b/lisp/progmodes/grep.el
@@ -1176,6 +1176,19 @@ grep-read-files
 
 (defvar grep-use-directories-skip 'auto-detect)
 
+(defun grep--filter-list-by-dir (list dir)
+  "Include elements of LIST which are applicable to DIR."
+  (delq nil (mapcar
+             (lambda (ignore)
+               (cond ((stringp ignore) ignore)
+                     ((consp ignore)
+                      (and (funcall (car ignore) dir) (cdr ignore)))))
+             list)))
+
+(defun grep-find-ignored-files (dir)
+  "Return the list of ignored files applicable to DIR."
+  (grep--filter-list-by-dir grep-find-ignored-files dir))
+
 ;;;###autoload
 (defun lgrep (regexp &optional files dir confirm)
   "Run grep, searching for REGEXP in FILES in directory DIR.
@@ -1236,20 +1249,13 @@ lgrep
 		       regexp
 		       files
 		       nil
-		       (and grep-find-ignored-files
-			    (concat " --exclude="
-				    (mapconcat
-                                     (lambda (ignore)
-                                       (cond ((stringp ignore)
-                                              (shell-quote-argument
-                                               ignore grep-quoting-style))
-                                             ((consp ignore)
-                                              (and (funcall (car ignore) dir)
-                                                   (shell-quote-argument
-                                                    (cdr ignore)
-                                                    grep-quoting-style)))))
-				     grep-find-ignored-files
-				     " --exclude=")))
+                       (when-let ((ignores (grep-find-ignored-files dir)))
+			 (concat " --exclude="
+				 (mapconcat
+                                  (lambda (ignore)
+                                    (shell-quote-argument ignore grep-quoting-style))
+                                  ignores
+                                  " --exclude=")))
 		       (and (eq grep-use-directories-skip t)
 			    '("--directories=skip"))))
 	(when command
@@ -1353,13 +1359,8 @@ rgrep
 	      (setq default-directory dir)))))))
 
 (defun rgrep-find-ignored-directories (dir)
-  "Return the list of ignored directories applicable to `dir'."
-  (delq nil (mapcar
-             (lambda (ignore)
-               (cond ((stringp ignore) ignore)
-                     ((consp ignore)
-                      (and (funcall (car ignore) dir) (cdr ignore)))))
-             grep-find-ignored-directories)))
+  "Return the list of ignored directories applicable to DIR."
+  (grep--filter-list-by-dir grep-find-ignored-directories dir))
 
 (defun rgrep-default-command (regexp files dir)
   "Compute the command for \\[rgrep] to use by default."
@@ -1377,37 +1378,31 @@ rgrep-default-command
            (shell-quote-argument ")" grep-quoting-style))
    dir
    (concat
-    (and grep-find-ignored-directories
-         (concat "-type d "
-                 (shell-quote-argument "(" grep-quoting-style)
-                 ;; we should use shell-quote-argument here
-                 " -path "
-                 (mapconcat
-                  (lambda (d)
-                    (shell-quote-argument (concat "*/" d) grep-quoting-style))
-                  (rgrep-find-ignored-directories dir)
-                  " -o -path ")
-                 " "
-                 (shell-quote-argument ")" grep-quoting-style)
-                 " -prune -o "))
-    (and grep-find-ignored-files
-         (concat (shell-quote-argument "!" grep-quoting-style) " -type d "
-                 (shell-quote-argument "(" grep-quoting-style)
-                 ;; we should use shell-quote-argument here
-                 " -name "
-                 (mapconcat
-                  (lambda (ignore)
-                    (cond ((stringp ignore)
-                           (shell-quote-argument ignore grep-quoting-style))
-                          ((consp ignore)
-                           (and (funcall (car ignore) dir)
-                                (shell-quote-argument
-                                 (cdr ignore) grep-quoting-style)))))
-                  grep-find-ignored-files
-                  " -o -name ")
-                 " "
-                 (shell-quote-argument ")" grep-quoting-style)
-                 " -prune -o ")))))
+    (when-let ((ignored-dirs (rgrep-find-ignored-directories dir)))
+      (concat "-type d "
+              (shell-quote-argument "(" grep-quoting-style)
+              ;; we should use shell-quote-argument here
+              " -path "
+              (mapconcat
+               (lambda (d)
+                 (shell-quote-argument (concat "*/" d) grep-quoting-style))
+               ignored-dirs
+               " -o -path ")
+              " "
+              (shell-quote-argument ")" grep-quoting-style)
+              " -prune -o "))
+    (when-let ((ignored-files (grep-find-ignored-files dir)))
+      (concat (shell-quote-argument "!" grep-quoting-style) " -type d "
+              (shell-quote-argument "(" grep-quoting-style)
+              ;; we should use shell-quote-argument here
+              " -name "
+              (mapconcat
+               (lambda (ignore) (shell-quote-argument ignore grep-quoting-style))
+               ignored-files
+               " -o -name ")
+              " "
+              (shell-quote-argument ")" grep-quoting-style)
+              " -prune -o ")))))
 
 (defun grep-find-toggle-abbreviation ()
   "Toggle showing the hidden part of rgrep/lgrep/zrgrep command line."
diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el
index abb11a1f2c5..294938c42ca 100644
--- a/lisp/progmodes/project.el
+++ b/lisp/progmodes/project.el
@@ -295,7 +295,7 @@ project-name
 Nominally unique, but not enforced."
   (file-name-nondirectory (directory-file-name (project-root project))))
 
-(cl-defgeneric project-ignores (_project _dir)
+(cl-defgeneric project-ignores (_project dir)
   "Return the list of glob patterns to ignore inside DIR.
 Patterns can match both regular files and directories.
 To root an entry, start it with `./'.  To match directories only,
@@ -305,12 +305,16 @@ project-ignores
   ;; TODO: Support whitelist entries.
   (require 'grep)
   (defvar grep-find-ignored-files)
+  (declare-function grep-find-ignored-files "grep" (dir))
+  (debug)
   (nconc
    (mapcar
     (lambda (dir)
       (concat dir "/"))
     vc-directory-exclusion-list)
-   grep-find-ignored-files))
+   (if (fboundp 'grep-find-ignored-files)
+       (grep-find-ignored-files dir)
+     grep-find-ignored-files)))
 
 (defun project--file-completion-table (all-files)
   (lambda (string pred action)
-- 
2.39.3