branch: elpa/cider
commit c7bd796bb564d29b7c7582f073378abc48fbfc98
Author: Bozhidar Batsov <[email protected]>
Commit: Bozhidar Batsov <[email protected]>
Stop nrepl-dict-merge from mutating a shared literal
When DICT1 was nil, `base' was bound to the quoted literal `\='(dict)',
which
`nrepl-dict-put' then mutated in place - permanently polluting that shared
constant, so every later nil-seeded merge in the session inherited its
contents. Real callers seed from nil (the REPL ns cache, eldoc
accumulation), so state leaked between them. Allocate a fresh dict instead.
---
lisp/nrepl-dict.el | 7 +++++--
test/nrepl-dict-tests.el | 9 ++++++++-
2 files changed, 13 insertions(+), 3 deletions(-)
diff --git a/lisp/nrepl-dict.el b/lisp/nrepl-dict.el
index b36073d46e..730949758f 100644
--- a/lisp/nrepl-dict.el
+++ b/lisp/nrepl-dict.el
@@ -130,10 +130,13 @@ FN must accept two arguments key and value."
(defun nrepl-dict-merge (dict1 dict2)
"Destructively merge DICT2 into DICT1.
Keys in DICT2 override those in DICT1."
- (let ((base (or dict1 '(dict))))
+ ;; Allocate a fresh dict when DICT1 is nil. Using the quoted literal
+ ;; `\='(dict)' here would be mutated in place by `nrepl-dict-put', polluting
+ ;; the shared constant for every later nil-seeded merge in the session.
+ (let ((base (or dict1 (nrepl-dict))))
(nrepl-dict-map (lambda (k v)
(nrepl-dict-put base k v))
- (or dict2 '(dict)))
+ (or dict2 (nrepl-dict)))
base))
(defun nrepl-dict-get-in (dict keys)
diff --git a/test/nrepl-dict-tests.el b/test/nrepl-dict-tests.el
index 854eb85617..3bfa236232 100644
--- a/test/nrepl-dict-tests.el
+++ b/test/nrepl-dict-tests.el
@@ -43,7 +43,14 @@
(it "handles nil values"
(expect (nrepl-dict-merge nil '(dict 1 3 "10" me)) :to-equal '(dict 1 3
"10" me))
- (expect (nrepl-dict-merge '(dict 1 3 "10" me) nil) :to-equal '(dict 1 3
"10" me))))
+ (expect (nrepl-dict-merge '(dict 1 3 "10" me) nil) :to-equal '(dict 1 3
"10" me)))
+
+ (it "does not leak state between nil-seeded merges"
+ ;; A nil DICT1 must yield a fresh dict, not a shared literal that later
+ ;; merges keep mutating.
+ (nrepl-dict-merge nil '(dict "x" 1))
+ (expect (nrepl-dict-merge nil nil) :to-equal '(dict))
+ (expect (nrepl-dict-merge nil '(dict "y" 2)) :to-equal '(dict "y" 2))))
(describe "nrepl-dict-contains"
:var (input)