Repository: clojure-lsp/clojure-lsp (clojure)

Issue:
Support `doto`
clojure-lsp doesn't appear to be knowledgeable about `doto`, which inserts its first argument as the first argument to all subsequent forms:

![2020-04-14-184449_1207x272_scrot](https://user-images.githubusercontent.com/4909079/79281125-2632d280-7e80-11ea-8095-9b8654abe58a.png)

(The relevant functions above all have arities that are 1 greater than the number clojure-lsp is complaining about, so when `session` gets added in by `doto`, the number of arguments is correct.)

Pull request:
Closes https://github.com/snoe/clojure-lsp/issues/126

Patch:
diff --git a/src/clojure_lsp/crawler.clj b/src/clojure_lsp/crawler.clj
index b4364001c..6369dbe93 100644
--- a/src/clojure_lsp/crawler.clj
+++ b/src/clojure_lsp/crawler.clj
@@ -107,7 +107,7 @@
       {:range (shared/->range call-site)
        :code :wrong-arity
        :message (let [plural (not= argc 1)]
-                  (format "No overload %s for %d argument%s"
+                  (format "No overload for '%s' with %d argument%s"
                           (:str call-site) argc (if plural "s" "")))
        :severity 1})))
 
diff --git a/src/clojure_lsp/parser.clj b/src/clojure_lsp/parser.clj
index f8f06c8ed..7497a889d 100644
--- a/src/clojure_lsp/parser.clj
+++ b/src/clojure_lsp/parser.clj
@@ -749,7 +749,9 @@
                   'clojure.core/some-> handle-thread
                   'clojure.core/some->> handle-thread
                   'clojure.core/cond-> handle-cond-thread
-                  'clojure.core/cond->> handle-cond-thread}]
+                  'clojure.core/cond->> handle-cond-thread
+                  'clojure.core/doto handle-thread}]
+
     (merge handlers (medley/map-keys #(symbol "cljs.core" (name %)) handlers))))
 
 (def default-macro-defs


Test patch:
diff --git a/test/clojure_lsp/handlers_test.clj b/test/clojure_lsp/handlers_test.clj
index c6f8732b1..db43fbed4 100644
--- a/test/clojure_lsp/handlers_test.clj
+++ b/test/clojure_lsp/handlers_test.clj
@@ -88,14 +88,14 @@
                                                                      (foo 1 ['a 'b])
                                                                      (foo 1 2 3 {:k 1 :v 2})" :clj {})}})
       (let [usages (crawler/find-diagnostics #{} "file://a.clj" (get-in @db/db [:file-envs "file://a.clj"]))]
-        (is (= ["No overload foo for 3 arguments"
-                "No overload baz for 1 argument"
-                "No overload bar for 0 arguments"
-                "No overload foo for 3 arguments"
-                "No overload foo for 0 arguments"
-                "No overload foo for 4 arguments"]
+        (is (= ["No overload for 'foo' with 3 arguments"
+                "No overload for 'baz' with 1 argument"
+                "No overload for 'bar' with 0 arguments"
+                "No overload for 'foo' with 3 arguments"
+                "No overload for 'foo' with 0 arguments"
+                "No overload for 'foo' with 4 arguments"]
                (map :message usages)))))
-    (testing "for threading macros"
+    (testing "for threading macros and doto"
       (reset! db/db {:file-envs {"file://a.clj" (parser/find-usages "(defn foo ([x] x) ([x y z] (z x y)))
                                                                      (defn bar [] :bar)
                                                                      (defn baz [arg & rest] (apply arg rest))
@@ -112,12 +112,18 @@
                                                                      (cond-> 0
                                                                       int? (bar :a :b)
                                                                       false (foo)
-                                                                      :else (baz 3))" :clj {})}})
+                                                                      :else (baz 3))
+                                                                     (doto 1
+                                                                       (foo)
+                                                                       (foo 1)
+                                                                       (bar))" :clj {})}})
       (let [usages (crawler/find-diagnostics #{} "file://a.clj" (get-in @db/db [:file-envs "file://a.clj"]))]
-        (is (= ["No overload foo for 2 arguments"
-                "No overload bar for 1 argument"
-                "No overload bar for 1 argument"
-                "No overload bar for 3 arguments"]
+        (is (= ["No overload for 'foo' with 2 arguments"
+                "No overload for 'bar' with 1 argument"
+                "No overload for 'bar' with 1 argument"
+                "No overload for 'bar' with 3 arguments"
+                "No overload for 'foo' with 2 arguments"
+                "No overload for 'bar' with 1 argument"]
                (map :message usages)))))
     (testing "with annotations"
       (reset! db/db {:file-envs {"file://a.clj" (parser/find-usages "(defn foo {:added \"1.0\"} [x] (inc x))
@@ -127,7 +133,7 @@
                                                                      (bar :a)
                                                                      (bar :a :b)" :clj {})}})
       (let [usages (crawler/find-diagnostics #{} "file://a.clj" (get-in @db/db [:file-envs "file://a.clj"]))]
-        (is (= ["No overload foo for 2 arguments"]
+        (is (= ["No overload for 'foo' with 2 arguments"]
                (map :message usages)))))
     (testing "for meta arglists"
       (reset! db/db {:file-envs {"file://a.clj" (parser/find-usages "(def
@@ -139,8 +145,8 @@
                                                                      (foo)
                                                                      (foo (foo :a :b))" :clj {})}})
       (let [usages (crawler/find-diagnostics #{} "file://a.clj" (get-in @db/db [:file-envs "file://a.clj"]))]
-        (is (= ["No overload foo for 0 arguments"
-                "No overload foo for 2 arguments"]
+        (is (= ["No overload for 'foo' with 0 arguments"
+                "No overload for 'foo' with 2 arguments"]
                (map :message usages)))))
     (testing "for schema defs"
       (reset! db/db {:file-envs {"file://a.clj" (parser/find-usages "(ns user (:require [schema.core :as s]))
@@ -152,8 +158,8 @@
                                                                      (foo 1)" :clj {})}})
       (let [usages (crawler/find-diagnostics #{} "file://a.clj" (get-in @db/db [:file-envs "file://a.clj"]))]
         (is (= ["Unused namespace: user"
-                "No overload foo for 0 arguments"
-                "No overload foo for 1 argument"]
+                "No overload for 'foo' with 0 arguments"
+                "No overload for 'foo' with 1 argument"]
                (map :message usages)))))
     (testing "for ignore-arity? macros"
       (let [usages  (parser/find-usages "(ns user (:require [schema.core :as s])) (s/defn foo [x :- int?] x) (foo 1 2 3)" :clj