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)

Reply via email to