Skip to content

Commit 5baa06e

Browse files
committed
Mark put-clojure-indent safe for use in .dir-locals.el, etc.
Add a put-clojure-indent form validator and attach it as a safe-local-eval-function property so that safe invocations won't trigger open-file prompts when used in local variables, or when added to .dir-locals.el like this: ((clojure-mode (eval . (put-clojure-indent 'defrecord '(2 :form :form (1)))))) For now, only support specs specified as lists, not vectors.
1 parent 33f267a commit 5baa06e

File tree

2 files changed

+121
-0
lines changed

2 files changed

+121
-0
lines changed

Diff for: clojure-mode.el

+47
Original file line numberDiff line numberDiff line change
@@ -1544,6 +1544,53 @@ This function also returns nil meaning don't specify the indentation."
15441544
"Instruct `clojure-indent-function' to indent the body of SYM by INDENT."
15451545
(put sym 'clojure-indent-function indent))
15461546

1547+
(defun clojure--maybe-quoted-symbol-p (x)
1548+
"Check that X is either a symbol or a quoted symbol like :foo or 'foo."
1549+
(or (symbolp x)
1550+
(and (listp x)
1551+
(= 2 (length x))
1552+
(eq 'quote (car x))
1553+
(symbolp (cadr x)))))
1554+
1555+
(defun clojure--valid-unquoted-indent-spec-p (spec)
1556+
"Check that the indentation SPEC is valid.
1557+
Validate it with respect to
1558+
https://docs.cider.mx/cider/indent_spec.html e.g. (2 :form
1559+
:form (1)))."
1560+
(or (integerp spec)
1561+
(memq spec '(:form :defn))
1562+
(and (listp spec)
1563+
(not (null spec))
1564+
(or (integerp (car spec))
1565+
(memq (car spec) '(:form :defn)))
1566+
(seq-every-p 'clojure--valid-unquoted-indent-spec-p (cdr spec)))))
1567+
1568+
(defun clojure--valid-indent-spec-p (spec)
1569+
"Check that the indentation SPEC (quoted if a list) is valid.
1570+
Validate it with respect to
1571+
https://docs.cider.mx/cider/indent_spec.html e.g. (2 :form
1572+
:form (1)))."
1573+
(or (integerp spec)
1574+
(and (keywordp spec) (memq spec '(:form :defn)))
1575+
(and (listp spec)
1576+
(= 2 (length spec))
1577+
(eq 'quote (car spec))
1578+
(clojure--valid-unquoted-indent-spec-p (cadr spec)))))
1579+
1580+
(defun clojure--valid-put-clojure-indent-call-p (exp)
1581+
"Check that EXP is a valid `put-clojure-indent' expression.
1582+
For example: (put-clojure-indent 'defrecord '(2 :form :form (1))."
1583+
(unless (and (listp exp)
1584+
(= 3 (length exp))
1585+
(eq 'put-clojure-indent (car exp))
1586+
(clojure--maybe-quoted-symbol-p (cadr exp))
1587+
(clojure--valid-indent-spec-p (caddr exp)))
1588+
(error "Unrecognized put-clojure-indent call: %s" exp))
1589+
t)
1590+
1591+
(put 'put-clojure-indent 'safe-local-eval-function
1592+
'clojure--valid-put-clojure-indent-call-p)
1593+
15471594
(defmacro define-clojure-indent (&rest kvs)
15481595
"Call `put-clojure-indent' on a series, KVS."
15491596
`(progn

Diff for: test/clojure-mode-safe-eval-test.el

+74
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
;;; clojure-mode-safe-eval-test.el --- Clojure Mode: safe eval test suite -*- lexical-binding: t; -*-
2+
3+
;; Copyright (C) 2014-2021 Bozhidar Batsov <[email protected]>
4+
;; Copyright (C) 2021 Rob Browning <[email protected]>
5+
6+
;; This file is not part of GNU Emacs.
7+
8+
;; This program is free software; you can redistribute it and/or modify
9+
;; it under the terms of the GNU General Public License as published by
10+
;; the Free Software Foundation, either version 3 of the License, or
11+
;; (at your option) any later version.
12+
13+
;; This program is distributed in the hope that it will be useful,
14+
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
;; GNU General Public License for more details.
17+
18+
;; You should have received a copy of the GNU General Public License
19+
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
20+
21+
;;; Commentary:
22+
23+
;; The safe eval test suite of Clojure Mode
24+
25+
;;; Code:
26+
(require 'clojure-mode)
27+
(require 'buttercup)
28+
29+
(describe "put-clojure-indent safe-local-eval-function property"
30+
(it "should be set to clojure--valid-put-clojure-indent-call-p"
31+
(expect (get 'put-clojure-indent 'safe-local-eval-function)
32+
:to-be 'clojure--valid-put-clojure-indent-call-p)))
33+
34+
(describe "clojure--valid-put-clojure-indent-call-p"
35+
(it "should approve valid forms"
36+
(expect (clojure--valid-put-clojure-indent-call-p
37+
'(put-clojure-indent 'foo 1)))
38+
(expect (clojure--valid-put-clojure-indent-call-p
39+
'(put-clojure-indent 'foo :defn)))
40+
(expect (clojure--valid-put-clojure-indent-call-p
41+
'(put-clojure-indent 'foo :form)))
42+
(expect (clojure--valid-put-clojure-indent-call-p
43+
'(put-clojure-indent 'foo '(1))))
44+
(expect (clojure--valid-put-clojure-indent-call-p
45+
'(put-clojure-indent 'foo '(:defn))))
46+
(expect (clojure--valid-put-clojure-indent-call-p
47+
'(put-clojure-indent 'foo '(:form))))
48+
(expect (clojure--valid-put-clojure-indent-call-p
49+
'(put-clojure-indent 'foo '(1 1))))
50+
(expect (clojure--valid-put-clojure-indent-call-p
51+
'(put-clojure-indent 'foo '(2 :form :form (1))))))
52+
(it "should reject invalid forms"
53+
(expect (clojure--valid-put-clojure-indent-call-p
54+
'(put-clojure-indent 1 1))
55+
:to-throw 'error)
56+
(expect (clojure--valid-put-clojure-indent-call-p
57+
'(put-clojure-indent 'foo :foo))
58+
:to-throw 'error)
59+
(expect (clojure--valid-put-clojure-indent-call-p
60+
'(put-clojure-indent 'foo (:defn)))
61+
:to-throw 'error)
62+
(expect (clojure--valid-put-clojure-indent-call-p
63+
'(put-clojure-indent 'foo '(:foo)))
64+
:to-throw 'error)
65+
(expect (clojure--valid-put-clojure-indent-call-p
66+
'(put-clojure-indent 'foo '(1 :foo)))
67+
:to-throw 'error)
68+
(expect (clojure--valid-put-clojure-indent-call-p
69+
'(put-clojure-indent 'foo '(1 "foo")))
70+
:to-throw 'error)))
71+
72+
(provide 'clojure-mode-safe-eval-test)
73+
74+
;;; clojure-mode-safe-eval-test.el ends here

0 commit comments

Comments
 (0)