diff --git a/.clj-kondo/config.edn b/.clj-kondo/config.edn new file mode 100644 index 0000000..377fe7e --- /dev/null +++ b/.clj-kondo/config.edn @@ -0,0 +1 @@ +{:lint-as {kibit.rules.util/defrules clj-kondo.lint-as/def-catch-all}} diff --git a/CHANGELOG.md b/CHANGELOG.md index b6af309..1bcf2cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. This change ## [Unreleased] +* Update to Clojure 1.11 to handle ##Inf etc. [#237](https://github.com/clj-commons/kibit/pull/237) +* Switch to borkdude/edamame for side-effect free parsing. [#235](https://github.com/clj-commons/kibit/pull/235), [#246](https://github.com/clj-commons/kibit/pull/246) +* Correctly gather options-spec require vectors as maps so we can check for :as and :as-alias. [#238](https://github.com/clj-commons/kibit/pull/238) +* Moved all of the test/resources files to a new corpus folder which isn't loaded by default on test runs. + ## 0.1.10 / 2024-05-09 * Fix scm information in generated pom.xml. diff --git a/corpus/as_alias.clj b/corpus/as_alias.clj new file mode 100644 index 0000000..fa7b635 --- /dev/null +++ b/corpus/as_alias.clj @@ -0,0 +1,5 @@ +(ns resources.as-alias + (:require + [foo.bar :a :b :as-alias fb])) + +::fb/example diff --git a/corpus/double_pound_reader_macros.clj b/corpus/double_pound_reader_macros.clj new file mode 100644 index 0000000..e797502 --- /dev/null +++ b/corpus/double_pound_reader_macros.clj @@ -0,0 +1 @@ +[##Inf ##-Inf ##NaN] diff --git a/test/resources/first.clj b/corpus/first.clj similarity index 100% rename from test/resources/first.clj rename to corpus/first.clj diff --git a/test/resources/fourth.txt b/corpus/fourth.txt similarity index 100% rename from test/resources/fourth.txt rename to corpus/fourth.txt diff --git a/test/resources/keyword_suggestions.clj b/corpus/keyword_suggestions.clj similarity index 100% rename from test/resources/keyword_suggestions.clj rename to corpus/keyword_suggestions.clj diff --git a/test/resources/keywords.clj b/corpus/keywords.clj similarity index 100% rename from test/resources/keywords.clj rename to corpus/keywords.clj diff --git a/corpus/namespaced_maps.clj b/corpus/namespaced_maps.clj new file mode 100644 index 0000000..f1bd4e9 --- /dev/null +++ b/corpus/namespaced_maps.clj @@ -0,0 +1 @@ +#:car{:make "Jeep" :model "Wrangler"} diff --git a/corpus/read_eval.clj b/corpus/read_eval.clj new file mode 100644 index 0000000..d30ae67 --- /dev/null +++ b/corpus/read_eval.clj @@ -0,0 +1 @@ +(if true (do #=(prn :hello :world!) :a)) diff --git a/test/resources/reader_conditionals.cljc b/corpus/reader_conditionals.cljc similarity index 60% rename from test/resources/reader_conditionals.cljc rename to corpus/reader_conditionals.cljc index 3ffb6e8..bc1ec71 100644 --- a/test/resources/reader_conditionals.cljc +++ b/corpus/reader_conditionals.cljc @@ -1,3 +1,5 @@ #?(:clj (+ 1 1) :cljs (+ 2 2) :default (+ 3 3)) + +{:a 1 :b 2 #?@(:clj [:c 3 :d 4])} diff --git a/test/resources/second.cljx b/corpus/second.cljx similarity index 100% rename from test/resources/second.cljx rename to corpus/second.cljx diff --git a/test/resources/sets.clj b/corpus/sets.clj similarity index 100% rename from test/resources/sets.clj rename to corpus/sets.clj diff --git a/test/resources/third.cljs b/corpus/third.cljs similarity index 100% rename from test/resources/third.cljs rename to corpus/third.cljs diff --git a/deps.edn b/deps.edn index 0280fb4..bf35086 100644 --- a/deps.edn +++ b/deps.edn @@ -1,13 +1,11 @@ {:paths ["src"] - :deps {org.clojure/clojure {:mvn/version "1.8.0"} + :deps {org.clojure/clojure {:mvn/version "1.11.1"} org.clojure/core.logic {:mvn/version "1.1.0"} org.clojure/tools.cli {:mvn/version "1.1.230"} - org.clojure/tools.reader {:mvn/version "1.4.2"} + borkdude/edamame {:mvn/version "1.4.25"} rewrite-clj/rewrite-clj {:mvn/version "1.1.47"}} - ;; At least Clojure v1.9 is required to run `clojure -X:`. Run this as - ;; `clojure -X:exec` or `clojure -X:exec -i -r markdown`, for example. - :aliases {:exec {:extra-deps {org.clojure/clojure {:mvn/version "1.9.0"} - org.babashka/cli {:mvn/version "0.7.51"}} + ;; Run this as `clojure -X:exec` or `clojure -X:exec -i -r markdown`, for example. + :aliases {:exec {:extra-deps {org.babashka/cli {:mvn/version "0.7.51"}} :exec-fn kibit.driver/exec :exec-args {:paths ["."]} :main-opts ["-m" "babashka.cli.exec"]}}} diff --git a/lein-kibit/project.clj b/lein-kibit/project.clj index 1102af8..0b26459 100644 --- a/lein-kibit/project.clj +++ b/lein-kibit/project.clj @@ -6,6 +6,7 @@ :url "http://www.eclipse.org/legal/epl-v10.html"} :dependencies [[org.clojure/tools.namespace "1.5.0"] [jonase/kibit ~(clojure.string/trim-newline (slurp "../resources/jonase/kibit/VERSION"))]] - :deploy-repositories [["releases" :clojars] + :deploy-repositories [["clojars" {:url "https://clojars.org/repo" + :sign-releases false}] ["snapshots" :clojars]] :eval-in-leiningen true) diff --git a/project.clj b/project.clj index 13d98d0..d08a41e 100644 --- a/project.clj +++ b/project.clj @@ -9,12 +9,11 @@ :url "https://github.com/clj-commons/kibit" :connection "scm:git:git://github.com/clj-commons/kibit.git" :developerConnection "scm:git:ssh://git@github.com/clj-commons/kibit.git"} - :dependencies [[org.clojure/clojure "1.8.0"] + :dependencies [[org.clojure/clojure "1.11.1"] [org.clojure/core.logic "1.1.0"] [org.clojure/tools.cli "1.1.230"] [rewrite-clj "1.1.47"] - [org.clojure/tools.reader "1.4.2"]] - :profiles {:dev {:resource-paths ["test/resources"]}} + [borkdude/edamame "1.4.25"]] :deploy-repositories [["clojars" {:url "https://clojars.org/repo" :sign-releases false}] ["snapshots" :clojars]] diff --git a/src/kibit/check.clj b/src/kibit/check.clj index 69b86b6..153d3fd 100644 --- a/src/kibit/check.clj +++ b/src/kibit/check.clj @@ -1,7 +1,6 @@ (ns kibit.check "Kibit's integration point and public API" (:require [clojure.java.io :as io] - [clojure.core.logic.unifier :as unifier] [kibit.core :as core] [kibit.rules :as core-rules] [kibit.reporters :as reporters] @@ -67,12 +66,13 @@ "Construct the canonical simplify-map given an expression and a simplified expression." [expr simplified-expr] - {:expr expr - :line (-> expr meta :line) - :column (-> expr meta :column) - :end-line (-> expr meta :end-line) - :end-column (-> expr meta :end-column) - :alt simplified-expr}) + (let [expr-meta (meta expr)] + {:expr expr + :line (:line expr-meta) + :column (:column expr-meta) + :end-line (:end-line expr-meta) + :end-column (:end-column expr-meta) + :alt simplified-expr})) ;; ### Guarding the check @@ -164,23 +164,19 @@ ;; The default resolution is overridden via the `merge` (defn check-expr - "" [expr & kw-opts] (let [{:keys [rules guard resolution]} (merge default-args {:resolution :toplevel} (apply hash-map kw-opts)) - rules (map unifier/prep rules) simplify-fn #((res->simplify resolution) % rules)] (check-aux expr simplify-fn guard))) (defn check-reader - "" [reader & kw-opts] (let [{:keys [rules guard resolution init-ns]} (merge default-args (apply hash-map kw-opts)) - rules (map unifier/prep rules) simplify-fn #((res->simplify resolution) % rules)] (keep #(check-aux % simplify-fn guard) ((res->read-seq resolution) reader init-ns)))) @@ -190,7 +186,6 @@ {(resolve '*default-data-reader-fn*) (fn [tag val] val)})) (defn check-file - "" [source-file & kw-opts] (let [{:keys [rules guard resolution reporter init-ns] :or {reporter reporters/cli-reporter}} diff --git a/src/kibit/check/reader.clj b/src/kibit/check/reader.clj index d835677..8a9cade 100644 --- a/src/kibit/check/reader.clj +++ b/src/kibit/check/reader.clj @@ -1,5 +1,5 @@ (ns kibit.check.reader - (:require [clojure.tools.reader :as reader]) + (:require [edamame.core :as e]) (:import [clojure.lang LineNumberingPushbackReader])) ;; Preprocessing @@ -69,8 +69,8 @@ (rest form)) (option-spec? form) - (let [[_ as? form-alias] form] - (deps-from-libspec prefix (first form) (when (= :as as?) form-alias))) + (let [opts (apply hash-map (next form))] + (deps-from-libspec prefix (first form) (or (:as opts) (:as-alias opts)))) (symbol? form) (list (with-meta @@ -142,6 +142,26 @@ into the namespace." (def eof (Object.)) +(defn- make-edamame-opts [alias-map] + (e/normalize-opts + {:all true + :read_eval true + :eof eof + :row-key :line + :col-key :column + :end-row-key :end-line + :end-col-key :end-column + :end-location true + :features #{:clj :cljs} + :read-cond :allow + :readers (fn reader [r] + (fn reader-value [v] + (list r v))) + :auto-resolve (fn [x] + (if (= :current x) + *ns* + (get alias-map x)))})) + (defn read-file "Generate a lazy sequence of top level forms from a LineNumberingPushbackReader" @@ -149,11 +169,11 @@ into the namespace." (let [ns (careful-refer (create-ns init-ns)) do-read (fn do-read [ns alias-map] (lazy-seq - (let [form (binding [*ns* ns - reader/*alias-map* (merge (ns-aliases ns) - (alias-map ns))] - (reader/read {:eof eof :read-cond :preserve} r)) - [ns? new-ns k] (when (sequential? form) form) + (let [form (binding [*ns* ns] + (e/parse-next r (make-edamame-opts + (merge (ns-aliases ns) + (alias-map ns))))) + [ns? new-ns] (when (sequential? form) form) new-ns (unquote-if-quoted new-ns) ns (if (and (symbol? new-ns) (#{'ns 'in-ns} ns?)) diff --git a/src/kibit/driver.clj b/src/kibit/driver.clj index 4098aac..e0add63 100644 --- a/src/kibit/driver.clj +++ b/src/kibit/driver.clj @@ -22,7 +22,7 @@ (defn ends-with? "Returns true if the java.io.File ends in any of the strings in coll" [file coll] - (some #(.endsWith (.getName ^File file) %) coll)) + (boolean (some #(.endsWith (.getName ^File file) %) coll))) (defn clojure-file? "Returns true if the java.io.File represents a Clojure source file. diff --git a/src/kibit/rules/arithmetic.clj b/src/kibit/rules/arithmetic.clj index c387fe2..59ef465 100644 --- a/src/kibit/rules/arithmetic.clj +++ b/src/kibit/rules/arithmetic.clj @@ -23,7 +23,4 @@ [(dec (Math/exp ?x)) (Math/expm1 ?x)] ;;ugly rounding tricks - [(long (+ ?x 0.5)) (Math/round ?x)] -) - - + [(long (+ ?x 0.5)) (Math/round ?x)]) diff --git a/src/kibit/rules/control_structures.clj b/src/kibit/rules/control_structures.clj index efecfe5..585d719 100644 --- a/src/kibit/rules/control_structures.clj +++ b/src/kibit/rules/control_structures.clj @@ -21,24 +21,3 @@ (while ?test . ?exprs)] [(let ?binding (do . ?exprs)) (let ?binding . ?exprs)] [(loop ?binding (do . ?exprs)) (loop ?binding . ?exprs)]) - -(comment - (when (not (pred? x y)) (f x y)) - - (if (all-streams-complete?) - (do - (deliver @all-clear :true) - (info "All streams complete."))) - - (if (pred? x) - (do (action a) - (action b) - (if-let [x (f a b c) - y (g a b c) - z (h a b c)] - (do (+ 1 0) - (= 1 1) - (< 1 0))) - (action d) - (action f)) - nil)) diff --git a/src/kibit/rules/util.clj b/src/kibit/rules/util.clj index 73a5d35..443d5c6 100644 --- a/src/kibit/rules/util.clj +++ b/src/kibit/rules/util.clj @@ -12,7 +12,8 @@ (defmacro defrules [name & rules] `(let [rules# (for [rule# '~rules] - (if (raw-rule? rule#) - (eval rule#) ;; raw rule, no need to compile - (compile-rule rule#)))] + (unifier/prep + (if (raw-rule? rule#) + (eval rule#) ;; raw rule, no need to compile + (compile-rule rule#))))] (def ~name (vec rules#)))) diff --git a/test/kibit/test/core.clj b/test/kibit/test/core.clj index af366c1..7ada2a3 100644 --- a/test/kibit/test/core.clj +++ b/test/kibit/test/core.clj @@ -15,7 +15,8 @@ "Hello" '(do "Hello") '(when test then) '(do (when test then)) :one '(do :one) - {:one 1} '(do {:one 1}))) + {:one 1} '(do {:one 1}) + #{:a :b} '#{(do :a) (do :b)})) ;; This test confirms when checking will happen and when it won't (deftest simplify-exprs diff --git a/test/kibit/test/driver.clj b/test/kibit/test/driver.clj index 3e88ebc..f0442fe 100644 --- a/test/kibit/test/driver.clj +++ b/test/kibit/test/driver.clj @@ -6,29 +6,33 @@ (deftest clojure-file-are (are [expected file] (= expected (driver/clojure-file? (io/file file))) - true "test/resources/first.clj" - true "test/resources/second.cljx" - true "test/resources/third.cljs" - false "test.resources/fourth.txt")) + true "corpus/first.clj" + true "corpus/second.cljx" + true "corpus/third.cljs" + false "corpus/fourth.txt")) (deftest find-clojure-sources-are - (is (= [(io/file "test/resources/first.clj") - (io/file "test/resources/keyword_suggestions.clj") - (io/file "test/resources/keywords.clj") - (io/file "test/resources/reader_conditionals.cljc") - (io/file "test/resources/second.cljx") - (io/file "test/resources/sets.clj") - (io/file "test/resources/third.cljs")] - (driver/find-clojure-sources-in-dir (io/file "test/resources"))))) + (is (= [(io/file "corpus/as_alias.clj") + (io/file "corpus/double_pound_reader_macros.clj") + (io/file "corpus/first.clj") + (io/file "corpus/keyword_suggestions.clj") + (io/file "corpus/keywords.clj") + (io/file "corpus/namespaced_maps.clj") + (io/file "corpus/read_eval.clj") + (io/file "corpus/reader_conditionals.cljc") + (io/file "corpus/second.cljx") + (io/file "corpus/sets.clj") + (io/file "corpus/third.cljs")] + (driver/find-clojure-sources-in-dir (io/file "corpus"))))) (deftest test-set-file - (is (driver/run ["test/resources/sets.clj"] nil))) + (is (driver/run ["corpus/sets.clj"] nil))) (deftest test-keywords-file (let [test-buf (ByteArrayOutputStream.) test-err (PrintWriter. test-buf)] (binding [*err* test-err] - (driver/run ["test/resources/keywords.clj"] nil)) + (driver/run ["corpus/keywords.clj"] nil)) (is (zero? (.size test-buf)) (format "Test err buffer contained '%s'" (.toString test-buf))))) @@ -42,7 +46,33 @@ {:alt (vec [:clojure.pprint/printing-key :resources.keyword-suggestions/local-key4 :some/other-key4]) :expr (into [] [:clojure.pprint/printing-key :resources.keyword-suggestions/local-key4 :some/other-key4])}) (map #(select-keys % [:expr :alt]) - (driver/run ["test/resources/keyword_suggestions.clj"] nil "--reporter" "no-op"))))) + (driver/run ["corpus/keyword_suggestions.clj"] nil "--reporter" "no-op"))))) -(deftest process-cljc-file - (is (driver/run ["test/resources/reader_conditionals.cljc"] nil))) +(defmacro with-err-str + [& body] + `(let [s# (java.io.StringWriter.)] + (binding [*err* s#] + ~@body + (str s#)))) + +(deftest process-reader-macros + (is (= ["" "" "" ""] + [(with-err-str + (driver/run ["corpus/reader_conditionals.cljc"] nil "--reporter" "no-op")) + (with-err-str + (driver/run ["corpus/double_pound_reader_macros.clj"] nil "--reporter" "no-op")) + (with-err-str + (driver/run ["corpus/namespaced_maps.clj"] nil "--reporter" "no-op")) + (with-err-str + (driver/run ["corpus/as_alias.clj"] nil "--reporter" "no-op"))]))) + +(deftest no-read-eval-test + (is (= [{:expr + '(if true (do (edamame.core/read-eval (prn :hello :world!)) :a)) + :line 1 + :column 1 + :end-line 1 + :end-column 41 + :alt '(when true (edamame.core/read-eval (prn :hello :world!)) :a)}] + (map #(dissoc % :file) + (driver/run ["corpus/read_eval.clj"] nil "--reporter" "no-op")))))