├── .clj-kondo └── config.edn ├── .gitignore ├── LICENSE ├── README.md ├── deps.edn ├── src └── clj_kondo │ ├── inspector.clj │ └── inspector │ └── impl.clj └── test └── clj_kondo └── inspector └── test_specs.clj /.clj-kondo/config.edn: -------------------------------------------------------------------------------- 1 | {:config-paths ["configs/inspector"]} 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/emacs,clojure 3 | 4 | ### Clojure ### 5 | pom.xml 6 | pom.xml.asc 7 | *.jar 8 | *.class 9 | /lib/ 10 | /classes/ 11 | /target/ 12 | /checkouts/ 13 | .lein-deps-sum 14 | .lein-repl-history 15 | .lein-plugins/ 16 | .lein-failures 17 | .nrepl-port 18 | .cpcache/ 19 | 20 | ### Emacs ### 21 | # -*- mode: gitignore; -*- 22 | *~ 23 | \#*\# 24 | /.emacs.desktop 25 | /.emacs.desktop.lock 26 | *.elc 27 | auto-save-list 28 | tramp 29 | .\#* 30 | 31 | # Org-mode 32 | .org-id-locations 33 | *_archive 34 | 35 | # flymake-mode 36 | *_flymake.* 37 | 38 | # eshell files 39 | /eshell/history 40 | /eshell/lastdir 41 | 42 | # elpa packages 43 | /elpa/ 44 | 45 | # reftex files 46 | *.rel 47 | 48 | # AUCTeX auto folder 49 | /auto/ 50 | 51 | # cask packages 52 | .cask/ 53 | dist/ 54 | 55 | # Flycheck 56 | flycheck_*.el 57 | 58 | # server auth directory 59 | /server/ 60 | 61 | # projectiles files 62 | .projectile 63 | 64 | # directory configuration 65 | .dir-locals.el 66 | 67 | 68 | # End of https://www.gitignore.io/api/emacs,clojure 69 | **/.clj-kondo/.cache 70 | .clj-kondo/configs/inspector 71 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Eclipse Public License - v 1.0 2 | 3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 4 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 5 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 6 | 7 | 1. DEFINITIONS 8 | 9 | "Contribution" means: 10 | 11 | a) in the case of the initial Contributor, the initial code and documentation 12 | distributed under this Agreement, and 13 | b) in the case of each subsequent Contributor: 14 | i) changes to the Program, and 15 | ii) additions to the Program; 16 | 17 | where such changes and/or additions to the Program originate from and are 18 | distributed by that particular Contributor. A Contribution 'originates' from 19 | a Contributor if it was added to the Program by such Contributor itself or 20 | anyone acting on such Contributor's behalf. Contributions do not include 21 | additions to the Program which: (i) are separate modules of software 22 | distributed in conjunction with the Program under their own license 23 | agreement, and (ii) are not derivative works of the Program. 24 | 25 | "Contributor" means any person or entity that distributes the Program. 26 | 27 | "Licensed Patents" mean patent claims licensable by a Contributor which are 28 | necessarily infringed by the use or sale of its Contribution alone or when 29 | combined with the Program. 30 | 31 | "Program" means the Contributions distributed in accordance with this Agreement. 32 | 33 | "Recipient" means anyone who receives the Program under this Agreement, 34 | including all Contributors. 35 | 36 | 2. GRANT OF RIGHTS 37 | a) Subject to the terms of this Agreement, each Contributor hereby grants 38 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 39 | reproduce, prepare derivative works of, publicly display, publicly perform, 40 | distribute and sublicense the Contribution of such Contributor, if any, and 41 | such derivative works, in source code and object code form. 42 | b) Subject to the terms of this Agreement, each Contributor hereby grants 43 | Recipient a non-exclusive, worldwide, royalty-free patent license under 44 | Licensed Patents to make, use, sell, offer to sell, import and otherwise 45 | transfer the Contribution of such Contributor, if any, in source code and 46 | object code form. This patent license shall apply to the combination of the 47 | Contribution and the Program if, at the time the Contribution is added by 48 | the Contributor, such addition of the Contribution causes such combination 49 | to be covered by the Licensed Patents. The patent license shall not apply 50 | to any other combinations which include the Contribution. No hardware per 51 | se is licensed hereunder. 52 | c) Recipient understands that although each Contributor grants the licenses to 53 | its Contributions set forth herein, no assurances are provided by any 54 | Contributor that the Program does not infringe the patent or other 55 | intellectual property rights of any other entity. Each Contributor 56 | disclaims any liability to Recipient for claims brought by any other entity 57 | based on infringement of intellectual property rights or otherwise. As a 58 | condition to exercising the rights and licenses granted hereunder, each 59 | Recipient hereby assumes sole responsibility to secure any other 60 | intellectual property rights needed, if any. For example, if a third party 61 | patent license is required to allow Recipient to distribute the Program, it 62 | is Recipient's responsibility to acquire that license before distributing 63 | the Program. 64 | d) Each Contributor represents that to its knowledge it has sufficient 65 | copyright rights in its Contribution, if any, to grant the copyright 66 | license set forth in this Agreement. 67 | 68 | 3. REQUIREMENTS 69 | 70 | A Contributor may choose to distribute the Program in object code form under its 71 | own license agreement, provided that: 72 | 73 | a) it complies with the terms and conditions of this Agreement; and 74 | b) its license agreement: 75 | i) effectively disclaims on behalf of all Contributors all warranties and 76 | conditions, express and implied, including warranties or conditions of 77 | title and non-infringement, and implied warranties or conditions of 78 | merchantability and fitness for a particular purpose; 79 | ii) effectively excludes on behalf of all Contributors all liability for 80 | damages, including direct, indirect, special, incidental and 81 | consequential damages, such as lost profits; 82 | iii) states that any provisions which differ from this Agreement are offered 83 | by that Contributor alone and not by any other party; and 84 | iv) states that source code for the Program is available from such 85 | Contributor, and informs licensees how to obtain it in a reasonable 86 | manner on or through a medium customarily used for software exchange. 87 | 88 | When the Program is made available in source code form: 89 | 90 | a) it must be made available under this Agreement; and 91 | b) a copy of this Agreement must be included with each copy of the Program. 92 | Contributors may not remove or alter any copyright notices contained within 93 | the Program. 94 | 95 | Each Contributor must identify itself as the originator of its Contribution, if 96 | any, in a manner that reasonably allows subsequent Recipients to identify the 97 | originator of the Contribution. 98 | 99 | 4. COMMERCIAL DISTRIBUTION 100 | 101 | Commercial distributors of software may accept certain responsibilities with 102 | respect to end users, business partners and the like. While this license is 103 | intended to facilitate the commercial use of the Program, the Contributor who 104 | includes the Program in a commercial product offering should do so in a manner 105 | which does not create potential liability for other Contributors. Therefore, if 106 | a Contributor includes the Program in a commercial product offering, such 107 | Contributor ("Commercial Contributor") hereby agrees to defend and indemnify 108 | every other Contributor ("Indemnified Contributor") against any losses, damages 109 | and costs (collectively "Losses") arising from claims, lawsuits and other legal 110 | actions brought by a third party against the Indemnified Contributor to the 111 | extent caused by the acts or omissions of such Commercial Contributor in 112 | connection with its distribution of the Program in a commercial product 113 | offering. The obligations in this section do not apply to any claims or Losses 114 | relating to any actual or alleged intellectual property infringement. In order 115 | to qualify, an Indemnified Contributor must: a) promptly notify the Commercial 116 | Contributor in writing of such claim, and b) allow the Commercial Contributor to 117 | control, and cooperate with the Commercial Contributor in, the defense and any 118 | related settlement negotiations. The Indemnified Contributor may participate in 119 | any such claim at its own expense. 120 | 121 | For example, a Contributor might include the Program in a commercial product 122 | offering, Product X. That Contributor is then a Commercial Contributor. If that 123 | Commercial Contributor then makes performance claims, or offers warranties 124 | related to Product X, those performance claims and warranties are such 125 | Commercial Contributor's responsibility alone. Under this section, the 126 | Commercial Contributor would have to defend claims against the other 127 | Contributors related to those performance claims and warranties, and if a court 128 | requires any other Contributor to pay any damages as a result, the Commercial 129 | Contributor must pay those damages. 130 | 131 | 5. NO WARRANTY 132 | 133 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN 134 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR 135 | IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, 136 | NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each 137 | Recipient is solely responsible for determining the appropriateness of using and 138 | distributing the Program and assumes all risks associated with its exercise of 139 | rights under this Agreement , including but not limited to the risks and costs 140 | of program errors, compliance with applicable laws, damage to or loss of data, 141 | programs or equipment, and unavailability or interruption of operations. 142 | 143 | 6. DISCLAIMER OF LIABILITY 144 | 145 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 146 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 147 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST 148 | PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 149 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 150 | OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS 151 | GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 152 | 153 | 7. GENERAL 154 | 155 | If any provision of this Agreement is invalid or unenforceable under applicable 156 | law, it shall not affect the validity or enforceability of the remainder of the 157 | terms of this Agreement, and without further action by the parties hereto, such 158 | provision shall be reformed to the minimum extent necessary to make such 159 | provision valid and enforceable. 160 | 161 | If Recipient institutes patent litigation against any entity (including a 162 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 163 | (excluding combinations of the Program with other software or hardware) 164 | infringes such Recipient's patent(s), then such Recipient's rights granted under 165 | Section 2(b) shall terminate as of the date such litigation is filed. 166 | 167 | All Recipient's rights under this Agreement shall terminate if it fails to 168 | comply with any of the material terms or conditions of this Agreement and does 169 | not cure such failure in a reasonable period of time after becoming aware of 170 | such noncompliance. If all Recipient's rights under this Agreement terminate, 171 | Recipient agrees to cease use and distribution of the Program as soon as 172 | reasonably practicable. However, Recipient's obligations under this Agreement 173 | and any licenses granted by Recipient relating to the Program shall continue and 174 | survive. 175 | 176 | Everyone is permitted to copy and distribute copies of this Agreement, but in 177 | order to avoid inconsistency the Agreement is copyrighted and may only be 178 | modified in the following manner. The Agreement Steward reserves the right to 179 | publish new versions (including revisions) of this Agreement from time to time. 180 | No one other than the Agreement Steward has the right to modify this Agreement. 181 | The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation 182 | may assign the responsibility to serve as the Agreement Steward to a suitable 183 | separate entity. Each new version of the Agreement will be given a 184 | distinguishing version number. The Program (including Contributions) may always 185 | be distributed subject to the version of the Agreement under which it was 186 | received. In addition, after a new version of the Agreement is published, 187 | Contributor may elect to distribute the Program (including its Contributions) 188 | under the new version. Except as expressly stated in Sections 2(a) and 2(b) 189 | above, Recipient receives no rights or licenses to the intellectual property of 190 | any Contributor under this Agreement, whether expressly, by implication, 191 | estoppel or otherwise. All rights in the Program not expressly granted under 192 | this Agreement are reserved. 193 | 194 | This Agreement is governed by the laws of the State of New York and the 195 | intellectual property laws of the United States of America. No party to this 196 | Agreement will bring a legal action under this Agreement more than one year 197 | after the cause of action arose. Each party waives its rights to a jury trial in 198 | any resulting litigation. 199 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Clj-kondo inspector 2 | 3 | Experimental tool using a REPL-assisted static analysis approach. It turns [clojure.spec.alpha](https://github.com/clojure/spec.alpha) specs into [clj-kondo](https://github.com/borkdude/clj-kondo/) [type annotations](https://github.com/borkdude/clj-kondo/blob/master/doc/types.md) for linting. 4 | 5 | Very alpha, breaking changes will happen. Not ready for serious usage. Contributions welcome. 6 | 7 | ## Usage 8 | 9 | ``` clojure 10 | (require '[clj-kondo.inspector :as i]) 11 | (require '[your.specs]) 12 | (i/emit-types) 13 | ``` 14 | 15 | Add `"configs/inspector"` to your `:config-paths` in `/.clj-kondo/config.edn`. Requires clj-kondo 2020.09.09 or newer. 16 | 17 | ## Example 18 | 19 | From `test/clj_kondo/inspector/test_specs.clj`: 20 | 21 | ``` clojure 22 | (ns clj-kondo.inspector.test-specs 23 | (:require [clj-kondo.inspector :as i] 24 | [clojure.spec.alpha :as s])) 25 | 26 | (defn foo [x y] 27 | (+ x y)) 28 | 29 | (s/def ::x string?) 30 | (s/def ::y ::x) 31 | 32 | (s/fdef foo :args (s/cat :x int? :y ::y)) 33 | 34 | (comment (foo 1 :foo)) ;; Expected string, received keyword 35 | 36 | (defn bar [m] 37 | m) 38 | 39 | (s/def ::z string?) 40 | 41 | (s/fdef bar :args (s/cat :m (s/keys :req-un [::x ::y] 42 | :opt-un [::z]))) 43 | 44 | (comment (bar {:x "foo"}) ;; Missing required key :y 45 | (bar {:x 1 :y "foo"}) ;; Expected string, received positive integer 46 | (bar {:x "foo" :y "foo" :z 1}) ;; Expected string, received positive integer 47 | ) 48 | ``` 49 | 50 | ## License 51 | 52 | Copyright © 2020 Michiel Borkent 53 | 54 | Distributed under the EPL License, same as Clojure. See LICENSE. 55 | -------------------------------------------------------------------------------- /deps.edn: -------------------------------------------------------------------------------- 1 | {:deps {org.clojure/tools.cli {:mvn/version "1.0.194"}}} 2 | -------------------------------------------------------------------------------- /src/clj_kondo/inspector.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.inspector 2 | (:require [clj-kondo.inspector.impl :as impl] 3 | [clojure.java.io :as io] 4 | [clojure.tools.cli :refer [parse-opts]])) 5 | 6 | (defn emit-types [] 7 | (impl/emit-types)) 8 | 9 | (def cli-options 10 | []) 11 | 12 | (defn -main [& args] 13 | (let [_ (:options (parse-opts args cli-options))] 14 | (impl/emit-types) 15 | (println "Add" (.getPath (io/file "configs" "inspector")) 16 | "to :config-paths in .clj-kondo/config.edn to activate."))) 17 | -------------------------------------------------------------------------------- /src/clj_kondo/inspector/impl.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.inspector.impl 2 | (:require [clojure.java.io :as io] 3 | [clojure.pprint :as pprint] 4 | [clojure.spec.alpha :as s])) 5 | 6 | (def fn->k 7 | {'clojure.core/int? :int 8 | 'clojure.core/string? :string}) 9 | 10 | (declare resolve-spec) 11 | 12 | (defn cat* [& forms] 13 | (let [specs (take-nth 2 (rest forms)) 14 | resolved (map resolve-spec specs)] 15 | resolved)) 16 | 17 | (defn unqualify [k] 18 | (keyword (name k))) 19 | 20 | (defn keys* [& {:keys [:req-un :opt-un]}] 21 | (let [required (map resolve-spec req-un) 22 | optional (map resolve-spec opt-un)] 23 | {:op :keys 24 | :req (zipmap (map unqualify req-un) required) 25 | :opt (zipmap (map unqualify opt-un) optional)})) 26 | 27 | (defn dispatch [[op & args]] 28 | (case op 29 | clojure.spec.alpha/cat (apply cat* args) 30 | clojure.spec.alpha/keys (apply keys* args) 31 | nil)) 32 | 33 | (defn resolve-spec [x] 34 | (cond (qualified-keyword? x) 35 | (resolve-spec (s/form (s/spec x))) 36 | (seq? x) (dispatch x) 37 | (symbol? x) (fn->k x) 38 | :else x)) 39 | 40 | (defn emit-types [] 41 | (let [cfg-file (io/file ".clj-kondo" "configs" "inspector" "config.edn") 42 | config 43 | (reduce (fn [acc sym] 44 | (let [sym-ns (symbol (namespace sym)) 45 | sym-name (symbol (name sym)) 46 | [_fspec _:args args-spec] (s/form (s/get-spec sym))] 47 | (if (seq? args-spec) 48 | (let [arg-types (resolve-spec args-spec)] 49 | (if-not (every? nil? arg-types) 50 | (let [arity (count arg-types)] 51 | (assoc-in acc [:linters :type-mismatch :namespaces 52 | sym-ns sym-name :arities arity :args] arg-types)) 53 | acc)) 54 | acc))) 55 | {} 56 | (filter symbol? (keys (s/registry))))] 57 | ;; (pprint/pprint config) 58 | (io/make-parents cfg-file) 59 | (spit cfg-file config) 60 | config)) 61 | 62 | ;;;; Scratch 63 | 64 | (comment 65 | (emit-types) 66 | ) 67 | -------------------------------------------------------------------------------- /test/clj_kondo/inspector/test_specs.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.inspector.test-specs 2 | (:require [clj-kondo.inspector :as i] 3 | [clojure.spec.alpha :as s])) 4 | 5 | (defn foo [x y] 6 | (+ x y)) 7 | 8 | (s/def ::x string?) 9 | (s/def ::y ::x) 10 | 11 | (s/fdef foo :args (s/cat :x int? :y ::y)) 12 | 13 | (comment (foo 1 :foo)) ;; Expected string, received keyword 14 | 15 | (defn bar [m] 16 | m) 17 | 18 | (s/def ::z string?) 19 | 20 | (s/fdef bar :args (s/cat :m (s/keys :req-un [::x ::y] 21 | :opt-un [::z]))) 22 | 23 | (comment (bar {:x "foo"}) ;; Missing required key :y 24 | (bar {:x 1 :y "foo"}) ;; Expected string, received positive integer 25 | (bar {:x "foo" :y "foo" :z 1}) ;; Expected string, received positive integer 26 | ) 27 | 28 | ;;;; Scratch 29 | 30 | (comment 31 | (i/emit-types) 32 | ) 33 | --------------------------------------------------------------------------------