branch: elpa/flycheck
commit 422e216065d554ef1d4f6613891dd1bab84a86bc
Merge: 77c100c380 43be6c3110
Author: Bozhidar Batsov <[email protected]>
Commit: GitHub <[email protected]>
Merge pull request #2146 from takeokunn/fix/issue-2144
Rewrite org-lint checker to run in current Emacs process
---
CHANGES.rst | 3 ++
doc/languages.rst | 18 +++-----
flycheck.el | 92 ++++++++++++++--------------------------
test/specs/test-checker-api.el | 12 +++---
test/specs/test-customization.el | 15 ++++---
5 files changed, 57 insertions(+), 83 deletions(-)
diff --git a/CHANGES.rst b/CHANGES.rst
index e247dc35eb..2f8730959a 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -17,6 +17,9 @@ Bugs fixed
- [#2086]: Fix the name of the PyMarkdown config.
- [#2036]: Fix ``awk-gawk`` checker passing spurious quotes to ``gawk
--source``.
+- [#2144]: Rewrite ``org-lint`` checker to run in the current Emacs process
+ instead of a ``-Q --batch`` subprocess. This eliminates false "Unknown source
+ block language" warnings for languages from external packages.
35.0 (2025-04-23)
======================
diff --git a/doc/languages.rst b/doc/languages.rst
index b2ad07558e..eea8661c2d 100644
--- a/doc/languages.rst
+++ b/doc/languages.rst
@@ -924,8 +924,8 @@ to view the docstring of the syntax checker. Likewise, you
may use
An Org mode syntax and style checker using ``org-lint``.
- The checker runs ``org-lint`` in an Emacs subprocess to detect issues
such
- as:
+ The checker runs ``org-lint`` in the current Emacs process to detect
+ issues such as:
- Invalid links
- Dead links
@@ -934,11 +934,9 @@ to view the docstring of the syntax checker. Likewise,
you may use
- Special characters in links
- And more...
- The checker automatically inherits your Org mode configuration, including
- ``org-directory`` and ``org-id-locations-file``. Note that ``load-path``
is
- not inherited for security reasons to prevent potential code injection.
- Org should be installed in a standard location that Emacs can find
without
- a custom ``load-path``.
+ Because the checker runs in the current Emacs process, it has access to
+ all installed packages and user configuration, avoiding false positives
+ for source block languages provided by external packages.
The checker is enabled by default when ``org-lint`` is available (Org
mode
9.0 or later).
@@ -946,12 +944,6 @@ to view the docstring of the syntax checker. Likewise,
you may use
See the ``org-lint`` documentation in Org mode for details about the
checks
performed.
- .. note::
-
- The checker does not inherit ``org-id-locations`` because this
variable
- can contain thousands of entries and exceed shell argument limits. The
- ``org-id-locations-file`` is used instead.
-
.. supported-language:: Opam
.. syntax-checker:: opam
diff --git a/flycheck.el b/flycheck.el
index f290da5aba..32f162e875 100644
--- a/flycheck.el
+++ b/flycheck.el
@@ -91,6 +91,7 @@
;; Tell the byte compiler about autoloaded functions from packages
(declare-function pkg-info-version-info "pkg-info" (package))
+(declare-function org-lint "org-lint" (&optional arg))
;;; Customization
@@ -8979,23 +8980,10 @@ Variables are taken from
`flycheck-emacs-lisp-checkdoc-variables'."
,@(seq-map (lambda (opt) `(setq-default ,opt ',(symbol-value opt)))
(seq-filter #'boundp flycheck-emacs-lisp-checkdoc-variables))))
-(defconst flycheck-org-lint-variables
- '(org-directory
- org-id-locations-file) ; File path only, not contents
- "Variables inherited by the org-lint subprocess.
-
-Note: We do NOT include `load-path' to prevent potential code injection
-and information disclosure. Org should be installed in a standard
-location that Emacs can find without a custom load-path.
-
-We also do not include `org-id-locations' because it can contain
-thousands of entries and exceed shell argument limits (ARG_MAX).")
-
-(defun flycheck-org-lint-variables-form ()
- "Make a sexp to pass relevant variables to an org-lint subprocess."
- `(progn
- ,@(seq-map (lambda (opt) `(setq-default ,opt ',(symbol-value opt)))
- (seq-filter #'boundp flycheck-org-lint-variables))))
+(defun flycheck-org-lint-available-p ()
+ "Check if org-lint is available."
+ (and (fboundp 'org-lint)
+ (require 'org nil 'no-error)))
(flycheck-define-checker emacs-lisp-checkdoc
"An Emacs Lisp style checker using CheckDoc.
@@ -9011,61 +8999,47 @@ The checker runs `checkdoc-current-buffer'."
:modes (emacs-lisp-mode)
:enabled flycheck--emacs-lisp-checkdoc-enabled-p)
-(defconst flycheck-org-lint-form
- (flycheck-prepare-emacs-lisp-form
- (with-demoted-errors "Org-lint error: %S"
- (require 'org)
- (let ((source (car command-line-args-left))
- (process-default-directory default-directory))
- (with-temp-buffer
- (insert-file-contents source 'visit)
- (setq buffer-file-name source)
- (setq default-directory process-default-directory)
- (delay-mode-hooks (org-mode))
- (setq delayed-mode-hooks nil)
- (dolist (err (org-lint))
- (pcase err
- (`(,_n [,line ,_trust ,desc ,_checker])
- (princ (format "%s:%s: %s\n" source line desc)))
- (_
- (princ (format "%s:1: Unexpected org-lint format: %S\n" source
err))))))))))
-
-(defun flycheck-org-lint-available-p ()
- "Check if org-lint is available."
- (and (fboundp 'org-lint)
- (require 'org nil 'no-error)))
-
(dolist (checker '(emacs-lisp emacs-lisp-checkdoc))
(setf (car (flycheck-checker-get checker 'command))
flycheck-this-emacs-executable))
-(flycheck-define-checker org-lint
+(flycheck-define-generic-checker 'org-lint
"An Org mode syntax checker using `org-lint'.
-The checker runs `org-lint' in an Emacs subprocess."
- :command ("emacs" (eval flycheck-emacs-args)
- "--eval" (eval (flycheck-sexp-to-string
- (flycheck-org-lint-variables-form)))
- "--eval" (eval flycheck-org-lint-form)
- "--" source)
- :error-patterns
- ((info line-start (file-name) ":" line ": " (message) line-end))
- :modes (org-mode)
- :enabled (lambda () (flycheck-org-lint-available-p))
+The checker runs `org-lint' in the current Emacs process, so it
+has access to all installed packages and user configuration."
+ :start (lambda (checker callback)
+ (condition-case err
+ (let ((errors
+ (delq nil
+ (mapcar
+ (lambda (e)
+ (pcase e
+ (`(,_n [,line ,_trust ,desc ,_checker])
+ (flycheck-error-new-at
+ line nil 'info desc
+ :checker checker))
+ (_
+ (flycheck-error-new-at
+ 1 nil 'warning
+ (format "Unexpected org-lint format: %S" e)
+ :checker checker))))
+ (org-lint)))))
+ (funcall callback 'finished errors))
+ (error (funcall callback 'errored
+ (error-message-string err)))))
+ :modes '(org-mode)
+ :enabled #'flycheck-org-lint-available-p
:verify (lambda (_)
(let ((org-version (when (require 'org nil 'no-error)
- (org-version))))
+ (org-version))))
(list (flycheck-verification-result-new
:label "Org-lint available"
:message (if (fboundp 'org-lint)
- (format "yes (Org %s)" org-version)
- "no")
+ (format "yes (Org %s)" org-version)
+ "no")
:face (if (fboundp 'org-lint) 'success 'warning))))))
-;; Set org-lint to use the current Emacs
-(setf (car (flycheck-checker-get 'org-lint 'command))
- flycheck-this-emacs-executable)
-
(defun flycheck-ember-template--check-for-config (&rest _ignored)
"Check the required config file is available up the file system."
(and buffer-file-name
diff --git a/test/specs/test-checker-api.el b/test/specs/test-checker-api.el
index 8f95a641a6..f7c7225cb7 100644
--- a/test/specs/test-checker-api.el
+++ b/test/specs/test-checker-api.el
@@ -78,14 +78,16 @@
(describe "flycheck-checker-executable"
(it "is-string"
(dolist (checker flycheck-checkers)
- (expect (stringp (flycheck-checker-executable checker))
:to-be-truthy)))
+ (when (flycheck-checker-get checker 'command)
+ (expect (stringp (flycheck-checker-executable checker))
:to-be-truthy))))
(it "override-the-executable"
(dolist (checker flycheck-checkers)
- (let ((variable (flycheck-checker-executable-variable checker)))
- (expect (eval `(let ((,variable "some-nice-executable"))
- (flycheck-checker-executable ',checker)))
- :to-equal "some-nice-executable")))))
+ (when (flycheck-checker-get checker 'command)
+ (let ((variable (flycheck-checker-executable-variable checker)))
+ (expect (eval `(let ((,variable "some-nice-executable"))
+ (flycheck-checker-executable ',checker)))
+ :to-equal "some-nice-executable"))))))
(describe "flycheck-checker-get"
(it "modes"
diff --git a/test/specs/test-customization.el b/test/specs/test-customization.el
index 70f8e9322e..83ffdd4c26 100644
--- a/test/specs/test-customization.el
+++ b/test/specs/test-customization.el
@@ -112,18 +112,21 @@
(it "is a special variable"
(dolist (checker flycheck-checkers)
- (let ((variable (flycheck-checker-executable-variable checker)))
- (expect (custom-variable-p variable) :to-be-truthy))))
+ (when (flycheck-checker-get checker 'command)
+ (let ((variable (flycheck-checker-executable-variable checker)))
+ (expect (custom-variable-p variable) :to-be-truthy)))))
(it "is customizable"
(dolist (checker flycheck-checkers)
- (let ((variable (flycheck-checker-executable-variable checker)))
- (expect (custom-variable-p variable) :to-be-truthy))))
+ (when (flycheck-checker-get checker 'command)
+ (let ((variable (flycheck-checker-executable-variable checker)))
+ (expect (custom-variable-p variable) :to-be-truthy)))))
(it "defaults to nil"
(dolist (checker flycheck-checkers)
- (let ((variable (flycheck-checker-executable-variable checker)))
- (expect (null (symbol-value variable)) :to-be-truthy)))))
+ (when (flycheck-checker-get checker 'command)
+ (let ((variable (flycheck-checker-executable-variable checker)))
+ (expect (null (symbol-value variable)) :to-be-truthy))))))
(describe "flycheck-keymap-prefix"