├── corpus ├── .clj-kondo │ ├── .gitkeep │ ├── acme │ │ └── lib │ │ │ └── config.edn │ ├── hooks │ │ ├── roundtrip.clj_kondo │ │ ├── new.clj │ │ ├── re_frame.clj │ │ ├── rum.clj │ │ └── better_cond.clj_kondo │ ├── macroexpand2.clj │ └── macroexpand │ │ └── weird_macro.clj ├── edn │ ├── edn.edn │ └── analysis.edn ├── directory.clj │ └── arity2.clj ├── namespace_config │ ├── .clj-kondo │ │ └── config.edn │ └── src │ │ ├── macro_usages.clj │ │ └── macros.clj ├── read_error │ ├── error.clj │ └── ok.clj ├── issue-1366 │ ├── dir-2 │ │ └── another_ns.clj │ └── dir-1 │ │ └── user.clj ├── clojure_dart │ └── awesome.cljd ├── issue-2223 │ ├── a.clj │ └── b.clj ├── .clj-kondo2 │ └── config.edn ├── cond_without_else │ ├── core.cljs │ └── core.cljc ├── my │ └── project │ │ └── foo.clj ├── no_errors.clj ├── data_readers.clj ├── data_readers.cljc ├── in-ns │ ├── base_ns.clj │ └── in_ns.clj ├── config_dir │ ├── config.edn │ └── foo.clj ├── issue-2239 │ ├── .clj-kondo │ │ ├── config.edn │ │ └── hooks │ │ │ └── a.clj │ ├── b.clj │ └── a.clj ├── withcljdir.jar ├── issue-1996 │ ├── .clj-kondo │ │ ├── config.edn │ │ └── hooks.clj │ └── src │ │ └── my_test.clj ├── config_in_ns │ └── my_custom_ns.clj ├── custom_lint_fn_ignore.cljc ├── hooks │ ├── new.clj │ ├── coerce_sexpr_roundtrip.clj │ ├── re_frame.clj │ ├── location.clj │ └── expectations.clj ├── issue-2215 │ ├── .clj-kondo │ │ ├── config.edn │ │ └── hooks │ │ │ └── clojure │ │ │ └── test.clj │ └── fail.clj ├── issue-1980 │ ├── src │ │ └── my_print.clj │ └── .clj-kondo │ │ ├── config.edn │ │ └── hooks.clj ├── exports │ ├── clj-kondo.config.jar │ └── dir │ │ └── clj-kondo.exports │ │ └── clj-kondo │ │ └── slingshot │ │ └── config.edn ├── schema │ ├── calls.clj │ ├── defrecord.clj │ ├── defs.clj │ └── defmethod.clj ├── spec │ ├── alpha.cljc │ └── alpha.cljs ├── java │ ├── classes │ │ └── foo │ │ │ └── bar │ │ │ └── AwesomeClass.class │ ├── usages.clj │ └── sources │ │ └── foo │ │ └── bar │ │ └── AwesomeClass.java ├── simple_test │ └── a_test.clj ├── skip_args │ ├── arity.clj │ ├── streams_test.clj │ └── comment.cljs ├── aliased_namespaces │ ├── excluded_alias.clj │ ├── expanded_by_clj_kondo.clj │ ├── macroexpansion_str.clj │ ├── macroexpansion.clj │ ├── analyze_call_hook.clj │ ├── multiple_aliases.clj │ ├── analyze_call_hook_str.clj │ └── interop.cljc ├── shadow_cljs │ ├── default.cljs │ └── dot_alias.cljs ├── unused_referred_var.clj ├── acme │ └── lib │ │ └── example.clj ├── namespace_name_mismatch │ ├── correct_file.clj │ ├── correct_file.cljs │ ├── correct_file_with_+_sign.clj │ ├── correct_file_with_+_sign.cljs │ ├── wrong_file.cljs │ ├── file-with-dashes.clj │ ├── wrong_folder │ │ └── foo.clj │ ├── ignored.clj │ ├── wrong_file_but_substring.clj │ ├── wrong_file.clj │ └── folder-with-dashes │ │ └── foo.clj ├── refer_all.cljs ├── config │ └── invalid_arity.edn ├── cljc │ ├── test_cljc_from_clj.clj │ ├── test_cljs.cljs │ ├── datascript.cljc │ ├── test_cljc.cljs │ └── test_cljc.cljc ├── private │ ├── private_defs.clj │ └── private_calls.clj ├── cljs_self_require.cljc ├── issue-2067 │ ├── .clj-kondo │ │ ├── config.edn │ │ └── hooks.clj │ └── src │ │ └── my_test2.clj ├── redefined_deftest.clj ├── feature_syntax.cljc ├── exclude_clojure.clj ├── nested_syntax_quote.clj ├── types │ └── insufficient.clj ├── inline_def.clj ├── defrecord.clj ├── no_unused_namespace.clj ├── cljs_ns_as_as_object.cljs ├── nested_namespaced_maps.clj ├── prefixed_libspec.clj ├── case.clj ├── invalid_arity │ ├── order.clj │ ├── defs.clj │ └── calls.clj ├── def_fn.clj ├── defmulti.clj ├── core.rrb-vector.clj ├── deftype.cljs ├── redundant_do.clj ├── record_protocol_metadata.clj ├── cond_without_else.clj ├── cond_without_else.cljc ├── cond_without_else.cljs ├── rename.cljc ├── redundant_let.clj ├── refer_all.clj ├── jdbc │ ├── next_test.clj │ ├── next.clj │ └── cjj_test.clj ├── import_vars.clj ├── deprecated_var.clj ├── use.clj ├── metadata.clj ├── defprotocol.clj ├── core_async │ └── alt.clj ├── babashka_script.clj ├── with_redefs.clj ├── compojure │ ├── core.clj │ └── consumer.clj ├── unresolved_symbol.clj ├── clojure.test.are.cljc ├── lint_as_for.clj ├── deftest.cljc ├── macroexpand2.cljs └── spec_syntax.clj ├── .carve ├── config.edn └── ignore ├── src ├── data_readers.clj ├── aaaa_this_has_to_be_first │ └── because_patches.clj └── clj_kondo │ └── impl │ ├── version.clj │ ├── findings.types.edn │ ├── parser.clj │ ├── var_info.clj │ ├── config.types.edn │ ├── types │ └── clojure │ │ └── string.clj │ ├── analyzer │ ├── common.clj │ ├── babashka.clj │ ├── datalog.clj │ └── core_async.clj │ └── linters │ └── edn_utils.clj ├── CONTRIBUTING.md ├── resources ├── CLJ_KONDO_RELEASED_VERSION ├── CLJ_KONDO_VERSION ├── clj_kondo │ └── impl │ │ └── cache │ │ └── built_in │ │ ├── cljs │ │ ├── clojure.browser.repl.preload.transit.json │ │ ├── clojure.edn.transit.json │ │ ├── cljs.nodejs.transit.json │ │ ├── clojure.core.protocols.transit.json │ │ ├── clojure.datafy.transit.json │ │ ├── cljs.source-map.base64.transit.json │ │ ├── cljs.repl.transit.json │ │ ├── clojure.reflect.transit.json │ │ ├── clojure.walk.transit.json │ │ ├── cljs.source-map.base64-vlq.transit.json │ │ ├── cljs.loader.transit.json │ │ ├── clojure.data.transit.json │ │ ├── clojure.browser.net.transit.json │ │ ├── cljs.spec.gen.alpha.transit.json │ │ └── clojure.browser.event.transit.json │ │ ├── clj │ │ ├── clojure.uuid.transit.json │ │ ├── clojure.tools.deps.interop.transit.json │ │ ├── clojure.java.browse-ui.transit.json │ │ ├── clojure.edn.transit.json │ │ ├── clojure.java.basis.transit.json │ │ ├── clojure.template.transit.json │ │ ├── clojure.core.specs.alpha.transit.json │ │ ├── clojure.datafy.transit.json │ │ ├── clojure.repl.deps.transit.json │ │ ├── clojure.java.basis.impl.transit.json │ │ ├── clojure.stacktrace.transit.json │ │ ├── clojure.test.tap.transit.json │ │ ├── clojure.java.browse.transit.json │ │ ├── clojure.java.process.transit.json │ │ ├── clojure.walk.transit.json │ │ ├── clojure.java.javadoc.transit.json │ │ ├── clojure.java.shell.transit.json │ │ ├── clojure.data.transit.json │ │ ├── clojure.inspector.transit.json │ │ ├── clojure.core.protocols.transit.json │ │ └── clojure.xml.transit.json │ │ └── cljc │ │ ├── cljs.support.transit.json │ │ ├── cljs.analyzer.passes.transit.json │ │ ├── cljs.core.specs.alpha.transit.json │ │ ├── cljs.analyzer.impl.namespaces.transit.json │ │ ├── cljs.env.transit.json │ │ ├── cljs.spec.gen.alpha.transit.json │ │ └── cljs.tagged-literals.transit.json └── META-INF │ └── native-image │ └── clj-kondo │ └── clj-kondo │ └── native-image.properties ├── .dir-locals.el ├── logo ├── icon.png ├── logo-3x1.png ├── icon-300dpi.png └── logo-300dpi.png ├── .clj-kondo ├── babashka │ ├── fs │ │ └── config.edn │ └── sci │ │ ├── config.edn │ │ └── sci │ │ └── core.clj └── hooks │ └── one_of.clj ├── screenshots ├── vim.png ├── demo.png ├── vscode.png ├── intellij-let.png ├── intellij-lsp.png ├── re-frame-hook.png ├── wrong-arity.png ├── hooks-with-bound.png ├── intellij-fw-config.png └── intellij-fw-global.png ├── script ├── versions.edn ├── extract-var-info ├── extract-versions ├── check_glibc.sh ├── extract-versions.template ├── analysis-test ├── lint-gh ├── test.bat ├── test ├── cljdoc-preview ├── changelog.clj ├── dump_types.clj ├── bump_version ├── built-in ├── compile.bat ├── compile ├── diff ├── setup-musl └── clj_kondo │ └── release_artifact.clj ├── analysis ├── assets │ └── namespace-graph.png ├── deps.edn ├── src │ └── clj_kondo │ │ └── tools │ │ ├── popular_vars.clj │ │ ├── missing_docstrings.clj │ │ ├── namespace_graph.clj │ │ ├── unused_vars.clj │ │ ├── find_var.clj │ │ ├── circular_dependencies.clj │ │ ├── pprint.clj │ │ └── private_vars.clj └── script │ └── unused_vars.cljs ├── examples └── README.md ├── .circleci └── script │ ├── deploy │ ├── install-leiningen │ ├── lein │ ├── release │ ├── install-clojure │ ├── tools.deps │ └── performance ├── Dockerfile.ci ├── src-profile └── clj_kondo │ └── profile.clj ├── .dockerignore ├── .github ├── FUNDING.yml ├── workflows │ ├── clj-kondo.yml │ └── diff.yml ├── pull_request_template.md ├── stale.yml └── ISSUE_TEMPLATE │ └── feature_request.md ├── test └── clj_kondo │ ├── compojure_test.clj │ ├── uninitialized_var_test.clj │ ├── equals_true_test.clj │ ├── equals_false_test.clj │ ├── babashka_test.clj │ ├── duplicate_require_test.clj │ ├── condition_always_true_test.clj │ ├── impl │ ├── utils_test.clj │ ├── config_test.clj │ ├── macroexpand_test.clj │ ├── extract_var_info_test.clj │ └── copy_config_test.clj │ ├── reify_test.clj │ ├── duplicate_field_name_test.clj │ ├── self_requiring_namespace_test.clj │ ├── config_in_tag_test.clj │ ├── analysis │ ├── instance_invocations_test.clj │ └── edn_test.clj │ ├── plus_minus_one_test.clj │ ├── warn_on_reflection_test.clj │ ├── missing_body_in_when_test.clj │ ├── jdbc_test.clj │ ├── unused_alias_test.clj │ ├── keyword_binding_test.clj │ └── clojure_data_xml_test.clj ├── Dockerfile.alpine ├── parser └── clj_kondo │ └── impl │ └── rewrite_clj │ ├── parser │ ├── string.clj │ ├── whitespace.clj │ └── namespaced_map.clj │ ├── node │ ├── regex.clj │ ├── comment.clj │ ├── token.clj │ ├── forms.clj │ ├── uneval.clj │ ├── integer.clj │ ├── keyword.clj │ └── string.clj │ └── parser.clj ├── inlined └── clj_kondo │ └── impl │ └── toolsreader │ └── v1v2v2 │ └── cljs │ └── tools │ └── reader │ └── reader_types.clj ├── .pre-commit-hooks.yaml.template ├── doc ├── docker.md └── cljdoc.edn ├── snapcraft.yaml ├── .pre-commit-hooks.yaml ├── Dockerfile ├── pod-test └── clj_kondo │ └── pod_test.clj ├── bb.edn ├── project.clj.template ├── .cirrus.yml └── .gitignore /corpus/.clj-kondo/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /corpus/edn/edn.edn: -------------------------------------------------------------------------------- 1 | {a 1 b 2 a 3} 2 | -------------------------------------------------------------------------------- /corpus/directory.clj/arity2.clj: -------------------------------------------------------------------------------- 1 | (inc) 2 | -------------------------------------------------------------------------------- /.carve/config.edn: -------------------------------------------------------------------------------- 1 | {:paths ["src" "test"]} 2 | -------------------------------------------------------------------------------- /src/data_readers.clj: -------------------------------------------------------------------------------- 1 | {f/q clojure.set/join} 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | See [doc/dev.md](doc/dev.md). 2 | -------------------------------------------------------------------------------- /corpus/namespace_config/.clj-kondo/config.edn: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /corpus/read_error/error.clj: -------------------------------------------------------------------------------- 1 | (ns read-error.error 2 | -------------------------------------------------------------------------------- /resources/CLJ_KONDO_RELEASED_VERSION: -------------------------------------------------------------------------------- 1 | 2023.12.15 2 | -------------------------------------------------------------------------------- /resources/CLJ_KONDO_VERSION: -------------------------------------------------------------------------------- 1 | 2023.12.16-SNAPSHOT 2 | -------------------------------------------------------------------------------- /corpus/issue-1366/dir-2/another_ns.clj: -------------------------------------------------------------------------------- 1 | (ns another-ns) 2 | -------------------------------------------------------------------------------- /corpus/clojure_dart/awesome.cljd: -------------------------------------------------------------------------------- 1 | (ns awesome) 2 | 3 | foo 4 | -------------------------------------------------------------------------------- /corpus/issue-2223/a.clj: -------------------------------------------------------------------------------- 1 | (ns a) 2 | 3 | (deftype Foo []) 4 | -------------------------------------------------------------------------------- /corpus/.clj-kondo2/config.edn: -------------------------------------------------------------------------------- 1 | {:config-paths ["../.clj-kondo"]} 2 | -------------------------------------------------------------------------------- /corpus/cond_without_else/core.cljs: -------------------------------------------------------------------------------- 1 | (ns cljs.core) 2 | 3 | (cond 1 2) 4 | -------------------------------------------------------------------------------- /corpus/my/project/foo.clj: -------------------------------------------------------------------------------- 1 | (ns my.project.foo) 2 | 3 | (defn- bar []) 4 | -------------------------------------------------------------------------------- /.dir-locals.el: -------------------------------------------------------------------------------- 1 | ((clojure-mode 2 | (cider-clojure-cli-aliases . "test"))) 3 | -------------------------------------------------------------------------------- /corpus/edn/analysis.edn: -------------------------------------------------------------------------------- 1 | {:a 1 :b :foo :c bar 2 | :exec-fn clojure.core/inc} 3 | -------------------------------------------------------------------------------- /corpus/no_errors.clj: -------------------------------------------------------------------------------- 1 | (ns corpus.no-errors) 2 | 3 | (defn foo [] 1) 4 | (foo) 5 | -------------------------------------------------------------------------------- /logo/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magnars/clj-kondo/master/logo/icon.png -------------------------------------------------------------------------------- /corpus/data_readers.clj: -------------------------------------------------------------------------------- 1 | {foo/bar my.project.foo/bar 2 | foo/baz my.project/baz} 3 | -------------------------------------------------------------------------------- /corpus/data_readers.cljc: -------------------------------------------------------------------------------- 1 | {foo/bar my.project.foo/bar 2 | foo/baz my.project/baz} 3 | -------------------------------------------------------------------------------- /.clj-kondo/babashka/fs/config.edn: -------------------------------------------------------------------------------- 1 | {:lint-as {babashka.fs/with-temp-dir clojure.core/let}} 2 | -------------------------------------------------------------------------------- /corpus/in-ns/base_ns.clj: -------------------------------------------------------------------------------- 1 | (ns in-ns.base-ns) 2 | 3 | (defn foo []) 4 | 5 | (foo 1 2 3) 6 | -------------------------------------------------------------------------------- /logo/logo-3x1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magnars/clj-kondo/master/logo/logo-3x1.png -------------------------------------------------------------------------------- /screenshots/vim.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magnars/clj-kondo/master/screenshots/vim.png -------------------------------------------------------------------------------- /.clj-kondo/babashka/sci/config.edn: -------------------------------------------------------------------------------- 1 | {:hooks {:macroexpand {sci.core/copy-ns sci.core/copy-ns}}} 2 | -------------------------------------------------------------------------------- /corpus/config_dir/config.edn: -------------------------------------------------------------------------------- 1 | {:linters {:unresolved-symbol {:exclude [(bar/weird-macro)]}}} 2 | -------------------------------------------------------------------------------- /corpus/issue-2239/.clj-kondo/config.edn: -------------------------------------------------------------------------------- 1 | {:hooks {:macroexpand {a/my-macro hooks.a/my-macro}}} 2 | -------------------------------------------------------------------------------- /corpus/withcljdir.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magnars/clj-kondo/master/corpus/withcljdir.jar -------------------------------------------------------------------------------- /logo/icon-300dpi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magnars/clj-kondo/master/logo/icon-300dpi.png -------------------------------------------------------------------------------- /logo/logo-300dpi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magnars/clj-kondo/master/logo/logo-300dpi.png -------------------------------------------------------------------------------- /screenshots/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magnars/clj-kondo/master/screenshots/demo.png -------------------------------------------------------------------------------- /corpus/issue-1996/.clj-kondo/config.edn: -------------------------------------------------------------------------------- 1 | {:hooks {:analyze-call {my-test/my-test hooks/my-test}}} 2 | -------------------------------------------------------------------------------- /screenshots/vscode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magnars/clj-kondo/master/screenshots/vscode.png -------------------------------------------------------------------------------- /script/versions.edn: -------------------------------------------------------------------------------- 1 | {:extract-clj-version "1.12.0-alpha5" 2 | :extract-cljs-version "1.11.60"} 3 | -------------------------------------------------------------------------------- /corpus/config_dir/foo.clj: -------------------------------------------------------------------------------- 1 | (ns config-dir.foo 2 | (:require [bar])) 3 | 4 | (bar/weird-macro x y z) 5 | -------------------------------------------------------------------------------- /corpus/config_in_ns/my_custom_ns.clj: -------------------------------------------------------------------------------- 1 | (ns config-in-ns.my-custom-ns) 2 | 3 | (defn ^:private foo []) 4 | -------------------------------------------------------------------------------- /corpus/custom_lint_fn_ignore.cljc: -------------------------------------------------------------------------------- 1 | (ns custom-lint-fn-ignore) 2 | 3 | #_:clj-kondo/ignore (eval '(+ 1 2 3)) 4 | -------------------------------------------------------------------------------- /corpus/hooks/new.clj: -------------------------------------------------------------------------------- 1 | (ns hooks.new) 2 | 3 | (new String "") 4 | (String.) 5 | #_:clj-kondo/ignore (String.) 6 | -------------------------------------------------------------------------------- /screenshots/intellij-let.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magnars/clj-kondo/master/screenshots/intellij-let.png -------------------------------------------------------------------------------- /screenshots/intellij-lsp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magnars/clj-kondo/master/screenshots/intellij-lsp.png -------------------------------------------------------------------------------- /screenshots/re-frame-hook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magnars/clj-kondo/master/screenshots/re-frame-hook.png -------------------------------------------------------------------------------- /screenshots/wrong-arity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magnars/clj-kondo/master/screenshots/wrong-arity.png -------------------------------------------------------------------------------- /corpus/issue-2215/.clj-kondo/config.edn: -------------------------------------------------------------------------------- 1 | {:hooks {:analyze-call {clojure.test/deftest hooks.clojure.test/deftest}}} 2 | -------------------------------------------------------------------------------- /corpus/issue-2239/.clj-kondo/hooks/a.clj: -------------------------------------------------------------------------------- 1 | (ns hooks.a) 2 | 3 | (defmacro my-macro [& body] 4 | `(do ~@body)) 5 | -------------------------------------------------------------------------------- /corpus/issue-1980/src/my_print.clj: -------------------------------------------------------------------------------- 1 | (ns my-print) 2 | 3 | (defn my-print [& xs] 4 | (prn xs)) 5 | 6 | (my-print 1) 7 | -------------------------------------------------------------------------------- /screenshots/hooks-with-bound.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magnars/clj-kondo/master/screenshots/hooks-with-bound.png -------------------------------------------------------------------------------- /screenshots/intellij-fw-config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magnars/clj-kondo/master/screenshots/intellij-fw-config.png -------------------------------------------------------------------------------- /screenshots/intellij-fw-global.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magnars/clj-kondo/master/screenshots/intellij-fw-global.png -------------------------------------------------------------------------------- /analysis/assets/namespace-graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magnars/clj-kondo/master/analysis/assets/namespace-graph.png -------------------------------------------------------------------------------- /corpus/.clj-kondo/acme/lib/config.edn: -------------------------------------------------------------------------------- 1 | {:linters {:unresolved-symbol {:exclude [(acme.lib.example/awful-macro [x y z])]}}} 2 | -------------------------------------------------------------------------------- /corpus/exports/clj-kondo.config.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magnars/clj-kondo/master/corpus/exports/clj-kondo.config.jar -------------------------------------------------------------------------------- /corpus/in-ns/in_ns.clj: -------------------------------------------------------------------------------- 1 | (ns in-ns.in-ns) 2 | 3 | (in-ns 'in-ns.base-ns) 4 | 5 | (foo 1 2 3) ;; this should give a warning 6 | -------------------------------------------------------------------------------- /corpus/schema/calls.clj: -------------------------------------------------------------------------------- 1 | (ns schema.calls 2 | (:require [schema.defs :as d])) 3 | 4 | (d/verify-signature) 5 | 6 | d/foo 7 | -------------------------------------------------------------------------------- /corpus/read_error/ok.clj: -------------------------------------------------------------------------------- 1 | (ns read-error.ok) 2 | 3 | (defn foo [] 1) 4 | 5 | ;; this should still yield an error 6 | (foo 1) 7 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | The examples have been moved to the [config](https://github.com/clj-kondo/config) project. 4 | -------------------------------------------------------------------------------- /.carve/ignore: -------------------------------------------------------------------------------- 1 | clj-kondo.main/-main 2 | clj-kondo.impl.types/misc-types 3 | clj-kondo.profile/-main 4 | pod.borkdude.clj-kondo/debug 5 | -------------------------------------------------------------------------------- /corpus/issue-2223/b.clj: -------------------------------------------------------------------------------- 1 | (ns b (:require [a :refer :all]) 2 | (:import [a Foo])) 3 | 4 | (defn dude [^Foo x] 5 | (.-y x)) 6 | 7 | -------------------------------------------------------------------------------- /corpus/issue-2239/b.clj: -------------------------------------------------------------------------------- 1 | (ns b 2 | (:require 3 | [a :refer [my-macro]])) 4 | 5 | (defn looses-name [] 6 | (my-macro (println a/x))) 7 | -------------------------------------------------------------------------------- /corpus/spec/alpha.cljc: -------------------------------------------------------------------------------- 1 | (ns spec.alpha 2 | (:refer-clojure :exclude [def])) 3 | 4 | (defmacro def [_name _keyword _spec] 5 | ::TODO) 6 | -------------------------------------------------------------------------------- /corpus/issue-1366/dir-1/user.clj: -------------------------------------------------------------------------------- 1 | (ns user 2 | (:require 3 | [clojure.set :refer :all] 4 | [clojure.string :refer [blank?]])) 5 | 6 | 7 | -------------------------------------------------------------------------------- /corpus/java/classes/foo/bar/AwesomeClass.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/magnars/clj-kondo/master/corpus/java/classes/foo/bar/AwesomeClass.class -------------------------------------------------------------------------------- /corpus/simple_test/a_test.clj: -------------------------------------------------------------------------------- 1 | (ns simple-test 2 | (:require [clojure.test :refer [deftest is]])) 3 | 4 | (deftest foo 5 | (is (= 1 1))) 6 | -------------------------------------------------------------------------------- /corpus/skip_args/arity.clj: -------------------------------------------------------------------------------- 1 | (ns skip-args.arity) 2 | 3 | (defmacro my-macro [a b c] 4 | `(+ ~a ~b ~c)) 5 | 6 | (my-macro 1 2 3 (select-keys)) 7 | -------------------------------------------------------------------------------- /corpus/aliased_namespaces/excluded_alias.clj: -------------------------------------------------------------------------------- 1 | (ns excluded-alias 2 | (:require [clojure.string :as str])) 3 | 4 | (clojure.string/join ", " (range 10)) 5 | -------------------------------------------------------------------------------- /corpus/aliased_namespaces/expanded_by_clj_kondo.clj: -------------------------------------------------------------------------------- 1 | (ns expanded-by-clj-kondo 2 | (:require [clojure.core :as cc])) 3 | 4 | (cond-> [] true (conj 1)) 5 | -------------------------------------------------------------------------------- /corpus/issue-2215/.clj-kondo/hooks/clojure/test.clj: -------------------------------------------------------------------------------- 1 | (ns hooks.clojure.test) 2 | 3 | (defn deftest [arg] 4 | ;; do nothing, just return arg 5 | arg) 6 | -------------------------------------------------------------------------------- /corpus/namespace_config/src/macro_usages.clj: -------------------------------------------------------------------------------- 1 | (ns macro-usages 2 | (:require [macros :refer [with-foo]])) 3 | 4 | (with-foo [a 1] 5 | a) ;; no warning 6 | -------------------------------------------------------------------------------- /corpus/aliased_namespaces/macroexpansion_str.clj: -------------------------------------------------------------------------------- 1 | (require '[clj-kondo.hooks-api :as api]) 2 | 3 | (defmacro new-> [x f] 4 | (list 'clojure.core/-> x f)) 5 | -------------------------------------------------------------------------------- /corpus/exports/dir/clj-kondo.exports/clj-kondo/slingshot/config.edn: -------------------------------------------------------------------------------- 1 | {:hooks 2 | {:analyze-call {slingshot.slingshot/try+ clj-kondo.slingshot.try-plus/try+}}} 3 | -------------------------------------------------------------------------------- /corpus/shadow_cljs/default.cljs: -------------------------------------------------------------------------------- 1 | (ns shadow-cljs.default 2 | (:require ["react-native-view-overflow" :default ViewOverflow])) 3 | 4 | [:> ViewOverflow ,,, ] 5 | -------------------------------------------------------------------------------- /corpus/unused_referred_var.clj: -------------------------------------------------------------------------------- 1 | (ns unused-referred-var 2 | (:require [clojure.string :refer [starts-with? ends-with?]])) 3 | 4 | (starts-with? "foo" "f") 5 | -------------------------------------------------------------------------------- /corpus/acme/lib/example.clj: -------------------------------------------------------------------------------- 1 | (ns acme.lib.example) 2 | 3 | (defmacro awful-macro [& _] 4 | ;; dark magic happening here 5 | ) 6 | 7 | (awful-macro [x y z a]) 8 | -------------------------------------------------------------------------------- /corpus/aliased_namespaces/macroexpansion.clj: -------------------------------------------------------------------------------- 1 | (ns macroexpansion 2 | (:require [clojure.core :as cc])) 3 | 4 | (defn new-> [x f] (f x)) 5 | 6 | (new-> 1 inc) 7 | -------------------------------------------------------------------------------- /corpus/namespace_name_mismatch/correct_file.clj: -------------------------------------------------------------------------------- 1 | (ns namespace-name-mismatch.correct-file) 2 | 3 | ;; This should NOT trigger the linter :namespace-name-mismatch. 4 | -------------------------------------------------------------------------------- /corpus/namespace_name_mismatch/correct_file.cljs: -------------------------------------------------------------------------------- 1 | (ns namespace-name-mismatch.correct-file) 2 | 3 | ;; This should NOT trigger the linter :namespace-name-mismatch. 4 | -------------------------------------------------------------------------------- /corpus/refer_all.cljs: -------------------------------------------------------------------------------- 1 | (ns macros) 2 | 3 | (defmacro foo [_]) 4 | 5 | (ns refer-all 6 | (:require [macros :refer-macros [foo]] :reload)) 7 | 8 | (foo) ;; caught 9 | -------------------------------------------------------------------------------- /corpus/skip_args/streams_test.clj: -------------------------------------------------------------------------------- 1 | (ns skip-args.streams-test 2 | (:require [riemann.test :refer [test-stream]])) 3 | 4 | (test-stream (select-keys {:a 1 :a 2})) 5 | -------------------------------------------------------------------------------- /corpus/aliased_namespaces/analyze_call_hook.clj: -------------------------------------------------------------------------------- 1 | (ns analyze-call-hook 2 | (:require [clojure.core :as cc])) 3 | 4 | (defn new-> [x f] (f x)) 5 | 6 | (new-> 1 inc) 7 | -------------------------------------------------------------------------------- /corpus/aliased_namespaces/multiple_aliases.clj: -------------------------------------------------------------------------------- 1 | (ns multiple-aliases 2 | (:require [baz.qux :as q] 3 | [baz.qux :as qq])) 4 | 5 | (baz.qux/some-fn 1 2 3) 6 | -------------------------------------------------------------------------------- /corpus/config/invalid_arity.edn: -------------------------------------------------------------------------------- 1 | {:linters {:invalid-arity {:level :off} 2 | :unused-binding {:level :off} 3 | :aliased-namespace-symbol {:level :off}}} 4 | -------------------------------------------------------------------------------- /corpus/hooks/coerce_sexpr_roundtrip.clj: -------------------------------------------------------------------------------- 1 | (ns hooks.coerce-sexpr-roundtrip 2 | (:require [myns :refer [defmodel]])) 3 | 4 | (new String "\\s+") 5 | (defmodel Foobar :table) 6 | -------------------------------------------------------------------------------- /corpus/.clj-kondo/hooks/roundtrip.clj_kondo: -------------------------------------------------------------------------------- 1 | (ns hooks.roundtrip) 2 | 3 | (defmacro defmodel 4 | [model _table-name] 5 | `(def ~model "Docstring." ~(keyword (name model)))) 6 | -------------------------------------------------------------------------------- /corpus/cljc/test_cljc_from_clj.clj: -------------------------------------------------------------------------------- 1 | (ns corpus.cljc.test-cljc-from-clj 2 | (:require [corpus.cljc.test-cljc :refer [bar]])) 3 | 4 | ;; why you no find this one? 5 | (bar 1 2 3) 6 | -------------------------------------------------------------------------------- /corpus/private/private_defs.clj: -------------------------------------------------------------------------------- 1 | (ns private.private-defs) 2 | 3 | (defn- private [x y z] x) 4 | (private 1 2 3) 5 | 6 | (defn ^:private private-by-meta []) 7 | (private-by-meta) 8 | -------------------------------------------------------------------------------- /.circleci/script/deploy: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ -z "$CIRCLE_PULL_REQUEST" ] && [ "$CIRCLE_BRANCH" = "master" ] 4 | then 5 | lein deploy clojars 6 | fi 7 | 8 | exit 0; 9 | -------------------------------------------------------------------------------- /corpus/issue-1980/.clj-kondo/config.edn: -------------------------------------------------------------------------------- 1 | {:linters {:discouraged-var {clojure.core/println {:message "Don't print!"}}} 2 | :hooks {:analyze-call {my-print/my-print hooks/my-print}}} 3 | -------------------------------------------------------------------------------- /corpus/issue-2215/fail.clj: -------------------------------------------------------------------------------- 1 | (ns fail 2 | (:require [clojure.test :refer [deftest]])) 3 | 4 | (deftest my-test 5 | (let [x 1] 6 | (let [y 2] 7 | (assert (not= x y))))) 8 | -------------------------------------------------------------------------------- /corpus/cljs_self_require.cljc: -------------------------------------------------------------------------------- 1 | (ns cljs-self-require 2 | #?(:cljs (:require-macros [cljs-self-require :refer [foo]]))) 3 | 4 | (defmacro foo [x] 5 | `(do (println ~x) (println ~x))) 6 | -------------------------------------------------------------------------------- /corpus/namespace_name_mismatch/correct_file_with_+_sign.clj: -------------------------------------------------------------------------------- 1 | (ns namespace-name-mismatch.correct-file-with-+-sign) 2 | 3 | ;; This should NOT trigger the linter :namespace-name-mismatch. 4 | -------------------------------------------------------------------------------- /corpus/namespace_name_mismatch/correct_file_with_+_sign.cljs: -------------------------------------------------------------------------------- 1 | (ns namespace-name-mismatch.correct-file-with-+-sign) 2 | 3 | ;; This should NOT trigger the linter :namespace-name-mismatch. 4 | -------------------------------------------------------------------------------- /corpus/skip_args/comment.cljs: -------------------------------------------------------------------------------- 1 | (ns skip-args.comment 2 | (:refer-clojure :exclude [comment]) 3 | (:require [cljs.core :as core])) 4 | 5 | (core/comment 6 | (select-keys)) 7 | 8 | 9 | -------------------------------------------------------------------------------- /corpus/issue-2239/a.clj: -------------------------------------------------------------------------------- 1 | (ns a) 2 | 3 | (defmacro unknown-macro [name value] 4 | `(def ~name ~value)) 5 | 6 | (unknown-macro x 2) 7 | 8 | (defmacro my-macro [& body] 9 | `(do ~@body)) 10 | -------------------------------------------------------------------------------- /corpus/namespace_name_mismatch/wrong_file.cljs: -------------------------------------------------------------------------------- 1 | (ns namespace-name-mismatch.foo) 2 | 3 | ;; This should trigger the linter :namespace-name-mismatch 4 | ;; because the namespace should have been 'wrong-file'. 5 | -------------------------------------------------------------------------------- /corpus/cond_without_else/core.cljc: -------------------------------------------------------------------------------- 1 | (ns cljs.core (:refer-clojure :exclude [cond]) 2 | #?(:cljs (:require-macros [cljs.core :as core]))) 3 | 4 | (defmacro cond [& clauses]) 5 | 6 | #?(:cljs (core/cond 1 2)) 7 | -------------------------------------------------------------------------------- /corpus/cljc/test_cljs.cljs: -------------------------------------------------------------------------------- 1 | (ns corpus.cljc.test-cljs 2 | (:require [corpus.cljc.test-cljc :refer [foo bar]])) 3 | 4 | (foo 1) ;; correct 5 | (foo 1 2) ;; incorrect 6 | (bar 1 2) ;; incorrect 7 | 8 | (defn baz []) 9 | -------------------------------------------------------------------------------- /corpus/issue-1996/src/my_test.clj: -------------------------------------------------------------------------------- 1 | (ns my-test 2 | (:require [foo :as foo])) 3 | 4 | (defn my-test [& xs] 5 | (prn xs)) 6 | 7 | (my-test 8 | (with-redefs [inc dec] 1) 9 | (foo/with-redefs [inc dec] 1)) 10 | -------------------------------------------------------------------------------- /corpus/issue-2067/.clj-kondo/config.edn: -------------------------------------------------------------------------------- 1 | {:hooks {:analyze-call {my-test/my-test hooks/my-test} 2 | :macroexpand {my-test/my-test-macro hooks/my-test-macro}} 3 | :ns-groups [{:pattern "my-test.*" :name my-test}]} 4 | -------------------------------------------------------------------------------- /src/aaaa_this_has_to_be_first/because_patches.clj: -------------------------------------------------------------------------------- 1 | (ns aaaa-this-has-to-be-first.because-patches 2 | {:no-doc true}) 3 | 4 | (when (System/getenv "CLJ_KONDO_NATIVE") 5 | (require '[aaaa-this-has-to-be-first.pprint])) 6 | -------------------------------------------------------------------------------- /corpus/redefined_deftest.clj: -------------------------------------------------------------------------------- 1 | (ns redefined-deftest 2 | (:require [clojure.test :refer [deftest]])) 3 | 4 | (deftest) 5 | 6 | (deftest foo) 7 | (deftest foo) ;; <- redefined test warning 8 | 9 | (foo 1) 10 | 11 | 12 | -------------------------------------------------------------------------------- /corpus/spec/alpha.cljs: -------------------------------------------------------------------------------- 1 | (ns spec.alpha 2 | (:refer-clojure :exclude [def]) 3 | (:require-macros [spec.alpha :as m])) 4 | 5 | (def x 1) ;; no arity error, although spec.alpha/def lives in a same-named ns 6 | (m/def foo ::foo) 7 | -------------------------------------------------------------------------------- /src/clj_kondo/impl/version.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.impl.version 2 | (:require 3 | [clojure.java.io :as io] 4 | [clojure.string :as str])) 5 | 6 | (def version 7 | (str/trim 8 | (slurp (io/resource "CLJ_KONDO_VERSION")))) 9 | -------------------------------------------------------------------------------- /corpus/feature_syntax.cljc: -------------------------------------------------------------------------------- 1 | (ns feature-syntax 2 | (:require [clojure.test :as t])) 3 | 4 | (t/deftest foo-test 5 | #?(:clj 6 | (t/testing "foo" 7 | (prn :x)) 8 | (t/testing "bar" 9 | (prn :y)))) 10 | -------------------------------------------------------------------------------- /analysis/deps.edn: -------------------------------------------------------------------------------- 1 | {:deps {clj-kondo/clj-kondo {:local/root ".."} 2 | aysylu/loom {:mvn/version "1.0.2"} 3 | com.stuartsierra/dependency {:mvn/version "0.2.0"} 4 | rewrite-clj/rewrite-clj {:mvn/version "1.1.46"}}} 5 | -------------------------------------------------------------------------------- /corpus/namespace_name_mismatch/file-with-dashes.clj: -------------------------------------------------------------------------------- 1 | (ns namespace-name-mismatch.file-with-dashes) 2 | 3 | ;; This should trigger the linter :namespace-name-mismatch 4 | ;; because the file name should have been `file_with_dashes.clj`. 5 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/cljs/clojure.browser.repl.preload.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$conn",["^ ","~:row",12,"~:col",1,"~:name","^0","~:ns","~$clojure.browser.repl.preload","~:top-ns","^5"],"~:filename","clojure/browser/repl/preload.cljs"] -------------------------------------------------------------------------------- /script/extract-var-info: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | rm -rf /tmp/var-info 4 | mkdir -p /tmp/var-info 5 | 6 | # shellcheck disable=1091 7 | source script/extract-versions 8 | 9 | clojure -M:extract -m clj-kondo.impl.extract-var-info 10 | -------------------------------------------------------------------------------- /.clj-kondo/babashka/sci/sci/core.clj: -------------------------------------------------------------------------------- 1 | (ns sci.core) 2 | 3 | (defmacro copy-ns 4 | ([ns-sym sci-ns] 5 | `(copy-ns ~ns-sym ~sci-ns nil)) 6 | ([ns-sym sci-ns opts] 7 | `[(quote ~ns-sym) 8 | ~sci-ns 9 | (quote ~opts)])) 10 | -------------------------------------------------------------------------------- /corpus/cljc/datascript.cljc: -------------------------------------------------------------------------------- 1 | (ns datascript 2 | (:refer-clojure :exclude [seqable?])) 3 | 4 | (defn #?@(:clj [seqable?] 5 | :cljs [^boolean seqable?]) 6 | #?(:clj ^Boolean [x] 7 | :cljs [x]) x) 8 | 9 | (seqable? 1 2) 10 | -------------------------------------------------------------------------------- /corpus/exclude_clojure.clj: -------------------------------------------------------------------------------- 1 | (ns clojure.core) 2 | 3 | (defn get [x y] x) 4 | 5 | (ns corpus.exclude-clojure1 6 | (:refer-clojure :exclude [get])) 7 | 8 | (get 1 2 3 4) 9 | 10 | (ns corpus.exclude-clojure2) 11 | 12 | (get 1 2 3 4) 13 | -------------------------------------------------------------------------------- /.circleci/script/install-leiningen: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | curl https://codeberg.org/leiningen/leiningen/raw/tag/2.9.10/bin/lein > lein 4 | sudo mkdir -p /usr/local/bin/ 5 | sudo mv lein /usr/local/bin/lein 6 | sudo chmod a+x /usr/local/bin/lein 7 | -------------------------------------------------------------------------------- /corpus/nested_syntax_quote.clj: -------------------------------------------------------------------------------- 1 | (ns nested-syntax-quote 2 | (:require [lib.foo :as foo])) 3 | 4 | (defn foo [x] 5 | `(+ ~(+ 1 2 `~x))) 6 | 7 | (defn nested-macro [tag] 8 | `(defmacro ~(symbol tag) [& args#] 9 | `(foo/bar ~@args#))) 10 | -------------------------------------------------------------------------------- /corpus/types/insufficient.clj: -------------------------------------------------------------------------------- 1 | (ns corpus.types.insufficient) 2 | 3 | (defn foo [a ^String b & args] 4 | [a b args]) 5 | 6 | (foo :bad :mkay) 7 | 8 | (defmacro my-macro [x]) 9 | 10 | (my-macro (foo [:not :a :fn-call :so "fine"])) 11 | 12 | -------------------------------------------------------------------------------- /corpus/namespace_name_mismatch/wrong_folder/foo.clj: -------------------------------------------------------------------------------- 1 | (ns namespace-name-mismatch.something.foo) 2 | 3 | ;; This should trigger the linter :namespace-name-mismatch 4 | ;; because the folder name should have been `something` 5 | ;; instead of `wrong_folder`. 6 | -------------------------------------------------------------------------------- /corpus/cljc/test_cljc.cljs: -------------------------------------------------------------------------------- 1 | (ns corpus.cljc.test-cljc 2 | (:require-macros [corpus.cljc.test-cljc :as c :refer [foo]])) 3 | 4 | (foo 1) ;; correct 5 | (foo 1 2) ;; incorrect 6 | 7 | (bar 1 2 3) ;; this call should not be recognized, since we didn't refer bar 8 | -------------------------------------------------------------------------------- /corpus/namespace_name_mismatch/ignored.clj: -------------------------------------------------------------------------------- 1 | #_{:clj-kondo/ignore [:namespace-name-mismatch]} 2 | (ns ignore-that-the-namespace-does-not-match-the-filename) 3 | 4 | ;; This should NOT trigger the linter :namespace-name-mismatch 5 | ;; because the linter is ignored. 6 | -------------------------------------------------------------------------------- /Dockerfile.ci: -------------------------------------------------------------------------------- 1 | FROM ubuntu:latest 2 | 3 | RUN apt-get update \ 4 | && apt-get install -y curl \ 5 | && mkdir -p /usr/local/bin 6 | 7 | COPY clj-kondo /usr/local/bin/clj-kondo 8 | 9 | RUN chmod +x /usr/local/bin/clj-kondo 10 | 11 | CMD ["clj-kondo"] 12 | -------------------------------------------------------------------------------- /corpus/namespace_name_mismatch/wrong_file_but_substring.clj: -------------------------------------------------------------------------------- 1 | (ns namespace-name-mismatch.wrong-file) 2 | 3 | ;; This should trigger the linter :namespace-name-mismatch; it's a 4 | ;; special case because this ensures that just checking `index-of` 5 | ;; is not enough. 6 | -------------------------------------------------------------------------------- /corpus/inline_def.clj: -------------------------------------------------------------------------------- 1 | (ns inline-def 2 | (:require [clojure.test :as t])) 3 | 4 | (defn foo [] 5 | (def x 1)) 6 | 7 | (defn- foo [] 8 | (def x 1)) 9 | 10 | (def foo (def x 1)) 11 | 12 | (t/deftest foo (def x 1)) 13 | 14 | (defmacro foo [] (def x 1)) 15 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/clj/clojure.uuid.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$default-uuid-reader",["^ ","~:row",11,"~:col",1,"~:private",true,"~:fixed-arities",["~#set",[1]],"~:name","^0","~:ns","~$clojure.uuid","~:top-ns","^8","~:type","~:fn"],"~:filename","clojure/uuid.clj"] -------------------------------------------------------------------------------- /corpus/aliased_namespaces/analyze_call_hook_str.clj: -------------------------------------------------------------------------------- 1 | (require '[clj-kondo.hooks-api :as api]) 2 | 3 | (fn [{:keys [node]}] 4 | (let [children (rest (:children node)) 5 | node (list* (api/token-node 'clojure.core/->) children)] 6 | {:node (api/list-node node)})) 7 | -------------------------------------------------------------------------------- /corpus/namespace_name_mismatch/wrong_file.clj: -------------------------------------------------------------------------------- 1 | (ns namespace-name-mismatch.foo) 2 | 3 | ;; This should trigger the linter :namespace-name-mismatch 4 | ;; because the namespace should have been 'wrong-file'. 5 | 6 | (ns namespace-name-mismatch.foo2) ;; this should not be reported 7 | -------------------------------------------------------------------------------- /corpus/defrecord.clj: -------------------------------------------------------------------------------- 1 | (ns defrecord 2 | (:import [java.net FileNameMap])) 3 | 4 | (defrecord Thing [a b] ;; b should not be reported as unused 5 | FileNameMap 6 | (getContentTypeFor [this fileName] (str a "-" fileName))) 7 | 8 | (->Thing 1 2 3) 9 | (map->Thing {:a 1} 2) 10 | -------------------------------------------------------------------------------- /corpus/namespace_name_mismatch/folder-with-dashes/foo.clj: -------------------------------------------------------------------------------- 1 | (ns namespace-name-mismatch.folder-with-dashes.foo) 2 | 3 | ;; This should trigger the linter :namespace-name-mismatch 4 | ;; because the folder name should have been `folder_with_dashes` 5 | ;; with underscores instead of dashes. 6 | -------------------------------------------------------------------------------- /corpus/no_unused_namespace.clj: -------------------------------------------------------------------------------- 1 | (ns no-unused-namespace 2 | (:require [clojure.string :as str])) 3 | 4 | (let [{score (if (str/starts-with? "foo" "f") 5 | :starts-with :not-starts-with)} 6 | {:starts-with 100 7 | :not-starts-with 0}] 8 | score) ;;=> 100 9 | -------------------------------------------------------------------------------- /corpus/cljs_ns_as_as_object.cljs: -------------------------------------------------------------------------------- 1 | (ns foo 2 | (:require [reagent.core :as r] 3 | ["apsl-react-native-button" :as NativeButton] 4 | ["constructor-export" :as ConstructorExport])) 5 | 6 | (def button (r/adapt-react-class NativeButton)) 7 | (def obj (ConstructorExport.)) 8 | -------------------------------------------------------------------------------- /corpus/nested_namespaced_maps.clj: -------------------------------------------------------------------------------- 1 | (ns nested-namespaced-maps 2 | (:require [clojure.test :as it])) 3 | 4 | (defn test-fn 5 | [map] 6 | (println #::it {:a #::it {}})) 7 | 8 | (test-fn #::it{:a 1}) ;; correct 9 | (test-fn #::it{:a 1} 1) ;; invalid 10 | 11 | #::it{:a 1 :a 2} 12 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/clj/clojure.tools.deps.interop.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$invoke-tool",["^ ","~:row",15,"~:col",1,"~:fixed-arities",["~#set",[1]],"~:name","^0","~:ns","~$clojure.tools.deps.interop","~:top-ns","^7","~:type","~:fn"],"~:filename","clojure/tools/deps/interop.clj"] -------------------------------------------------------------------------------- /corpus/hooks/re_frame.clj: -------------------------------------------------------------------------------- 1 | (ns bar 2 | {:clj-kondo/config '{:linters {:re-frame/keyword {:level :warning}} 3 | :hooks {:analyze-call {re-frame.core/dispatch hooks.re-frame/dispatch}}}} 4 | (:require [re-frame.core :as r :refer [dispatch]])) 5 | 6 | (dispatch [:foo 1]) 7 | -------------------------------------------------------------------------------- /corpus/issue-2067/src/my_test2.clj: -------------------------------------------------------------------------------- 1 | (ns my-test2 2 | (:require [foo :as foo])) 3 | 4 | (defn my-test [& xs] 5 | (prn xs)) 6 | 7 | (defmacro my-test-macro [x] 8 | x) 9 | 10 | (my-test 11 | (with-redefs [inc dec] 1) 12 | (foo/with-redefs [inc dec] 1)) 13 | 14 | (my-test-macro []) 15 | -------------------------------------------------------------------------------- /corpus/prefixed_libspec.clj: -------------------------------------------------------------------------------- 1 | (ns foo.bar.baz) 2 | 3 | (defn b [_]) 4 | 5 | (ns foo.baz) 6 | 7 | (defn c [_]) 8 | 9 | (ns prefixed-libspec 10 | (:require [foo 11 | [bar [baz :as b]] 12 | [baz :as c]])) 13 | 14 | (b/b) ;; invalid arity 15 | (c/c) ;; invalid arity 16 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/clj/clojure.java.browse-ui.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$open-url-in-swing",["^ ","~:row",15,"~:col",1,"~:private",true,"~:fixed-arities",["~#set",[1]],"~:name","^0","~:ns","~$clojure.java.browse-ui","~:top-ns","^8","~:type","~:fn"],"~:filename","clojure/java/browse_ui.clj"] -------------------------------------------------------------------------------- /corpus/issue-1980/.clj-kondo/hooks.clj: -------------------------------------------------------------------------------- 1 | (ns hooks 2 | (:require [clj-kondo.hooks-api :as api])) 3 | 4 | (defn my-print [{:keys [node]}] 5 | {:node (with-meta (api/list-node (list* (api/token-node 'clojure.core/println) (rest (:children node)))) 6 | {:clj-kondo/ignore [:discouraged-var]})}) 7 | -------------------------------------------------------------------------------- /src-profile/clj_kondo/profile.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.profile 2 | (:require [clj-async-profiler.core :as prof] 3 | [clj-kondo.main :as main])) 4 | 5 | (defn -main [& options] 6 | ;; prevent loading async profiler when lein test scans test files 7 | (prof/profile (apply main/main options)) 8 | (shutdown-agents)) 9 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .circleci/ 2 | .git/ 3 | .clj-kondo/ 4 | .cpcache/ 5 | .github/ 6 | doc/ 7 | classes/ 8 | screenshots/ 9 | logo/ 10 | test/ 11 | target/ 12 | .gitignore 13 | .carve_ignore 14 | .gitmodules 15 | appveyor.yml 16 | .DS_Store 17 | CHANGES.md 18 | deps.edn 19 | Dockerfile 20 | LICENSE 21 | README.md 22 | snapcraft.yaml 23 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/clj/clojure.edn.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$read",["^ ","~:row",14,"~:col",1,"~:fixed-arities",["~#set",[0,1,2]],"~:name","^0","~:ns","~$clojure.edn","~:top-ns","^7","~:type","~:fn"],"~$read-string",["^ ","^1",37,"^2",1,"^3",["^4",[1,2]],"^5","^;","^6","^7","^8","^7","^9","^:"],"~:filename","clojure/edn.clj"] -------------------------------------------------------------------------------- /corpus/case.clj: -------------------------------------------------------------------------------- 1 | (ns case) 2 | 3 | (def fn-name 'select-keys) 4 | 5 | (case fn-name 6 | (select-keys filter) 7 | (filter 1 2 3) ;; invalid 8 | (odd? pos? neg?) 9 | (filter 1 2 3) ;; invalid 10 | ) 11 | 12 | (case fn-name 13 | (select-keys filter) 14 | (filter 1 2 3) ;; invalid 15 | (odd? 1 2) ;; invalid 16 | ) 17 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/cljs/clojure.edn.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$read",["^ ","~:row",20,"~:col",1,"~:fixed-arities",["~#set",[1,4,2]],"~:name","^0","~:ns","~$clojure.edn","~:top-ns","^7","~:type","~:fn"],"~$read-string",["^ ","^1",44,"^2",1,"^3",["^4",[1,2]],"^5","^;","^6","^7","^8","^7","^9","^:"],"~:filename","clojure/edn.cljs"] -------------------------------------------------------------------------------- /corpus/invalid_arity/order.clj: -------------------------------------------------------------------------------- 1 | (ns corpus.invalid-arity.order) 2 | 3 | ;; call to def special form with docstring 4 | (def x "the number one" 1) 5 | (defmacro def [k spec-form]) 6 | ;; valid call to macro 7 | (corpus.invalid-arity.order/def ::foo int?) 8 | ;; invalid call to macro 9 | (corpus.invalid-arity.order/def ::foo int? string?) 10 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/cljc/cljs.support.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~:filename","cljs/support.cljc","~:clj",["^ ","~$assert-args",["^ ","~:row",11,"~:col",1,"~:macro",true,"~:varargs-min-arity",1,"~:name","^2","~:ns","~$cljs.support","~:top-ns","^9"]],"~:cljs",["^ ","^2",["^ ","^3",11,"^4",1,"^5",true,"^6",1,"^7","^2","^8","^9","^:","^9"]]] -------------------------------------------------------------------------------- /corpus/def_fn.clj: -------------------------------------------------------------------------------- 1 | (ns def-fn (:refer-clojure :exclude [cons])) 2 | 3 | (def 4 | ^{:arglists '([x seq]) 5 | :doc "Returns a new seq where x is the first element and seq is 6 | the rest." 7 | :added "1.0" 8 | :static true} 9 | 10 | cons (fn* ^:static cons [x seq] (. clojure.lang.RT (cons x seq)))) 11 | 12 | (cons 1 2 3) 13 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: borkdude # [borkdude] # I joined the waiting list, but so far no response 4 | open_collective: clj-kondo 5 | ko_fi: borkdude 6 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 7 | custom: # Replace with a single custom sponsorship URL 8 | -------------------------------------------------------------------------------- /corpus/private/private_calls.clj: -------------------------------------------------------------------------------- 1 | (ns private.private-calls 2 | (:require [private.private-defs :as x :refer [private]])) 3 | 4 | (private 1 2 3) 5 | (x/private-by-meta) 6 | (map private [] [] []) 7 | (map #'private.private-defs/private [] [] []) ;; <- should not be reported 8 | #_:clj-kondo/ignore (private 1 2 3) 9 | #_:clj-kondo/ignore private 10 | -------------------------------------------------------------------------------- /corpus/defmulti.clj: -------------------------------------------------------------------------------- 1 | (ns defmulti 2 | (:require [integrant.core :as ig])) 3 | 4 | (defmulti greeting 5 | (fn [x] (x "language"))) 6 | 7 | (defmethod greetingx "English" [x y] 8 | x) 9 | 10 | (defmethod ig/pre-init-spec :my/key [_] ::args) 11 | 12 | (defmulti xyz (fn ([x _] x) ([x _ _] x))) 13 | (defmethod xyz "z" ([x y] x) ([_ _ _] (inc))) 14 | -------------------------------------------------------------------------------- /corpus/invalid_arity/defs.clj: -------------------------------------------------------------------------------- 1 | (ns corpus.invalid-arity.defs) 2 | 3 | (defn public-fixed [x y z] x) 4 | (defn public-multi-arity ([x] (public-multi-arity x false)) ([x y] x)) 5 | (defn public-varargs [x y & zs] x) 6 | 7 | (public-fixed 1) 8 | (public-multi-arity 1) 9 | (public-multi-arity 1 2) 10 | (public-multi-arity 1 2 3) 11 | (public-varargs 1) 12 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/clj/clojure.java.basis.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$initial-basis",["^ ","~:row",37,"~:col",1,"~:fixed-arities",["~#set",[0]],"~:name","^0","~:ns","~$clojure.java.basis","~:top-ns","^7","~:type","~:fn"],"~$current-basis",["^ ","^1",43,"^2",1,"^3",["^4",[0]],"^5","^;","^6","^7","^8","^7","^9","^:"],"~:filename","clojure/java/basis.clj"] -------------------------------------------------------------------------------- /test/clj_kondo/compojure_test.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.compojure-test 2 | (:require 3 | [clj-kondo.test-utils :refer [lint!]] 4 | [clojure.java.io :as io] 5 | [clojure.test :as t :refer [deftest is testing]] 6 | [missing.test.assertions])) 7 | 8 | (deftest compojure-test 9 | (is (empty? (lint! (io/file "corpus" "compojure" "core_test.clj"))))) 10 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/clj/clojure.template.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$apply-template",["^ ","~:row",30,"~:col",1,"~:fixed-arities",["~#set",[3]],"~:name","^0","~:ns","~$clojure.template","~:top-ns","^7","~:type","~:fn"],"~$do-template",["^ ","^1",45,"^2",1,"~:macro",true,"~:varargs-min-arity",2,"^5","^;","^6","^7","^8","^7"],"~:filename","clojure/template.clj"] -------------------------------------------------------------------------------- /corpus/shadow_cljs/dot_alias.cljs: -------------------------------------------------------------------------------- 1 | (ns shadow-cljs.dot-alias 2 | (:require ["dayjs" :as dayjs] 3 | ["dayjs/plugin/timezone" :as dayjs.timezone] 4 | ["dayjs/plugin/utc" :as dayjs.utc])) 5 | 6 | (.extend dayjs dayjs.utc) 7 | (.extend dayjs dayjs.timezone) 8 | 9 | (require '["dayjs/plugin/utc2" :as dayjs.utc2]) 10 | (.extend dayjs dayjs.utc2) 11 | -------------------------------------------------------------------------------- /.github/workflows/clj-kondo.yml: -------------------------------------------------------------------------------- 1 | name: clj-kondo checks 2 | 3 | on: [push] 4 | 5 | jobs: 6 | self-lint: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v2 12 | - uses: DeLaGuardo/clojure-lint-action@master 13 | with: 14 | clj-kondo-args: --lint src test 15 | github_token: ${{ secrets.GITHUB_TOKEN }} 16 | -------------------------------------------------------------------------------- /resources/META-INF/native-image/clj-kondo/clj-kondo/native-image.properties: -------------------------------------------------------------------------------- 1 | ImageName=clj-kondo 2 | Args=-J-Dborkdude.dynaload.aot=true \ 3 | --initialize-at-build-time=com.fasterxml.jackson \ 4 | --initialize-at-build-time=com.github.javaparser \ 5 | -H:IncludeResources=clj_kondo/impl/cache/built_in/.* \ 6 | -H:Log=registerResource: \ 7 | --no-server \ 8 | -------------------------------------------------------------------------------- /.circleci/script/lein: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | err=0 4 | function _trap_error() { 5 | local exit_code="$1" 6 | if [ "$exit_code" -ne 2 ] && [ "$exit_code" -ne 3 ]; then 7 | (( err |= "$exit_code" )) 8 | fi 9 | } 10 | 11 | trap '_trap_error $?' ERR 12 | trap 'exit $err' SIGINT SIGTERM 13 | 14 | lein clj-kondo --lint "$(lein classpath)" 15 | 16 | exit "$err" 17 | -------------------------------------------------------------------------------- /corpus/invalid_arity/calls.clj: -------------------------------------------------------------------------------- 1 | (ns corpus.invalid-arity.calls 2 | (:require [corpus.invalid-arity.defs 3 | :as x :refer [public-fixed 4 | public-varargs 5 | public-multi-arity]])) 6 | 7 | (public-fixed 1) 8 | (x/public-fixed 1) 9 | (corpus.invalid-arity.defs/public-fixed 1) 10 | (public-multi-arity 1 2 3) 11 | (public-varargs 1) -------------------------------------------------------------------------------- /corpus/core.rrb-vector.clj: -------------------------------------------------------------------------------- 1 | (ns definterface) 2 | 3 | (definterface NodeManager 4 | (node [^java.util.concurrent.atomic.AtomicReference edit arr]) 5 | (empty []) 6 | (array [node]) 7 | (^java.util.concurrent.atomic.AtomicReference edit [node]) 8 | (^boolean regular [node]) 9 | (clone [^clojure.core.ArrayManager am ^int shift node])) 10 | 11 | (defn foo [x] 12 | (aget ^objects x 0)) 13 | -------------------------------------------------------------------------------- /corpus/.clj-kondo/hooks/new.clj: -------------------------------------------------------------------------------- 1 | (ns hooks.new 2 | (:require [clj-kondo.hooks-api :as api])) 3 | 4 | (defn new [{:keys [node]}] 5 | (api/reg-finding! (assoc (meta node) 6 | :message (str "Interop is no good! " (api/generated-node? (first (:children node)))) 7 | :type :interop))) 8 | 9 | (defmacro new-macroexpand [& body] 10 | `(do ~body)) 11 | -------------------------------------------------------------------------------- /corpus/hooks/location.clj: -------------------------------------------------------------------------------- 1 | (ns location 2 | {:clj-kondo/config '{:hooks {:analyze-call {hooks-location-test/foo " 3 | 4 | (require '[clj-kondo.hooks-api :as api]) 5 | 6 | (fn [{:keys [:node]}] 7 | {:node (api/list-node 8 | (list* (api/token-node 'inc) 9 | (rest (:children node))))})"}}}} 10 | (:require [hooks-location-test :as foo])) 11 | 12 | (foo/foo "foo") 13 | (foo/foo 1 2 3) 14 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/cljs/cljs.nodejs.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$require",["^ ","~:row",16,"~:col",1,"~:name","^0","~:ns","~$cljs.nodejs","~:top-ns","^5"],"~$process",["^ ","^1",17,"^2",1,"^3","^7","^4","^5","^6","^5"],"~$enable-util-print!",["^ ","^1",19,"^2",1,"~:fixed-arities",["~#set",[0]],"^3","^8","^4","^5","^6","^5","~:arities",["^ ","~i0",["^ ","~:ret","~:nil"]],"~:type","~:fn"],"~:filename","cljs/nodejs.cljs"] -------------------------------------------------------------------------------- /corpus/deftype.cljs: -------------------------------------------------------------------------------- 1 | (ns deftype) 2 | 3 | (deftype LazySeq2 [meta ^:mutable fx ^:mutable s ^:mutable __hash] 4 | Object 5 | (toString [coll] 6 | (pr-str* coll)) 7 | (equiv [this other] 8 | (-equiv this other)) 9 | (sval [coll] 10 | (if (nil? fn) 11 | s 12 | (do 13 | (set! s (fn)) 14 | (set! fn nil) 15 | s))) 16 | IPending 17 | (-realized? [coll] 18 | (not fn))) 19 | -------------------------------------------------------------------------------- /corpus/redundant_do.clj: -------------------------------------------------------------------------------- 1 | (ns redundant-do) 2 | 3 | (do) 4 | (do 1 (do 2)) 5 | (defn foo [] (do 1 2 3)) 6 | (fn [] (do 1 2)) 7 | (let [x 1] 1 2 (do 1 2 3)) 8 | (when (do 1 2 3)) ;; no mention of redundant do but mention of missing body in when 9 | (when :foo (do 1 2 3)) 10 | (when-not :foo (do 1 2 3)) 11 | (future (do 1 2)) 12 | (when-let [x 1] (do x 2)) 13 | (when-first [a [1 2 3]] (do 1 a)) 14 | (when-some [a 1] (do 2 a)) 15 | -------------------------------------------------------------------------------- /corpus/java/usages.clj: -------------------------------------------------------------------------------- 1 | (ns java.usages (:import [clojure.lang PersistentVector])) 2 | 3 | (try (catch Exception foo foo)) 4 | Thread/sleep 5 | (Thread/sleep 100) 6 | (Thread. (fn [])) 7 | PersistentVector 8 | 9 | (import '[clojure.lang Compiler]) 10 | Compiler/specials 11 | foo.bar.Baz 12 | foo.bar.Baz/EMPTY 13 | java.util.Date 14 | (java.io.File/createTempFile "foo" "bar") 15 | (java.io.File. "foo") 16 | (java.lang.String. "foo") 17 | -------------------------------------------------------------------------------- /corpus/record_protocol_metadata.clj: -------------------------------------------------------------------------------- 1 | (ns record-protocol-metadata 2 | (:import (com.newrelic.api.agent Trace))) ;; <= Trace flagged as unused import 3 | 4 | (defprotocol Handler 5 | (handle [this req] "Handle a Ring request.")) 6 | 7 | (defrecord NewRelicHandler [handler] 8 | Handler 9 | (^{Trace {:dispatcher true :transactionType "web"}} handle [_this req] ;; <= Trace used here as a Java Annotation 10 | (handler req))) 11 | -------------------------------------------------------------------------------- /src/clj_kondo/impl/findings.types.edn: -------------------------------------------------------------------------------- 1 | {reg-finding! 2 | {:arities {2 {:args [{:op :keys 3 | :req {:findings :atom}} 4 | {:op :keys 5 | :req {:filename :string 6 | :row :int 7 | :col :int 8 | :type :keyword 9 | :message :string}}] 10 | :ret :nil}}}} 11 | -------------------------------------------------------------------------------- /corpus/cond_without_else.clj: -------------------------------------------------------------------------------- 1 | (ns cond-without-else1 2 | (:refer-clojure :exclude [cond]) 3 | (:require [clojure.core :as c])) 4 | 5 | (def n (rand-int 10)) 6 | 7 | (c/cond 8 | (neg? n) "negative" 9 | :default "positive") 10 | 11 | (ns cond-without-else2) 12 | (def n (rand-int 10)) 13 | 14 | (cond 15 | (neg? n) "negative" 16 | :default "positive") 17 | 18 | ;; this one should not be caught: 19 | (cond 20 | (odd? n) 1 21 | :else 2) 22 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/clj/clojure.core.specs.alpha.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$even-number-of-forms?",["^ ","~:row",48,"~:col",1,"~:fixed-arities",["~#set",[1]],"~:name","^0","~:ns","~$clojure.core.specs.alpha","~:top-ns","^7","~:arities",["^ ","~i1",["^ ","~:ret","~:boolean"]],"~:type","~:fn"],"~$quotable",["^ ","^1",229,"^2",1,"~:macro",true,"~:private",true,"^3",["^4",[1]],"^5","^>","^6","^7","^8","^7"],"~:filename","clojure/core/specs/alpha.clj"] -------------------------------------------------------------------------------- /script/extract-versions: -------------------------------------------------------------------------------- 1 | export CLJ_KONDO_EXTRACT_CLJ_VERSION="1.12.0-alpha5" 2 | export CLJ_KONDO_EXTRACT_CLJS_VERSION="1.11.60" 3 | export CLJ_KONDO_EXTRACT_CORE_DEPS=" 4 | {:deps {org.clojure/clojure {:mvn/version \"$CLJ_KONDO_EXTRACT_CLJ_VERSION\"} 5 | org.clojure/clojurescript {:mvn/version \"$CLJ_KONDO_EXTRACT_CLJS_VERSION\"}}}" 6 | export CLJ_KONDO_EXTRACT_CORE_DEPS_PATH=$(clojure -Spath -Sdeps "$CLJ_KONDO_EXTRACT_CORE_DEPS" -A:built-in-cache) 7 | -------------------------------------------------------------------------------- /corpus/cond_without_else.cljc: -------------------------------------------------------------------------------- 1 | (ns cond-without-else1 2 | (:refer-clojure :exclude [cond]) 3 | (:require [clojure.core :as c])) 4 | 5 | (def n (rand-int 10)) 6 | 7 | (c/cond 8 | (neg? n) "negative" 9 | :default "positive") 10 | 11 | (ns cond-without-else2) 12 | (def n (rand-int 10)) 13 | 14 | (cond 15 | (neg? n) "negative" 16 | :default "positive") 17 | 18 | ;; this one should not be caught: 19 | (cond 20 | (odd? n) 1 21 | :else 2) 22 | -------------------------------------------------------------------------------- /corpus/cond_without_else.cljs: -------------------------------------------------------------------------------- 1 | (ns cond-without-else1 2 | (:refer-clojure :exclude [cond]) 3 | (:require [clojure.core :as c])) 4 | 5 | (def n (rand-int 10)) 6 | 7 | (c/cond 8 | (neg? n) "negative" 9 | :default "positive") 10 | 11 | (ns cond-without-else2) 12 | (def n (rand-int 10)) 13 | 14 | (cond 15 | (neg? n) "negative" 16 | :default "positive") 17 | 18 | ;; this one should not be caught: 19 | (cond 20 | (odd? n) 1 21 | :else 2) 22 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/cljs/clojure.core.protocols.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$Datafiable",["^ ","~:row",11,"~:col",1,"~:name","^0","~:ns","~$clojure.core.protocols","~:top-ns","^5"],"~$datafy",["^ ","^1",13,"^2",3,"~:fixed-arities",["~#set",[1]],"^3","^7","^4","^5","^6","^5"],"~$Navigable",["^ ","^1",22,"^2",1,"^3","^:","^4","^5","^6","^5"],"~$nav",["^ ","^1",24,"^2",3,"^8",["^9",[3]],"^3","^;","^4","^5","^6","^5"],"~:filename","clojure/core/protocols.cljs"] -------------------------------------------------------------------------------- /script/check_glibc.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | function ver_lte() { 4 | printf '%s\n%s' "$1" "$2" | sort -C -V 5 | } 6 | 7 | max_glibc_version="2.31" 8 | current_glibc_version=$(ldd --version | head -1 | awk '{print $4}' | cut -d "-" -f 1) 9 | 10 | function bail() { 11 | echo "glibc greater than max version ${max_glibc_version}: ${current_glibc_version}" 12 | exit 1 13 | } 14 | 15 | ver_lte "${current_glibc_version}" "${max_glibc_version}" || bail 16 | -------------------------------------------------------------------------------- /corpus/rename.cljc: -------------------------------------------------------------------------------- 1 | (ns rename 2 | (:refer-clojure :rename {update core-update} :exclude [conj]) 3 | (:require [clojure.string :rename {join foo}])) 4 | #?(:clj (core-update)) ;; <- arity error 5 | #?(:cljs (core-update)) ;; <- arity error 6 | #?(:clj conj) ;; <- unresolved 7 | #?(:cljs conj) ;; <- unresolved 8 | #?(:clj (join)) ;; <- unresolved 9 | #?(:cljs (join)) ;; <- unresolved 10 | #?(:clj (foo)) ;; <- arity error 11 | #?(:cljs (foo)) ;; <- arity error 12 | -------------------------------------------------------------------------------- /test/clj_kondo/uninitialized_var_test.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.uninitialized-var-test 2 | (:require 3 | [clj-kondo.test-utils :as tu :refer [lint! assert-submaps2]] 4 | [clojure.test :as t :refer [deftest is testing]])) 5 | 6 | (deftest uninitialized-var-test 7 | (assert-submaps2 8 | [{:row 1, 9 | :col 1, 10 | :level :warning, 11 | :message "Uninitialized var"}] 12 | (lint! "(def x)" {:linters {:uninitialized-var {:level :warning}}}))) 13 | 14 | -------------------------------------------------------------------------------- /corpus/redundant_let.clj: -------------------------------------------------------------------------------- 1 | (ns redundant-let) 2 | 3 | (let [x 1] 4 | (let [y 2])) 5 | 6 | (let [x 1] 7 | #_(println "hello") 8 | (let [y 2])) 9 | 10 | (let [x 1] 11 | ;; (println "hello") 12 | (let [y 2])) 13 | 14 | ;; this one should not be reported: 15 | (defn two-cases [] 16 | (let [resource 1] 17 | (let [result [1 (inc resource) 3]] 18 | (println (= 2 (count result)))) 19 | (let [result [1 (inc resource)]] 20 | (println (= 3 (count result)))))) 21 | -------------------------------------------------------------------------------- /corpus/refer_all.clj: -------------------------------------------------------------------------------- 1 | (ns funs) 2 | 3 | (defn foo [_]) 4 | (defn bar [_]) 5 | (defn baz [_]) 6 | 7 | (ns another-funs) 8 | 9 | (defn another-foo []) 10 | 11 | (ns refer-all 12 | (:require [funs :refer :all :exclude [baz] :rename {bar new-name}] :reload 13 | [another-funs :refer :all])) 14 | 15 | (foo) ;; caught 16 | (new-name) ;; caught 17 | (baz) ;; unrecognized call 18 | 19 | (bar) ;; not recognized 20 | 21 | (another-foo) ;; should be resolved without warning 22 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/clj/clojure.datafy.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$datafy",["^ ","~:row",15,"~:col",1,"~:fixed-arities",["~#set",[1]],"~:name","^0","~:ns","~$clojure.datafy","~:top-ns","^7","~:type","~:fn"],"~$nav",["^ ","^1",30,"^2",1,"^3",["^4",[3]],"^5","^;","^6","^7","^8","^7","^9","^:"],"~$sortmap",["^ ","^1",39,"^2",1,"~:private",true,"^3",["^4",[1]],"^5","^<","^6","^7","^8","^7","~:arities",["^ ","~i1",["^ ","~:ret","~:sorted-map"]],"^9","^:"],"~:filename","clojure/datafy.clj"] -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/cljs/clojure.datafy.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$datafy",["^ ","~:row",14,"~:col",1,"~:fixed-arities",["~#set",[1]],"~:name","^0","~:ns","~$clojure.datafy","~:top-ns","^7","~:type","~:fn"],"~$nav",["^ ","^1",31,"^2",1,"^3",["^4",[3]],"^5","^;","^6","^7","^8","^7","^9","^:"],"~$datify-ref",["^ ","^1",40,"^2",1,"~:private",true,"^3",["^4",[1]],"^5","^<","^6","^7","^8","^7","~:arities",["^ ","~i1",["^ ","~:ret","~:vector"]],"^9","^:"],"~:filename","clojure/datafy.cljs"] -------------------------------------------------------------------------------- /script/extract-versions.template: -------------------------------------------------------------------------------- 1 | export CLJ_KONDO_EXTRACT_CLJ_VERSION="{{extract-clj-version}}" 2 | export CLJ_KONDO_EXTRACT_CLJS_VERSION="{{extract-cljs-version}}" 3 | export CLJ_KONDO_EXTRACT_CORE_DEPS=" 4 | {:deps {org.clojure/clojure {:mvn/version \"$CLJ_KONDO_EXTRACT_CLJ_VERSION\"} 5 | org.clojure/clojurescript {:mvn/version \"$CLJ_KONDO_EXTRACT_CLJS_VERSION\"}}}" 6 | export CLJ_KONDO_EXTRACT_CORE_DEPS_PATH=$(clojure -Spath -Sdeps "$CLJ_KONDO_EXTRACT_CORE_DEPS" -A:built-in-cache) 7 | -------------------------------------------------------------------------------- /corpus/.clj-kondo/macroexpand2.clj: -------------------------------------------------------------------------------- 1 | (ns macroexpand2) 2 | 3 | (defmacro $ [op & args] 4 | (list* (symbol (str "." op)) 'sh args)) 5 | 6 | (defmacro form-env-macro [_] 7 | (list* 'clojure.core/+ 8 | [(when (= 'form-env-macro (first &form)) "foo") 9 | (when (contains? &env 'x) :foo)])) 10 | 11 | (defmacro private-defn [sym] 12 | `(defn ~(with-meta sym {:private true}) [] 13 | ;; redundant stuff that should not be reported :) 14 | (do (let [] (let []))))) 15 | -------------------------------------------------------------------------------- /src/clj_kondo/impl/parser.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.impl.parser 2 | {:no-doc true} 3 | (:require 4 | [clj-kondo.impl.utils :as utils :refer [parse-string-all]])) 5 | 6 | (defn parse-string [s] 7 | (parse-string-all s)) 8 | 9 | ;;;; Scratch 10 | 11 | (comment 12 | (parse-string "(+ 1 2 3) #_1 2 ;; no") 13 | (:meta (utils/parse-string "^{:a 1} [1 2 3]")) 14 | (:meta (utils/parse-string "^{:a 1} ^{:b 1} [1 2 3]")) 15 | (:meta (utils/parse-string "^{:a 1} ^{:b 1} [1 2 3]")) 16 | ) 17 | -------------------------------------------------------------------------------- /src/clj_kondo/impl/var_info.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.impl.var-info 2 | {:no-doc true} 3 | (:require [clj-kondo.impl.var-info-gen])) 4 | 5 | (declare clojure-core-syms cljs-core-syms) 6 | 7 | ;; in addition to what `special-form?` regards as special: 8 | (def special-forms '#{.. let fn loop}) 9 | 10 | (defn core-sym? [lang sym] 11 | (case lang 12 | :clj (contains? clojure-core-syms sym) 13 | :cljs (contains? cljs-core-syms sym))) 14 | 15 | ;;;; Scratch 16 | 17 | (comment 18 | ) 19 | -------------------------------------------------------------------------------- /Dockerfile.alpine: -------------------------------------------------------------------------------- 1 | FROM alpine:3 2 | 3 | COPY clj-kondo /bin/clj-kondo 4 | 5 | RUN chmod +x /bin/clj-kondo 6 | 7 | RUN apk --no-cache add curl ca-certificates tar && \ 8 | curl -Ls https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.28-r0/glibc-2.28-r0.apk > /tmp/glibc-2.28-r0.apk && \ 9 | apk add --allow-untrusted --force-overwrite /tmp/glibc-2.28-r0.apk 10 | RUN echo 'hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4' >> /etc/nsswitch.conf 11 | 12 | CMD ["clj-kondo"] 13 | -------------------------------------------------------------------------------- /corpus/cljc/test_cljc.cljc: -------------------------------------------------------------------------------- 1 | (ns corpus.cljc.test-cljc) 2 | 3 | #?(:clj (defmacro foo [x y] 4 | x) 5 | :cljs (defmacro foo [x] ;; self-hosted macro? :-) 6 | x)) 7 | 8 | ;; valid calls on lines 9 and 10: 9 | #?(:clj (foo 1 2) 10 | :cljs (foo 1)) 11 | 12 | ;; invalid calls on lines 13 and 14: 13 | #?(:clj (foo 1) 14 | :cljs (foo 1 2)) 15 | 16 | ;; bar is function that is callable from both CLJ and CLJS: 17 | (defn bar [x] 18 | x) 19 | 20 | (bar 1) 21 | (bar 1 2 3) 22 | -------------------------------------------------------------------------------- /parser/clj_kondo/impl/rewrite_clj/parser/string.clj: -------------------------------------------------------------------------------- 1 | (ns ^{:no-doc true} clj-kondo.impl.rewrite-clj.parser.string 2 | (:require 3 | [clj-kondo.impl.rewrite-clj.node :as node] 4 | [clj-kondo.impl.rewrite-clj.parser 5 | [utils :as u]] 6 | [clojure.string :as string])) 7 | 8 | (defn parse-string 9 | [reader] 10 | (node/string-node (u/read-string-data reader))) 11 | 12 | (defn parse-regex 13 | [reader] 14 | (let [h (u/read-string-data reader)] 15 | (string/join "\n" h))) 16 | -------------------------------------------------------------------------------- /corpus/jdbc/next_test.clj: -------------------------------------------------------------------------------- 1 | (ns jdbc.next-test 2 | (:require [next.jdbc :as jdbc])) 3 | 4 | (defn tx-check 5 | [] 6 | (let [db {:dbtype "some-db" :dbname "kondo"}] 7 | ;; should not flag tx 8 | (jdbc/with-transaction [tx (jdbc/get-datasource db)] 9 | (jdbc/execute! tx ["select * from table where foo = ?" 123])) 10 | ;; should not flag tx or binding arity 11 | (jdbc/with-transaction [tx (jdbc/get-datasource db) {}] 12 | (jdbc/execute! tx ["select * from table where foo = ?" 123])))) 13 | -------------------------------------------------------------------------------- /script/analysis-test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | echo "Analysis test (emit readable EDN)" 4 | rm -rf /tmp/kondo-test-analysis 5 | mkdir -p /tmp/kondo-test-analysis 6 | 7 | clojure -M:clj-kondo --config '{:output {:format :edn} :analysis {:locals true :keywords true :arglists true}}' --lint "$(clojure -Spath -A:cljs)" > /tmp/kondo-test-analysis/out.edn 8 | 9 | set -eo pipefail 10 | 11 | clojure -M -e '(do (require (quote [clojure.edn :as edn])) (edn/read-string (slurp "/tmp/kondo-test-analysis/out.edn")) nil)' 12 | -------------------------------------------------------------------------------- /corpus/import_vars.clj: -------------------------------------------------------------------------------- 1 | (ns app.core) 2 | 3 | (defn foo [_x]) ;; 1 arg 4 | 5 | (ns app.api 6 | (:require 7 | [clojure.walk] 8 | [clojure.data] 9 | [app.core] 10 | [potemkin :refer [import-vars]])) 11 | 12 | (import-vars 13 | [clojure.walk 14 | prewalk 15 | postwalk] 16 | [clojure.data 17 | diff] 18 | [app.core foo]) 19 | 20 | (ns consumer 21 | (:require [app.api :refer [prewalk foo]])) 22 | 23 | (prewalk) ;; wrong arity for clojure.walk/prewalk 24 | (foo) ;; wrong arity for app.core/foo 25 | -------------------------------------------------------------------------------- /test/clj_kondo/equals_true_test.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.equals-true-test 2 | (:require 3 | [clj-kondo.test-utils :refer [assert-submaps2 lint!]] 4 | [clojure.test :refer [deftest is testing]])) 5 | 6 | (deftest equals-true-test 7 | (assert-submaps2 8 | '({:file "", :row 1, :col 1, :level :warning, :message "Prefer (true? x) over (= true x)"}) 9 | (lint! "(= true 1)" {:linters {:equals-true {:level :warning}}})) 10 | (is (empty? (lint! "(= true 1 1)" {:linters {:equals-true {:level :warning}}})))) 11 | -------------------------------------------------------------------------------- /test/clj_kondo/equals_false_test.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.equals-false-test 2 | (:require 3 | [clj-kondo.test-utils :refer [assert-submaps2 lint!]] 4 | [clojure.test :refer [deftest is testing]])) 5 | 6 | (deftest equals-false-test 7 | (assert-submaps2 8 | '({:file "", :row 1, :col 1, :level :warning, :message "Prefer (false? x) over (= false x)"}) 9 | (lint! "(= false 1)" {:linters {:equals-false {:level :warning}}})) 10 | (is (empty? (lint! "(= false 1 1)" {:linters {:equals-false {:level :warning}}})))) 11 | -------------------------------------------------------------------------------- /.clj-kondo/hooks/one_of.clj: -------------------------------------------------------------------------------- 1 | ;; (one-of x [foo bar]), foo bar are literal symbols 2 | 3 | (ns hooks.one-of 4 | (:require [clj-kondo.hooks-api :as api])) 5 | 6 | (defn one-of [{:keys [node]}] 7 | (let [[matchee matches] (rest (:children node)) 8 | new-node (api/list-node 9 | [(api/token-node 'case) 10 | matchee 11 | (with-meta (api/list-node (:children matches)) 12 | (meta matches)) 13 | matchee])] 14 | {:node new-node})) 15 | -------------------------------------------------------------------------------- /script/lint-gh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | user="$1" 4 | project="$2" 5 | if [ -z "$project" ]; then 6 | echo "Usage: username project" 7 | exit 1 8 | fi 9 | 10 | cd /tmp 11 | git clone "https://github.com/$user/$project" || true 12 | cd "$project" 13 | git pull 14 | 15 | if [ -f project.clj ]; then 16 | lein_classpath="$(lein classpath)" 17 | fi 18 | clj_classpath="$(clojure -Spath)" 19 | 20 | classpath="$lein_classpath:$clj_classpath" 21 | # echo "Linting $classpath" 22 | 23 | clj-kondo --lint "$classpath" src test resources 24 | -------------------------------------------------------------------------------- /corpus/deprecated_var.clj: -------------------------------------------------------------------------------- 1 | (ns deprecated-var) 2 | 3 | (ns foo.foo) (defn ^:deprecated deprecated-fn []) 4 | (ns foo.bar 5 | (:require [foo.foo :refer [deprecated-fn]])) 6 | (deprecated-fn) ;; <- unreported 7 | 8 | (ns foo.baz 9 | (:require [foo.foo :refer [deprecated-fn]])) 10 | (deprecated-fn) ;; <- reported 11 | (defn allowed [] 12 | (deprecated-fn)) ;; <- unreported 13 | (defn ignore [] 14 | (deprecated-fn)) ;; <- unreported 15 | 16 | (ns bar.bar 17 | (:require [foo.foo :refer [deprecated-fn]])) 18 | (deprecated-fn) ;; <- unreported 19 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/clj/clojure.repl.deps.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$add-loader-url",["^ ","~:row",22,"~:col",1,"~:private",true,"~:fixed-arities",["~#set",[1]],"~:name","^0","~:ns","~$clojure.repl.deps","~:top-ns","^8","~:type","~:fn"],"~$add-libs",["^ ","^1",35,"^2",1,"^4",["^5",[1]],"^6","^<","^7","^8","^9","^8","^:","^;"],"~$add-lib",["^ ","^1",57,"^2",1,"^4",["^5",[1,2]],"^6","^=","^7","^8","^9","^8","^:","^;"],"~$sync-deps",["^ ","^1",83,"^2",1,"~:varargs-min-arity",0,"^6","^>","^7","^8","^9","^8","^:","^;"],"~:filename","clojure/repl/deps.clj"] -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/cljc/cljs.analyzer.passes.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~:filename","cljs/analyzer/passes.cljc","~:clj",["^ ","~$apply-passes",["^ ","~:row",11,"~:col",1,"~:fixed-arities",["~#set",[3,2]],"~:name","^2","~:ns","~$cljs.analyzer.passes","~:top-ns","^9","~:type","~:fn"],"~$walk",["^ ","^3",20,"^4",1,"^5",["^6",[3,2]],"^7","^=","^8","^9","^:","^9","^;","^<"]],"~:cljs",["^ ","^2",["^ ","^3",11,"^4",1,"^5",["^6",[3,2]],"^7","^2","^8","^9","^:","^9","^;","^<"],"^=",["^ ","^3",20,"^4",1,"^5",["^6",[3,2]],"^7","^=","^8","^9","^:","^9","^;","^<"]]] -------------------------------------------------------------------------------- /inlined/clj_kondo/impl/toolsreader/v1v2v2/cljs/tools/reader/reader_types.clj: -------------------------------------------------------------------------------- 1 | (ns ^{:no-doc true} clj-kondo.impl.toolsreader.v1v2v2.cljs.tools.reader.reader-types) 2 | 3 | (defmacro log-source 4 | "If reader is a SourceLoggingPushbackReader, execute body in a source 5 | logging context. Otherwise, execute body, returning the result." 6 | [reader & body] 7 | `(if (and (source-logging-reader? ~reader) 8 | (not (cljs.tools.reader.impl.utils/whitespace? (peek-char ~reader)))) 9 | (log-source* ~reader (^:once fn* [] ~@body)) 10 | (do ~@body))) -------------------------------------------------------------------------------- /src/clj_kondo/impl/config.types.edn: -------------------------------------------------------------------------------- 1 | {lint-as-config {:arities {1 {:args [:map]}}} 2 | merge-config! {:arities {2 {:args [:nilable/map :nilable/map] 3 | :ret :nilable/map}}} 4 | fq-syms->vecs {:arities {1 {:args [:seqable] 5 | :ret :seqable}}} 6 | skip-args {:arities {1 {:args [:map] 7 | :ret :seqable} 8 | 2 {:args [:map :keyword] 9 | :ret :seqable}}} 10 | skip? {:arities {2 {:args [:map :seqable] 11 | :ret :boolean}}}} 12 | -------------------------------------------------------------------------------- /.pre-commit-hooks.yaml.template: -------------------------------------------------------------------------------- 1 | --- 2 | - id: clj-kondo 3 | name: clj-kondo 4 | description: '`clj-kondo` is a command-line utility for enforcing style consistency across Clojure projects.' 5 | entry: clj-kondo --lint 6 | language: system 7 | types: [clojure] 8 | - id: clj-kondo-docker 9 | name: clj-kondo (via docker) 10 | description: '`clj-kondo` is a command-line utility for enforcing style consistency across Clojure projects.' 11 | entry: cljkondo/clj-kondo:{{version}} 12 | args: [clj-kondo, --lint] 13 | language: docker_image 14 | types: [clojure] 15 | -------------------------------------------------------------------------------- /corpus/use.clj: -------------------------------------------------------------------------------- 1 | (ns use) 2 | 3 | (ns foo1 4 | (:use [clojure.string])) ;; this message contains join 5 | 6 | join 7 | 8 | (ns foo1b 9 | (:use clojure.string)) ;; this message contains join 10 | 11 | join 12 | 13 | (ns foo1c 14 | (:use clojure.set)) ;; this message also contains join, but from clojure set 15 | 16 | join 17 | 18 | (ns foo2 19 | (:use [clojure.string :only [join]])) 20 | 21 | (ns foo2b) 22 | (use '[clojure.string :only [join]]) 23 | 24 | (ns foo4) 25 | (use 'clojure.set) 26 | join 27 | 28 | (ns foo6) 29 | (use '[clojure.string]) 30 | join 31 | 32 | -------------------------------------------------------------------------------- /test/clj_kondo/babashka_test.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.babashka-test 2 | (:require 3 | [clj-kondo.test-utils :refer 4 | [lint! assert-submaps]] 5 | [clojure.java.io :as io] 6 | [clojure.test :as t :refer [deftest is testing]])) 7 | 8 | (deftest format-test 9 | (assert-submaps 10 | '({:file "corpus/babashka_script.clj", :row 16, :col 9, :level :error, :message "Unresolved symbol: x"}) 11 | (lint! (io/file "corpus" "babashka_script.clj") 12 | {:linters {:unresolved-symbol {:level :error} 13 | :unused-binding {:level :warning}}}))) 14 | -------------------------------------------------------------------------------- /corpus/metadata.clj: -------------------------------------------------------------------------------- 1 | (ns repro 2 | (:require [clojure.test :refer [is testing]])) 3 | 4 | (defn pretty-print 5 | {:test (fn [] 6 | (testing "Default printing options" 7 | (let [res (as-> (repeat 1 "foo") x x)] 8 | (is res))))} 9 | []) 10 | 11 | (defn ^{:test (fn [] 12 | (testing "Default printing options" 13 | (let [res2 (as-> (repeat 1 "foo") x x)] 14 | (println "testing!") 15 | (is res2))))} 16 | pretty-print2 17 | []) 18 | 19 | ((:test (meta #'pretty-print2))) 20 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/cljs/cljs.source-map.base64.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$chars64",["^ ","~:row",11,"~:col",1,"~:name","^0","~:ns","~$cljs.source-map.base64","~:top-ns","^5","~:type","~:string"],"~$char->int",["^ ","^1",12,"^2",1,"^3","^9","^4","^5","^6","^5","^7","~:map"],"~$int->char",["^ ","^1",13,"^2",1,"^3","^;","^4","^5","^6","^5","^7","^:"],"~$encode",["^ ","^1",15,"^2",1,"~:fixed-arities",["~#set",[1]],"^3","^<","^4","^5","^6","^5","^7","~:fn"],"~$decode",["^ ","^1",21,"^2",1,"^=",["^>",[1]],"^3","^@","^4","^5","^6","^5","^7","^?"],"~:filename","cljs/source_map/base64.cljs"] -------------------------------------------------------------------------------- /script/test.bat: -------------------------------------------------------------------------------- 1 | rem force download of clojure 1.10.1 for extraction test 2 | call lein with-profiles +clojure-1.10.2 deps > NUL 3 | 4 | echo "CLJ_KONDO_TEST_ENV: %CLJ_KONDO_TEST_ENV%" 5 | 6 | IF "%CLJ_KONDO_TEST_ENV%"=="native" ( 7 | lein do clean, test 8 | ) ELSE ( 9 | rem else branch 10 | echo "Testing with Clojure 1.9.0" 11 | rem clojure -A:clojure-1.9.0:test 12 | call lein with-profiles +clojure-1.9.0 do clean, test 13 | 14 | echo "Testing with Clojure 1.10.2" 15 | rem clojure -A:clojure-1.10.1:test 16 | call lein with-profiles +clojure-1.10.2 do clean, test 17 | ) 18 | -------------------------------------------------------------------------------- /script/test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | 5 | # shellcheck disable=1091 6 | source script/extract-versions 7 | 8 | if [ "$CLJ_KONDO_TEST_ENV" = "native" ]; then 9 | clojure -M:test 10 | else 11 | echo "Testing with Clojure 1.9.0" 12 | clojure -Sforce -M:clojure-1.9.0:test 13 | lein with-profiles +clojure-1.9.0 "do" clean, test 14 | 15 | echo "Testing with Clojure 1.10.2" 16 | clojure -Sforce -M:clojure-1.10.2:test 17 | lein with-profiles +clojure-1.10.2 "do" clean, test 18 | fi 19 | 20 | echo "Pod test" 21 | clojure -M:test:pod-test -d pod-test 22 | -------------------------------------------------------------------------------- /analysis/src/clj_kondo/tools/popular_vars.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.tools.popular-vars 2 | (:require [clj-kondo.core :as clj-kondo])) 3 | 4 | (defn -main [n & paths] 5 | (let [n (Integer. n) 6 | analysis (:analysis (clj-kondo/run! {:lint paths 7 | :config {:analysis true}})) 8 | {:keys [:var-usages]} analysis 9 | vars (map (juxt :to :name) var-usages) 10 | freqs (frequencies vars) 11 | freqs (sort-by second > freqs) 12 | top-n (take n freqs)] 13 | (doseq [[[ns name] n] top-n] 14 | (println (str ns "/" name ": " n))))) 15 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/clj/clojure.java.basis.impl.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$read-edn",["^ ","~:row",18,"~:col",1,"~:private",true,"~:fixed-arities",["~#set",[1]],"~:name","^0","~:ns","~$clojure.java.basis.impl","~:top-ns","^8","~:type","~:fn"],"~$read-basis",["^ ","^1",33,"^2",1,"^3",true,"^4",["^5",[1]],"^6","^<","^7","^8","^9","^8","^:","^;"],"~$init-basis",["^ ","^1",41,"^2",1,"^6","^=","^7","^8","^9","^8"],"~$the-basis",["^ ","^1",45,"^2",1,"^6","^>","^7","^8","^9","^8"],"~$update-basis!",["^ ","^1",48,"^2",1,"~:varargs-min-arity",1,"^6","^?","^7","^8","^9","^8","^:","^;"],"~:filename","clojure/java/basis/impl.clj"] -------------------------------------------------------------------------------- /corpus/.clj-kondo/macroexpand/weird_macro.clj: -------------------------------------------------------------------------------- 1 | (ns macroexpand.weird-macro 2 | (:require [clj-kondo.hooks-api :as api])) 3 | 4 | (defn weird-macro [{:keys [:node]}] 5 | (let [[binding-vec & body] (rest (:children node)) 6 | [sym val opts] (:children binding-vec)] 7 | (when-not (and sym val) 8 | (throw (ex-info "No sym and val provided" {}))) 9 | (let [new-node (api/list-node 10 | (list* 11 | (api/token-node 'let) 12 | (api/vector-node [sym val]) 13 | opts 14 | body))] 15 | {:node new-node}))) 16 | -------------------------------------------------------------------------------- /corpus/defprotocol.clj: -------------------------------------------------------------------------------- 1 | (ns defprotocol) 2 | 3 | (defprotocol ^{:added "1.3"} Foo 4 | "This is my protocol" 5 | (^{:added "1.3"} -foo [this] [this x] [this x y] "foo docs")) 6 | 7 | (extend-protocol Foo 8 | nil 9 | (-foo ([this]) ([this x]) ([this x y]))) 10 | 11 | (-foo nil) 12 | (-foo nil 1) 13 | (-foo nil 1 2) 14 | (-foo nil 1 2 3) ;; wrong 15 | 16 | ;; #314: 17 | (defprotocol IntoInputStream 18 | (into-input-stream ^java.io.InputStream [this])) 19 | 20 | (ns useprotocol (:require [defprotocol :as protocols])) 21 | 22 | ;; #314: 23 | (fn [x] 24 | (some-> x protocols/into-input-stream clojure.core/slurp)) 25 | -------------------------------------------------------------------------------- /analysis/src/clj_kondo/tools/missing_docstrings.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.tools.missing-docstrings 2 | (:require [clj-kondo.core :as clj-kondo])) 3 | 4 | (defn -main [& paths] 5 | (let [analysis (:analysis (clj-kondo/run! {:lint paths 6 | :config {:analysis {:var-usages false}} 7 | :skip-lint true})) 8 | {:keys [:var-definitions]} analysis] 9 | (doseq [{:keys [:ns :name :private :doc]} var-definitions] 10 | (when (and (not private) 11 | (not doc)) 12 | (println (str ns "/" name ": missing docstring")))))) 13 | -------------------------------------------------------------------------------- /corpus/aliased_namespaces/interop.cljc: -------------------------------------------------------------------------------- 1 | (ns interop 2 | {:no-doc true} 3 | (:refer-clojure :exclude [eval demunge var?]) 4 | (:require [clojure.string :as str] 5 | [sci.impl.macros :as macros] 6 | [sci.impl.types :as t] 7 | [sci.impl.vars :as vars] 8 | [sci.lang :as lang])) 9 | 10 | (defn dynamic-var 11 | ([name] 12 | (dynamic-var name nil (meta name))) 13 | ([name init-val] 14 | (dynamic-var name init-val (meta name))) 15 | ([name init-val meta] 16 | (let [meta (assoc meta :dynamic true :name (identity name))] 17 | (sci.lang.Var. init-val name meta false false)))) 18 | -------------------------------------------------------------------------------- /doc/docker.md: -------------------------------------------------------------------------------- 1 | # Docker 2 | 3 | To run with Docker: 4 | 5 | docker run -v $PWD/src:/src --rm cljkondo/clj-kondo clj-kondo --lint src 6 | 7 | To lint an entire project including dependencies, you can mount your Maven 8 | dependencies into the container: 9 | 10 | docker run -v $PWD/src:/src -v $HOME/.m2:$HOME/.m2 --rm cljkondo/clj-kondo \ 11 | clj-kondo --lint $(clj -Spath) 12 | 13 | To lint stdin: 14 | 15 | echo '(select-keys)' | docker run -i --rm cljkondo/clj-kondo clj-kondo --lint - 16 | 17 | You can use the `latest` tag to get the latest non-SNAPSHOT release. See [tags](https://hub.docker.com/r/cljkondo/clj-kondo/tags). 18 | -------------------------------------------------------------------------------- /corpus/core_async/alt.clj: -------------------------------------------------------------------------------- 1 | (ns core-async.alt 2 | (:require [clojure.core.async :as a] 3 | [clojure.string :as str])) 4 | 5 | (def c (a/chan)) (def t (a/timeout 10000)) 6 | (a/alt! [c t] ([v ch] [ch v]) ;; no unresolved symbol warnings 7 | x1 x2) ;; unresolved symbols 8 | 9 | ;; no unresolved symbol warnings 10 | ;; namespace clojure.string is loaded from cache, so invalid arity 11 | (a/alt!! [c t] ([v ch] (str/join "\n" [ch v] 1)) 12 | x3 x4) ;; unresolved symbols 13 | 14 | (loop [] 15 | (a/alt!! 16 | (a/timeout 1000) 17 | ([v _ch] 18 | (println "got" v) 19 | (recur)))) ;; no invalid arity for recur 20 | -------------------------------------------------------------------------------- /corpus/namespace_config/src/macros.clj: -------------------------------------------------------------------------------- 1 | (ns macros 2 | {:clj-kondo/ignore [:unused-binding]}) 3 | 4 | (let [x 1]) ;; no warning about unused binding 5 | (inc :foo) ;; this warning is still displayed 6 | 7 | (defmacro with-foo 8 | {:clj-kondo/lint-as 'clojure.core/let} 9 | [bnds & body] 10 | `(let [~@bnds] 11 | ~@body)) 12 | 13 | (with-foo [a 1] 14 | a) ;; no warning, macro configured as clojure.core/let 15 | 16 | (defmacro matcher 17 | {:clj-kondo/ignore [:unresolved-symbol :type-mismatch]} 18 | [m match-expr & body] 19 | ;; dummy 20 | [m match-expr body]) 21 | 22 | (matcher {:a 1} {?a 1} (inc :foo)) ;; no warning in usage of macro 23 | -------------------------------------------------------------------------------- /corpus/.clj-kondo/hooks/re_frame.clj: -------------------------------------------------------------------------------- 1 | (ns hooks.re-frame 2 | (:require [clj-kondo.hooks-api :as api])) 3 | 4 | (defn dispatch [{:keys [:node]}] 5 | (let [sexpr (api/sexpr node) 6 | event (second sexpr) 7 | kw (first event)] 8 | (when (and (vector? event) 9 | (keyword? kw) 10 | (not (qualified-keyword? kw))) 11 | (let [{:keys [:row :col]} (some-> node :children second :children first meta)] 12 | (api/reg-finding! {:message "keyword should be fully qualified!" 13 | :type :re-frame/keyword 14 | :row row 15 | :col col}))))) 16 | -------------------------------------------------------------------------------- /parser/clj_kondo/impl/rewrite_clj/node/regex.clj: -------------------------------------------------------------------------------- 1 | (ns ^{:no-doc true} clj-kondo.impl.rewrite-clj.node.regex 2 | (:require [clj-kondo.impl.rewrite-clj.node.protocols :as node])) 3 | 4 | 5 | ;; ## Node 6 | 7 | (defrecord RegexNode [pattern] 8 | clj-kondo.impl.rewrite-clj.node.protocols/Node 9 | (tag [_] :regex) 10 | (printable-only? [_] false) 11 | (sexpr [_] (list 're-pattern pattern)) 12 | (length [_] 1) 13 | (string [_] (str "#\"" pattern "\""))) 14 | 15 | (node/make-printable! RegexNode) 16 | 17 | ;; ## Constructor 18 | 19 | (defn regex-node 20 | "Create node representing a regex" 21 | [pattern-string] 22 | (->RegexNode pattern-string)) 23 | -------------------------------------------------------------------------------- /snapcraft.yaml: -------------------------------------------------------------------------------- 1 | # build: snapcraft 2 | # test: https://snapcraft.io/first-snap/pre-built/macos/test 3 | # push: snapcraft push clj-kondo_..._.snap 4 | name: clj-kondo 5 | version: 2019.07.31-alpha 6 | summary: A linter for Clojure code that sparks joy. 7 | description: | 8 | A linter for Clojure code that sparks joy. 9 | confinement: strict 10 | base: core18 11 | grade: stable 12 | 13 | parts: 14 | clj-kondo: 15 | plugin: dump 16 | source: https://github.com/clj-kondo/clj-kondo/releases/download/v$SNAPCRAFT_PROJECT_VERSION/clj-kondo-$SNAPCRAFT_PROJECT_VERSION-linux-amd64.zip 17 | 18 | apps: 19 | clj-kondo: 20 | command: clj-kondo 21 | plugs: [home] 22 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/cljc/cljs.core.specs.alpha.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~:filename","cljs/core/specs/alpha.cljc","~:clj",["^ ","~$even-number-of-forms?",["^ ","~:row",58,"~:col",1,"~:fixed-arities",["~#set",[1]],"~:name","^2","~:ns","~$cljs.core.specs.alpha","~:top-ns","^9","~:arities",["^ ","~i1",["^ ","~:ret","~:boolean"]],"~:type","~:fn"],"~$quoted",["^ ","^3",209,"^4",1,"~:private",true,"^5",["^6",[1]],"^7","^@","^8","^9","^:","^9","^>","^?"]],"~:cljs",["^ ","^2",["^ ","^3",58,"^4",1,"^5",["^6",[1]],"^7","^2","^8","^9","^:","^9","^;",["^ ","~i1",["^ ","^<","^="]],"^>","^?"],"^@",["^ ","^3",209,"^4",1,"^A",true,"^5",["^6",[1]],"^7","^@","^8","^9","^:","^9","^>","^?"]]] -------------------------------------------------------------------------------- /corpus/issue-1996/.clj-kondo/hooks.clj: -------------------------------------------------------------------------------- 1 | (ns hooks 2 | (:require [clj-kondo.hooks-api :as api])) 3 | 4 | (defn lint-with-redefs [{:keys [name ns]} expr] 5 | (when (and (= ns 'clojure.core) 6 | (= name 'with-redefs)) 7 | (api/reg-finding! (assoc (meta expr) 8 | :type :discouraged-var 9 | :message "Don't use with-redefs")))) 10 | 11 | (defn my-test [{:keys [node]}] 12 | (let [[core-wrd foo-wrd] (rest (:children node))] 13 | (lint-with-redefs (api/resolve {:name (api/sexpr (first (:children core-wrd)))}) core-wrd) 14 | (lint-with-redefs (api/resolve {:name (api/sexpr (first (:children foo-wrd)))}) foo-wrd))) 15 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/cljs/cljs.repl.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$print-doc",["^ ","~:row",15,"~:col",1,"~:fixed-arities",["~#set",[1]],"~:name","^0","~:ns","~$cljs.repl","~:top-ns","^7","~:type","~:fn"],"~$Error->map",["^ ","^1",62,"^2",1,"^3",["^4",[1]],"^5","^;","^6","^7","^8","^7","~:arities",["^ ","~i1",["^ ","~:ret","~:nilable/map"]],"^9","^:"],"~$ex-triage",["^ ","^1",99,"^2",1,"^3",["^4",[1]],"^5","^?","^6","^7","^8","^7","^<",["^ ","~i1",["^ ","^=","~:associative"]],"^9","^:"],"~$ex-str",["^ ","^1",156,"^2",1,"^3",["^4",[1]],"^5","^A","^6","^7","^8","^7","^9","^:"],"~$error->str",["^ ","^1",229,"^2",1,"^3",["^4",[1]],"^5","^B","^6","^7","^8","^7","^9","^:"],"~:filename","cljs/repl.cljs"] -------------------------------------------------------------------------------- /corpus/schema/defrecord.clj: -------------------------------------------------------------------------------- 1 | (ns schema.defrecord 2 | (:require [schema.core :as s])) 3 | 4 | (s/defrecord StampedNames 5 | [date :- Long 6 | names :- [s/Str]]) 7 | 8 | (s/defn stamped-names :- StampedNames 9 | [names :- [s/Str]] 10 | (StampedNames. (str (System/currentTimeMillis)) names)) 11 | 12 | (stamped-names ["foo" "bar"]) 13 | 14 | ->StampedNames 15 | map->StampedNames 16 | 17 | (s/defrecord Record2 18 | [_] 19 | {:a s/Int 20 | :b s/Int}) ;; ok 21 | 22 | (s/defrecord ErrorRecord 23 | [x] 24 | {(s/optional-key :a) s/Int 25 | (s/optional-key :b) s/Int} ;; ok 26 | (fn [x] x) ;; extra validator 27 | clojure.lang.IFn 28 | (applyTo [_ xs] (apply + x xs))) 29 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | Please answer the following questions and leave the below in as part of your PR. 2 | 3 | - [ ] I have read the [developer documentation](https://github.com/clj-kondo/clj-kondo/blob/master/doc/dev.md). 4 | 5 | - [ ] This PR corresponds to an [issue with a clear problem statement](https://github.com/clj-kondo/clj-kondo/blob/master/doc/dev.md#start-with-an-issue-before-writing-code). 6 | 7 | - [ ] This PR contains a [test](https://github.com/clj-kondo/clj-kondo/blob/master/doc/dev.md#tests) to prevent against future regressions 8 | 9 | - [ ] I have updated the [CHANGELOG.md](https://github.com/clj-kondo/clj-kondo/blob/master/CHANGELOG.md) file with a description of the addressed issue. 10 | -------------------------------------------------------------------------------- /.circleci/script/release: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | 5 | rm -rf /tmp/release 6 | mkdir -p /tmp/release 7 | cp clj-kondo /tmp/release 8 | 9 | VERSION=$(cat resources/CLJ_KONDO_VERSION) 10 | 11 | if [[ "$CLJ_KONDO_PLATFORM" = "linux" ]]; then 12 | jar="target/clj-kondo-$VERSION-standalone.jar" 13 | bb release-artifact "$jar" 14 | cp "$jar" /tmp/release 15 | fi 16 | 17 | cd /tmp/release 18 | 19 | ## release binary as zip archive 20 | 21 | archive="clj-kondo-$VERSION-$CLJ_KONDO_PLATFORM-${CLJ_KONDO_ARCH:-amd64}.zip" 22 | 23 | zip "$archive" clj-kondo 24 | 25 | cd - 26 | 27 | bb release-artifact "/tmp/release/$archive" 28 | 29 | ## cleanup 30 | 31 | cd /tmp/release 32 | rm clj-kondo 33 | -------------------------------------------------------------------------------- /.pre-commit-hooks.yaml: -------------------------------------------------------------------------------- 1 | # GENERATED by script/update-project.clj, DO NOT EDIT 2 | # To change hooks, edit .pre-commit-hooks.yaml.template and run script/update-templates.clj. 3 | 4 | --- 5 | - id: clj-kondo 6 | name: clj-kondo 7 | description: '`clj-kondo` is a command-line utility for enforcing style consistency across Clojure projects.' 8 | entry: clj-kondo --lint 9 | language: system 10 | types: [clojure] 11 | - id: clj-kondo-docker 12 | name: clj-kondo (via docker) 13 | description: '`clj-kondo` is a command-line utility for enforcing style consistency across Clojure projects.' 14 | entry: cljkondo/clj-kondo:2023.12.15 15 | args: [clj-kondo, --lint] 16 | language: docker_image 17 | types: [clojure] 18 | -------------------------------------------------------------------------------- /corpus/babashka_script.clj: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bb 2 | 3 | (require '[babashka.process :refer [$ check]]) 4 | 5 | (def out-page (str "gh-pages/" 6 | (or (System/getenv "BABASHKA_BOOK_MAIN") 7 | "master") 8 | ".html")) 9 | 10 | (-> ($ asciidoctor src/book.adoc -o ~out-page -a docinfo=shared) 11 | check) 12 | 13 | (binding [*out* *err*] 14 | (println "Done writing to" out-page)) 15 | 16 | (-> ($ ~x) ;; unresolved 17 | check) 18 | 19 | (defn example 20 | [] 21 | (let [env {"SOME_VAR" "wee"}] ;; not unused 22 | ;; unused binding env 23 | (-> ($ {:out :string :env env} echo "hi") 24 | (check) 25 | (:out) 26 | (println)))) 27 | -------------------------------------------------------------------------------- /corpus/with_redefs.clj: -------------------------------------------------------------------------------- 1 | (ns with-redefs 2 | (:require [environ.core :refer [env]] 3 | [foo :refer [some-constant]] 4 | [utils.kafka :refer [send-message]] 5 | [utils :refer [fn-1 fn-2 fn-3 fn-4]])) 6 | 7 | (defn ^:private mock-fn 8 | [& args] 9 | args) 10 | 11 | (defn with-updated-system 12 | [f] 13 | (let [updated-env (assoc env :flag true)] 14 | (with-redefs [fn-1 (constantly identity) 15 | fn-2 (constantly identity) 16 | fn-3 (constantly identity) 17 | fn-4 (constantly identity) 18 | send-message mock-fn 19 | env updated-env 20 | some-constant 100] 21 | (f)))) 22 | -------------------------------------------------------------------------------- /test/clj_kondo/duplicate_require_test.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.duplicate-require-test 2 | (:require [clj-kondo.core :as clj-kondo] 3 | [clj-kondo.test-utils :refer [assert-submaps]] 4 | [clojure.test :refer [deftest is testing]])) 5 | 6 | (deftest duplicate-require-test 7 | (assert-submaps 8 | '({:type :duplicate-require 9 | :filename "", 10 | :duplicate-ns clojure.string 11 | :row 1, 12 | :col 43, 13 | :level :warning, 14 | :message "duplicate require of clojure.string"}) 15 | (-> (with-in-str 16 | "(ns foo (:require [clojure.string :as s] [clojure.string :as str])) s/join" 17 | (clj-kondo/run! {:lint ["-"]})) 18 | :findings))) 19 | -------------------------------------------------------------------------------- /corpus/compojure/core.clj: -------------------------------------------------------------------------------- 1 | (ns compojure.core 2 | (:require macro foo)) 3 | 4 | (defmacro defroutes 5 | "Define a Ring handler function from a sequence of routes. The name may 6 | optionally be followed by a doc-string and metadata map." 7 | [name & routes] 8 | (let [[name routes] (macro/name-with-attributes name routes)] 9 | `(def ~name (routes ~@routes)))) 10 | 11 | (defmacro GET "Generate a `GET` route." 12 | [path args & body] 13 | (foo/compile-route :get path args body)) 14 | 15 | (defmacro POST "Generate a `POST` route." 16 | [path args & body] 17 | (foo/compile-route :post path args body)) 18 | 19 | (declare DELETE PUT ANY make-routes routes wrap-routes let-routes context make-route rfn routing) 20 | -------------------------------------------------------------------------------- /doc/cljdoc.edn: -------------------------------------------------------------------------------- 1 | {:cljdoc.doc/tree 2 | [["Readme" {:file "README.md"}] 3 | ["Changelog" {:file "CHANGELOG.md"}] 4 | ["Installation" {:file "doc/install.md"}] 5 | ["Running on the JVM" {:file "doc/jvm.md"}] 6 | ["Running with Docker" {:file "doc/docker.md"}] 7 | ["Configuration" {:file "doc/config.md"}] 8 | ["Linters" {:file "doc/linters.md"}] 9 | ["Types" {:file "doc/types.md"}] 10 | ["Hooks" {:file "doc/hooks.md"}] 11 | ["Analysis data" {:file "analysis/README.md"}] 12 | ["Editor integration" {:file "doc/editor-integration.md"}] 13 | ["CI integration" {:file "doc/ci-integration.md"}] 14 | ["Developer documentation" {:file "doc/dev.md"}] 15 | ["Building from source" {:file "doc/build.md"}] 16 | ["Used by" {:file "doc/companies.md"}]]} 17 | -------------------------------------------------------------------------------- /script/cljdoc-preview: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | rm -rf /tmp/cljdoc 4 | mkdir -p /tmp/cljdoc 5 | version=$(cat resources/CLJ_KONDO_VERSION) 6 | 7 | echo "---- cljdoc preview: installing jar in local repo" 8 | lein install 9 | 10 | echo "---- cljdoc preview: ingesting clj-kondo" 11 | docker run --rm -v "$PWD:/clj-kondo" \ 12 | -v "$HOME/.m2:/root/.m2" -v /tmp/cljdoc:/app/data --entrypoint "clojure" \ 13 | cljdoc/cljdoc -A:cli ingest -p clj-kondo/clj-kondo -v "$version" \ 14 | --git /clj-kondo 15 | 16 | echo "---- cljdoc preview: starting server on port 8000" 17 | docker run --rm -p 8000:8000 -v /tmp/cljdoc:/app/data cljdoc/cljdoc 18 | # go directly to http://localhost:8000/d/clj-kondo/clj-kondo/2019.06.24-alpha-SNAPSHOT, not via the search 19 | -------------------------------------------------------------------------------- /corpus/unresolved_symbol.clj: -------------------------------------------------------------------------------- 1 | (ns ^{:clj-kondo/config '{:linters {:unresolved-symbol {:exclude [unresolved-fn1]}}}} 2 | unresolved-symbol) 3 | 4 | (comment 5 | (unresolved-fn1)) 6 | 7 | (ns ^{:clj-kondo/config '{:linters {:unresolved-symbol {:exclude [unresolved-fn2]}}}} 8 | unresolved-symbol2) 9 | 10 | (comment 11 | (unresolved-fn1) 12 | (unresolved-fn2)) 13 | 14 | (require '[clojure.set :refer [union]]) 15 | (clojure.set/join) ;; <- should be resolved 16 | union ;; <- also resolved 17 | 18 | (foo 1) ;; using a function before its definition is unresolved 19 | (defn foo []) 20 | 21 | (declare bar) 22 | (bar 1) ;; but declaring a var and then calling it is not unresolved. moreover, 23 | ;; this is the wrong arity, so an error 24 | (defn bar []) 25 | -------------------------------------------------------------------------------- /corpus/compojure/consumer.clj: -------------------------------------------------------------------------------- 1 | (ns compojure.consumer 2 | ;; namespace local config 3 | (:require [compojure.core :refer :all] ;; <- refer :all... 4 | [compojure.handler :as handler])) 5 | 6 | (defroutes) ;; <- arity warning 7 | (GET) ;; <- arity warning 8 | (POST) ;; <- arity warning 9 | 10 | (defroutes app-routes 11 | (GET "/" [] "

Hello World

") 12 | (GET "/user" {{:keys [user-id]} :session} ;; <- no unresolved symbols due to config 13 | (str "The current user is " user-id) ;; <- no unresolved symbols due to config 14 | x)) ;; <- this symbol is still unresolved 15 | 16 | (def app 17 | (handler/site app-routes)) ;; <- defroutes is linted as def, so clj-kondo 18 | ;; knows app-routes is a var 19 | 20 | 21 | -------------------------------------------------------------------------------- /.circleci/script/install-clojure: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | install_dir=${1:-/tmp/clojure} 4 | mkdir -p "$install_dir" 5 | cd /tmp 6 | curl -O -sL https://download.clojure.org/install/clojure-tools-1.11.1.1165.tar.gz 7 | tar xzf clojure-tools-1.11.1.1165.tar.gz 8 | cd clojure-tools 9 | clojure_lib_dir="$install_dir/lib/clojure" 10 | mkdir -p "$clojure_lib_dir/libexec" 11 | cp ./*.jar "$clojure_lib_dir/libexec" 12 | cp deps.edn "$clojure_lib_dir" 13 | cp example-deps.edn "$clojure_lib_dir" 14 | 15 | sed -i -e 's@PREFIX@'"$clojure_lib_dir"'@g' clojure 16 | mkdir -p "$install_dir/bin" 17 | cp clojure "$install_dir/bin" 18 | cp clj "$install_dir/bin" 19 | 20 | cd /tmp 21 | rm -rf clojure-tools-1.11.1.1165.tar.gz 22 | rm -rf clojure-tools 23 | echo "Installed clojure to $install_dir/bin" 24 | -------------------------------------------------------------------------------- /corpus/clojure.test.are.cljc: -------------------------------------------------------------------------------- 1 | (ns clojure.test.are 2 | (:require #?(:clj [cljs.test :refer [are deftest testing]] 3 | :cljs [clojure.test :refer [are deftest testing]]))) 4 | 5 | (are [x y z] (= x y z) 6 | 2 (+ 1 1) 2, 7 | 4 (* 2 2) 4) ;; no unresolved symbols 8 | 9 | ;; see #1284 10 | (deftest are-with-testing 11 | (are [f] (testing f 12 | (= "1" (f 1))) 13 | str 14 | pr-str)) 15 | 16 | (deftest linter-tests 17 | (testing "Nested #()s are not allowed" 18 | (are [key pred] (every? #(pred (get % key)) []) 19 | :events #(every? (fn [{:keys [id timestamp]}] ;; no warning about nested fn literal 20 | (and (string? id) 21 | (pos-int? timestamp))) 22 | %)))) 23 | -------------------------------------------------------------------------------- /corpus/lint_as_for.clj: -------------------------------------------------------------------------------- 1 | (ns lint-as-for 2 | {:clj-kondo/config '{;; TODO: setting the level doesn't yet work in ns local 3 | ;; configs, see #430, so we have to set it in our tests 4 | :linters {:unresolved-symbol {:level :warning}} 5 | :lint-as {plumbing.core/for-map clojure.core/for 6 | clojure.java.jdbc/with-db-transaction clojure.core/let}}}) 7 | 8 | (require 'plumbing.core) 9 | 10 | (plumbing.core/for-map [[k v] {} :let [[v & _rst] v]] [k v]) 11 | 12 | (for [[k v] {} :let [[v & _rst] v]] [k v]) 13 | 14 | ;; the fix for #434 does not result in a redundant let warning for: 15 | (require 'clojure.java.jdbc) 16 | (let [_a 1 d 2] 17 | (clojure.java.jdbc/with-db-transaction [db d] db)) 18 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/clj/clojure.stacktrace.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$root-cause",["^ ","~:row",20,"~:col",1,"~:fixed-arities",["~#set",[1]],"~:name","^0","~:ns","~$clojure.stacktrace","~:top-ns","^7","~:type","~:fn"],"~$print-trace-element",["^ ","^1",28,"^2",1,"^3",["^4",[1]],"^5","^;","^6","^7","^8","^7","^9","^:"],"~$print-throwable",["^ ","^1",40,"^2",1,"^3",["^4",[1]],"^5","^<","^6","^7","^8","^7","~:arities",["^ ","~i1",["^ ","~:ret",["^4",["~:nil"]]]],"^9","^:"],"~$print-stack-trace",["^ ","^1",50,"^2",1,"^3",["^4",[1,2]],"^5","^@","^6","^7","^8","^7","^9","^:"],"~$print-cause-trace",["^ ","^1",72,"^2",1,"^3",["^4",[1,2]],"^5","^A","^6","^7","^8","^7","^9","^:"],"~$e",["^ ","^1",82,"^2",1,"^3",["^4",[0]],"^5","~$e","^6","^7","^8","^7","^9","^:"],"~:filename","clojure/stacktrace.clj"] -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/cljs/clojure.reflect.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$evaluate-javascript",["^ ","~:row",15,"~:col",1,"~:private",true,"~:fixed-arities",["~#set",[1]],"~:name","^0","~:ns","~$clojure.reflect","~:top-ns","^8","~:type","~:fn"],"~$query-reflection",["^ ","^1",21,"^2",1,"^3",true,"^4",["^5",[2]],"^6","^<","^7","^8","^9","^8","^:","^;"],"~$meta",["^ ","^1",33,"^2",1,"^4",["^5",[2]],"^6","^=","^7","^8","^9","^8","^:","^;"],"~$macroexpand",["^ ","^1",41,"^2",1,"^4",["^5",[1]],"^6","^>","^7","^8","^9","^8","^:","^;"],"~$print-doc",["^ ","^1",47,"^2",1,"^4",["^5",[1]],"^6","^?","^7","^8","^9","^8","~:arities",["^ ","~i1",["^ ","~:ret",["^5",["~:nil"]]]],"^:","^;"],"~$doc",["^ ","^1",53,"^2",1,"^4",["^5",[1]],"^6","^C","^7","^8","^9","^8","^:","^;"],"~:filename","clojure/reflect.cljs"] -------------------------------------------------------------------------------- /script/changelog.clj: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bb 2 | 3 | (ns changelog 4 | (:require [clojure.string :as str])) 5 | 6 | (let [changelog (slurp "CHANGELOG.md") 7 | replaced (str/replace changelog 8 | #" #(\d+)" 9 | (fn [[_ issue after]] 10 | (format " [#%s](https://github.com/clj-kondo/clj-kondo/issues/%s)%s" 11 | issue issue (str after)))) 12 | replaced (str/replace replaced 13 | #"@([a-zA-Z0-9-_]+)([, \.)])" 14 | (fn [[_ name after]] 15 | (format "[@%s](https://github.com/%s)%s" 16 | name name after)))] 17 | (spit "CHANGELOG.md" replaced)) 18 | -------------------------------------------------------------------------------- /.github/workflows/diff.yml: -------------------------------------------------------------------------------- 1 | name: Linting diff 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | linting-diff: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v2 13 | 14 | - name: "Prepare Java" 15 | uses: "actions/setup-java@v2.4.0" 16 | with: 17 | distribution: "adopt" 18 | java-version: "8" 19 | 20 | - name: Setup Clojure 21 | uses: DeLaGuardo/setup-clojure@master 22 | with: 23 | tools-deps: '1.10.1.727' 24 | 25 | - name: Cache 26 | uses: actions/cache@v1 27 | with: 28 | path: ~/.m2/repository 29 | key: ${{ runner.os }}-project-clj-${{ hashFiles('**/project.clj') }} 30 | 31 | - name: Diff 32 | run: ./script/diff || true 33 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/cljs/clojure.walk.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$walk",["^ ","~:row",37,"~:col",1,"~:fixed-arities",["~#set",[3]],"~:name","^0","~:ns","~$clojure.walk","~:top-ns","^7","~:type","~:fn"],"~$postwalk",["^ ","^1",54,"^2",1,"^3",["^4",[2]],"^5","^;","^6","^7","^8","^7","^9","^:"],"~$prewalk",["^ ","^1",62,"^2",1,"^3",["^4",[2]],"^5","^<","^6","^7","^8","^7","^9","^:"],"~$keywordize-keys",["^ ","^1",68,"^2",1,"^3",["^4",[1]],"^5","^=","^6","^7","^8","^7","^9","^:"],"~$stringify-keys",["^ ","^1",76,"^2",1,"^3",["^4",[1]],"^5","^>","^6","^7","^8","^7","^9","^:"],"~$prewalk-replace",["^ ","^1",84,"^2",1,"^3",["^4",[2]],"^5","^?","^6","^7","^8","^7","^9","^:"],"~$postwalk-replace",["^ ","^1",92,"^2",1,"^3",["^4",[2]],"^5","^@","^6","^7","^8","^7","^9","^:"],"~:filename","clojure/walk.cljs"] -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/clj/clojure.test.tap.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$print-tap-plan",["^ ","~:row",45,"~:col",1,"~:fixed-arities",["~#set",[1]],"~:name","^0","~:ns","~$clojure.test.tap","~:top-ns","^7","~:type","~:fn"],"~$print-tap-diagnostic",["^ ","^1",51,"^2",1,"^3",["^4",[1]],"^5","^;","^6","^7","^8","^7","^9","^:"],"~$print-tap-pass",["^ ","^1",59,"^2",1,"^3",["^4",[1]],"^5","^<","^6","^7","^8","^7","^9","^:"],"~$print-tap-fail",["^ ","^1",65,"^2",1,"^3",["^4",[1]],"^5","^=","^6","^7","^8","^7","^9","^:"],"~$tap-report",["^ ","^1",72,"^2",1,"^5","^>","^6","^7","^8","^7"],"~$print-diagnostics",["^ ","^1",78,"^2",1,"^3",["^4",[1]],"^5","^?","^6","^7","^8","^7","^9","^:"],"~$with-tap-output",["^ ","^1",117,"^2",1,"~:macro",true,"~:varargs-min-arity",0,"^5","^@","^6","^7","^8","^7"],"~:filename","clojure/test/tap.clj"] -------------------------------------------------------------------------------- /parser/clj_kondo/impl/rewrite_clj/parser/whitespace.clj: -------------------------------------------------------------------------------- 1 | (ns ^{:no-doc true} clj-kondo.impl.rewrite-clj.parser.whitespace 2 | (:require [clj-kondo.impl.rewrite-clj 3 | [node :as node] 4 | [reader :as reader]])) 5 | 6 | (defn parse-whitespace 7 | "Parse as much whitespace as possible. The created node can either contain 8 | only linebreaks or only space/tabs." 9 | [reader] 10 | (let [c (reader/peek reader)] 11 | (cond (reader/linebreak? c) 12 | (node/newline-node 13 | (reader/read-while reader reader/linebreak?)) 14 | 15 | (reader/comma? c) 16 | (node/comma-node 17 | (reader/read-while reader reader/comma?)) 18 | 19 | :else 20 | (node/whitespace-node 21 | (reader/read-while reader reader/space?))))) 22 | -------------------------------------------------------------------------------- /corpus/.clj-kondo/hooks/rum.clj: -------------------------------------------------------------------------------- 1 | (ns hooks.rum 2 | (:require [clj-kondo.hooks-api :as api])) 3 | 4 | (defn f [{:keys [:node]}] 5 | (let [args (rest (:children node)) 6 | component-name (first args) 7 | args (next args) 8 | body 9 | (loop [args* args 10 | mixins []] 11 | (if (seq args*) 12 | (let [a (first args*)] 13 | (if (vector? (api/sexpr a)) 14 | (cons a (concat mixins (rest args*))) 15 | (recur (rest args*) 16 | (conj mixins a)))) 17 | args)) 18 | new-node (with-meta 19 | (api/list-node (list* (api/token-node 'defn) component-name body)) 20 | (meta node))] 21 | ;; (prn (meta sexpr)) 22 | ;; (prn expr) 23 | {:node new-node})) 24 | -------------------------------------------------------------------------------- /corpus/jdbc/next.clj: -------------------------------------------------------------------------------- 1 | (ns jdbc.next-test 2 | "Syntax check examples for next.jdbc/with-transaction. 3 | 4 | The clojure.java.jdbc/with-db-* functions would all be treated the same way." 5 | (:require [next.jdbc :as jdbc])) 6 | 7 | (def db {:dbtype "some-db" :dbname "kondo"}) 8 | 9 | (jdbc/with-transaction [tx (jdbc/get-datasource db) {} {}] ;; 2 or 3 forms 10 | (jdbc/execute! tx ["select * from table where foo = ?" 123])) 11 | 12 | (jdbc/with-transaction [tx] ;; 2 or 3 forms 13 | (jdbc/execute! tx ["select * from table where foo = ?" 123])) 14 | 15 | (jdbc/with-transaction ;; requires vector for binding 16 | (jdbc/execute! tx ["select * from table where foo = ?" 123])) 17 | 18 | (jdbc/with-transaction [[tx] (jdbc/get-datasource db)] ;; requires a symbol 19 | (jdbc/execute! tx ["select * from table where foo = ?" 123])) 20 | -------------------------------------------------------------------------------- /test/clj_kondo/condition_always_true_test.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.condition-always-true-test 2 | (:require 3 | [clj-kondo.test-utils :refer [lint! assert-submaps2]] 4 | [clojure.test :as t :refer [deftest is testing]])) 5 | 6 | (deftest condition-always-true-test 7 | (assert-submaps2 8 | '({:file "", :row 1, :col 20, :level :warning, :message "Condition always true"} 9 | {:file "", 10 | :row 1, 11 | :col 35, 12 | :level :warning, 13 | :message "Condition always true"}) 14 | (lint! "(defn foo [x] [(if inc x 2) (when inc 2)])" 15 | '{:linters {:condition-always-true {:level :warning}}})) 16 | (is (empty? 17 | (lint! "(defn foo [x] (if x inc 2)) 18 | (defn bar [x] (if x 2 inc))" 19 | '{:linters {:condition-always-true {:level :warning}}})))) 20 | -------------------------------------------------------------------------------- /corpus/schema/defs.clj: -------------------------------------------------------------------------------- 1 | (ns schema.defs 2 | (:require [schema.core :as s])) 3 | 4 | (s/defn ^:private verify-signature :- (s/maybe s/Int) 5 | [message :- s/Str 6 | [base64-encoded-signature :- s/Str] 7 | {:keys [a]} :- {:a s/Int}]) 8 | 9 | (verify-signature 1 [2] {:a 3}) ;;correct 10 | (verify-signature 1 2) ;; incorrect 11 | 12 | (s/defn) ;; doesn't crash the app 13 | 14 | ;; from kekkonen 15 | (s/defn handler 16 | ([meta :- s/Any] 17 | (handler (dissoc meta :handle) (:handle meta))) 18 | ([meta :- s/Any f :- s/Any] 19 | (assert (:name meta) "handler should have :name") 20 | (vary-meta f merge {:type :handler} meta))) 21 | 22 | (handler {}) ;; this should be OK 23 | 24 | (s/defn bar :- #(last %) 25 | [x] 26 | x) 27 | 28 | (s/def foo :- long "a long" 2) 29 | 30 | (schema.core/defn baz [] 31 | (str {:a 1 :b})) 32 | -------------------------------------------------------------------------------- /src/clj_kondo/impl/types/clojure/string.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.impl.types.clojure.string 2 | {:no-doc true}) 3 | 4 | (def clojure-string 5 | {;; 75 6 | 'replace 7 | {:arities {3 {:args [:char-sequence #{:string :char :regex} #{:string :char :ifn}] 8 | :ret :string}}} 9 | ;; 180 10 | 'join 11 | {:arities {1 {:args [:seqable] 12 | :ret :string} 13 | 2 {:args [:any :seqable] 14 | :ret :string}}} 15 | ;; 360 16 | 'starts-with? 17 | {:arities {2 {:args [:char-sequence :string] 18 | :ret :boolean}}} 19 | ;; 366 20 | 'ends-with? 21 | {:arities {2 {:args [:char-sequence :string] 22 | :ret :boolean}}} 23 | ;; 372 24 | 'includes? 25 | {:arities {2 {:args [:char-sequence :char-sequence] 26 | :ret :boolean}}}}) 27 | -------------------------------------------------------------------------------- /corpus/deftest.cljc: -------------------------------------------------------------------------------- 1 | (ns deftest 2 | (:require [clojure.test :refer [deftest is are #?(:cljs async)]]) 3 | (:import clojure.lang.ExceptionInfo)) 4 | 5 | (deftest are-test 6 | (are [?a ?b] 7 | (is (= ?a (dec ?b))) 8 | 1 2 9 | 10 11 10 | 14 15)) 11 | 12 | (deftest missing-test-assertion-false-positive 13 | (are [v expected?] (expected? (contains? #{:bar :baz} v)) 14 | :foo false? 15 | :bar true? 16 | :baz true?)) 17 | 18 | (deftest thrown-test 19 | (is (thrown? #?(:clj Exception :cljs js/Error) :foo)) 20 | (are [x] (thrown? #?(:clj Exception :cljs js/Error) x) :foo)) 21 | 22 | (deftest thown-with-msg-test 23 | (is (thrown-with-msg? 24 | ExceptionInfo #"uh oh" 25 | (throw (ex-info "uh oh" {})))) 26 | (is (thrown? ExceptionInfo (throw (ex-info "uh oh" {}))))) 27 | 28 | #?(:cljs 29 | (async foo (foo))) 30 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/clj/clojure.java.browse.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$macosx?",["^ ","~:row",19,"~:col",1,"~:private",true,"~:fixed-arities",["~#set",[0]],"~:name","^0","~:ns","~$clojure.java.browse","~:top-ns","^8","~:type","~:fn"],"~$xdg-open-loc",["^ ","^1",23,"^2",1,"^3",true,"^4",["^5",[0]],"^6","^<","^7","^8","^9","^8","^:","^;"],"~$open-url-script-val",["^ ","^1",31,"^2",1,"^3",true,"^4",["^5",[0]],"^6","^=","^7","^8","^9","^8","^:","^;"],"~$*open-url-script*",["^ ","^1",42,"^2",1,"^6","^>","^7","^8","^9","^8"],"~$open-url-in-browser",["^ ","^1",44,"^2",1,"^3",true,"^4",["^5",[1]],"^6","^?","^7","^8","^9","^8","^:","^;"],"~$open-url-in-swing",["^ ","^1",59,"^2",1,"^3",true,"^4",["^5",[1]],"^6","^@","^7","^8","^9","^8","^:","^;"],"~$browse-url",["^ ","^1",68,"^2",1,"^4",["^5",[1]],"^6","^A","^7","^8","^9","^8","^:","^;"],"~:filename","clojure/java/browse.clj"] -------------------------------------------------------------------------------- /test/clj_kondo/impl/utils_test.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.impl.utils-test 2 | (:require [clj-kondo.impl.utils :as utils :refer [constant? parse-string]] 3 | [clojure.test :as t :refer [deftest is]])) 4 | 5 | (deftest constant-test 6 | (is (constant? (parse-string "{:a 1 'x 2}"))) 7 | (is (constant? (parse-string "'{:a 1 x 2}"))) 8 | (is (constant? (parse-string "1"))) 9 | (is (constant? (parse-string "\"foo\""))) 10 | (is (constant? (parse-string "\\x"))) 11 | (is (constant? (parse-string "true"))) 12 | (is (constant? (parse-string ":k"))) 13 | (is (constant? (parse-string "::k/foo"))) 14 | (is (false? (constant? (parse-string "x")))) 15 | (is (false? (constant? (parse-string "(java.util.HashMap. {})")))) 16 | (is (false? (constant? (parse-string "{:a 1 x 2}"))))) 17 | 18 | ;;;; Scratch 19 | 20 | (comment 21 | (t/run-tests) 22 | ) 23 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM clojure:lein-2.9.1 AS BASE 2 | 3 | RUN apt-get update 4 | RUN apt-get install --no-install-recommends -yy curl unzip build-essential zlib1g-dev sudo 5 | WORKDIR "/opt" 6 | RUN curl -sLO https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.0.0/graalvm-ce-java11-linux-amd64-21.0.0.tar.gz 7 | RUN tar -xzf graalvm-ce-java11-linux-amd64-21.0.0.tar.gz 8 | ENV GRAALVM_HOME="/opt/graalvm-ce-java11-21.0.0" 9 | ENV JAVA_HOME="/opt/graalvm-ce-java11-21.0.0/bin" 10 | ENV PATH="$JAVA_HOME:$PATH" 11 | COPY . . 12 | 13 | ARG CLJ_KONDO_STATIC= 14 | ARG CLJ_KONDO_MUSL= 15 | ENV CLJ_KONDO_STATIC=$CLJ_KONDO_STATIC 16 | ENV CLJ_KONDO_MUSL=$CLJ_KONDO_MUSL 17 | 18 | RUN ./script/setup-musl 19 | RUN ./script/compile 20 | 21 | FROM ubuntu:latest 22 | RUN mkdir -p /usr/local/bin 23 | COPY --from=BASE /opt/clj-kondo /usr/local/bin/clj-kondo 24 | CMD ["clj-kondo"] 25 | -------------------------------------------------------------------------------- /corpus/issue-2067/.clj-kondo/hooks.clj: -------------------------------------------------------------------------------- 1 | (ns hooks 2 | (:require [clj-kondo.hooks-api :as api])) 3 | 4 | (defn lint-with-redefs [{:keys [name ns]} expr] 5 | (when (and (= ns 'clojure.core) 6 | (= name 'with-redefs)) 7 | (api/reg-finding! (assoc (meta expr) 8 | :type :discouraged-var 9 | :message "Don't use with-redefs")))) 10 | 11 | (defn my-test [{:keys [node]}] 12 | (let [[core-wrd foo-wrd] (rest (:children node))] 13 | (lint-with-redefs (api/resolve {:name (api/sexpr (first (:children core-wrd)))}) core-wrd) 14 | (lint-with-redefs (api/resolve {:name (api/sexpr (first (:children foo-wrd)))}) foo-wrd))) 15 | 16 | (defmacro my-test-macro [x] 17 | (api/reg-finding! (assoc (meta x) 18 | :type :discouraged-var 19 | :message (str x)))) 20 | -------------------------------------------------------------------------------- /script/dump_types.clj: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bb 2 | 3 | (ns dump-types 4 | (:require 5 | [clojure.java.io :as io] 6 | [cognitect.transit :as transit])) 7 | 8 | (def clj-files (rest (file-seq (io/file "resources" "clj_kondo" "impl" "cache" "built_in" "clj")))) 9 | 10 | (defn transit->edn [f] 11 | (with-open [is (io/input-stream (io/file f))] 12 | (let [reader (transit/reader is :json)] 13 | (transit/read reader)))) 14 | 15 | (defn types [f] 16 | (let [edn (transit->edn f)] 17 | (for [[k v] edn 18 | :let [ar (:arities v)] 19 | :when ar] 20 | [k ar]))) 21 | 22 | (let [output 23 | (with-out-str 24 | (doseq [f clj-files] 25 | (println "=== " (.getPath f) " ===") 26 | (doseq [[k v] (types f)] 27 | (println k v)) 28 | (println)))] 29 | (spit (io/file "doc" "types.txt") output)) 30 | -------------------------------------------------------------------------------- /analysis/src/clj_kondo/tools/namespace_graph.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.tools.namespace-graph 2 | (:require [clj-kondo.core :as clj-kondo] 3 | [loom.graph :refer [digraph]] 4 | [loom.io :refer [view]])) 5 | 6 | (defn -main [& paths] 7 | (let [analysis (:analysis (clj-kondo/run! {:lint paths 8 | :config {:analysis {:var-usages false 9 | :var-definitions {:shallow true}}} 10 | :skip-lint true})) 11 | {:keys [:namespace-definitions :namespace-usages]} analysis 12 | nodes (map :name namespace-definitions) 13 | edges (map (juxt :from :to) namespace-usages) 14 | g (apply digraph (concat nodes edges))] 15 | ;; install GraphViz, e.g. with brew install graphviz 16 | (view g))) 17 | -------------------------------------------------------------------------------- /script/bump_version: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # I'm using brew coreutils which installs GNU date. If that isn't present, it 4 | # will use normal date. 5 | date_cmd=$(command -v gdate) 6 | if [ -z "$date_cmd" ]; then 7 | date_cmd=$(command -v date) 8 | fi 9 | 10 | date_pattern="+%Y.%m.%d" 11 | today=$(date $date_pattern) 12 | version="$today" 13 | 14 | if [ "$1" = "release" ]; then 15 | echo -e "$version" > resources/CLJ_KONDO_VERSION 16 | script/update-templates.clj 17 | elif [ "$1" = "post-release" ]; then 18 | cat resources/CLJ_KONDO_VERSION > resources/CLJ_KONDO_RELEASED_VERSION 19 | tomorrow=$($date_cmd $date_pattern --date='next day') 20 | snapshot_version="$tomorrow-SNAPSHOT" 21 | echo "$snapshot_version" > resources/CLJ_KONDO_VERSION 22 | script/update-templates.clj 23 | else 24 | echo "Usage: script/bump-version ( release | post-release )" 25 | fi 26 | -------------------------------------------------------------------------------- /parser/clj_kondo/impl/rewrite_clj/node/comment.clj: -------------------------------------------------------------------------------- 1 | (ns ^{:no-doc true} clj-kondo.impl.rewrite-clj.node.comment 2 | (:require [clj-kondo.impl.rewrite-clj.node.protocols :as node])) 3 | 4 | ;; ## Node 5 | 6 | (defrecord CommentNode [s] 7 | node/Node 8 | (tag [_] :comment) 9 | (printable-only? [_] true) 10 | (sexpr [_] 11 | (throw (UnsupportedOperationException.))) 12 | (length [_] 13 | (+ 1 (count s))) 14 | (string [_] 15 | (str ";" s)) 16 | 17 | Object 18 | (toString [this] 19 | (node/string this))) 20 | 21 | (node/make-printable! CommentNode) 22 | 23 | ;; ## Constructor 24 | 25 | (defn comment-node 26 | "Create node representing an EDN comment." 27 | [s] 28 | {:pre [(re-matches #"[^\r\n]*[\r\n]?" s)]} 29 | (->CommentNode s)) 30 | 31 | (defn comment? 32 | "Check whether a node represents a comment." 33 | [node] 34 | (= (node/tag node) :comment)) 35 | -------------------------------------------------------------------------------- /test/clj_kondo/reify_test.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.reify-test 2 | (:require [clj-kondo.test-utils :refer [lint! assert-submaps]] 3 | [clojure.test :as t :refer [deftest is]])) 4 | 5 | (deftest reify-test 6 | (assert-submaps 7 | '({:file "", :row 1, :col 36, :level :warning, :message "unused binding x"}) 8 | (lint! "(reify clojure.lang.IDeref (deref [x] nil))" 9 | {:linters {:unused-binding {:level :warning}}})) 10 | (is (empty? (lint! "(reify clojure.lang.IDeref (deref [_] nil))" 11 | {:linters {:unresolved-symbol {:level :error}}}))) 12 | (is (empty? (lint! " 13 | (ns fiddle 14 | (:import (java.lang.management PlatformManagedObject) 15 | (javax.management ObjectName))) 16 | 17 | (reify PlatformManagedObject 18 | (^ObjectName getObjectName [_this]))" 19 | {:linters {:unused-import {:level :warning}}})))) 20 | -------------------------------------------------------------------------------- /parser/clj_kondo/impl/rewrite_clj/node/token.clj: -------------------------------------------------------------------------------- 1 | (ns ^{:no-doc true} clj-kondo.impl.rewrite-clj.node.token 2 | (:require [clj-kondo.impl.rewrite-clj.node.protocols :as node])) 3 | 4 | (set! *warn-on-reflection* true) 5 | 6 | ;; ## Node 7 | 8 | (defrecord TokenNode [value string-value] 9 | node/Node 10 | (tag [_] :token) 11 | (printable-only? [_] false) 12 | (sexpr [this] (if (instance? clojure.lang.IObj value) 13 | (with-meta value (meta this)) 14 | value)) 15 | (length [_] (count string-value)) 16 | (string [_] string-value) 17 | 18 | Object 19 | (toString [this] 20 | (node/string this))) 21 | 22 | (node/make-printable! TokenNode) 23 | 24 | ;; ## Constructor 25 | 26 | (defn token-node 27 | "Create node for an unspecified EDN token." 28 | [value & [string-value]] 29 | (->TokenNode 30 | value 31 | (or string-value (pr-str value)))) 32 | -------------------------------------------------------------------------------- /script/built-in: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo 4 | 5 | rm -rf /tmp/built-in 6 | mkdir -p /tmp/built-in 7 | version=$(cat resources/CLJ_KONDO_VERSION) 8 | echo "$version" 9 | 10 | # shellcheck disable=1091 11 | source script/extract-versions 12 | 13 | clojure -M:clj-kondo/dev --lint "$CLJ_KONDO_EXTRACT_CORE_DEPS_PATH" --cache /tmp/built-in --dependencies 14 | 15 | base_path="/tmp/built-in/v1" 16 | 17 | rm "$base_path"/clj/clojure.tools.reader.*.transit.json 18 | rm "$base_path"/cljs/cljs.tools.reader.*transit.json 19 | 20 | cp "$base_path"/clj/clojure.*.transit.json resources/clj_kondo/impl/cache/built_in/clj 21 | cp "$base_path"/cljs/cljs.*.transit.json resources/clj_kondo/impl/cache/built_in/cljs 22 | cp "$base_path"/cljs/clojure.*.transit.json resources/clj_kondo/impl/cache/built_in/cljs 23 | cp "$base_path"/cljc/cljs.*.transit.json resources/clj_kondo/impl/cache/built_in/cljc 24 | 25 | script/dump_types.clj 26 | -------------------------------------------------------------------------------- /test/clj_kondo/duplicate_field_name_test.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.duplicate-field-name-test 2 | (:require [clj-kondo.test-utils :refer [assert-submaps lint!]] 3 | [clojure.test :refer [deftest is testing]] 4 | [missing.test.assertions])) 5 | 6 | (deftest duplicate-field-name-test 7 | (testing "deftype" 8 | (assert-submaps 9 | '({:file "", :row 1, :col 13, :level :error, :message "Duplicate field name: field"} 10 | {:file "", :row 1, :col 33, :level :error, :message "Duplicate field name: field"}) 11 | (lint! "(deftype T [field another-field field])"))) 12 | 13 | (testing "defrecord" 14 | (assert-submaps 15 | '({:file "", :row 1, :col 15, :level :error, :message "Duplicate field name: field"} 16 | {:file "", :row 1, :col 35, :level :error, :message "Duplicate field name: field"}) 17 | (lint! "(defrecord R [field another-field field])")))) 18 | -------------------------------------------------------------------------------- /src/clj_kondo/impl/analyzer/common.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.impl.analyzer.common 2 | {:no-doc true}) 3 | 4 | (defonce common (volatile! {})) 5 | 6 | (defn analyze-like-let [ctx expr] 7 | ((get @common 'analyze-like-let) ctx expr)) 8 | 9 | (defn analyze-expression** [ctx expr] 10 | ((get @common 'analyze-expression**) ctx expr)) 11 | 12 | (defn extract-bindings [ctx expr] 13 | ((get @common 'extract-bindings) ctx expr)) 14 | 15 | (defn ctx-with-bindings [ctx expr] 16 | ((get @common 'ctx-with-bindings) ctx expr)) 17 | 18 | (defn analyze-children [ctx expr] 19 | ((get @common 'analyze-children) ctx expr)) 20 | 21 | (defn analyze-defn [ctx expr defined-by defined-by->lint-as] 22 | ((get @common 'analyze-defn) ctx expr defined-by defined-by->lint-as)) 23 | 24 | (defn analyze-usages2 [ctx expr] 25 | ((get @common 'analyze-usages2) ctx expr)) 26 | 27 | (defn reg-finding! [ctx m] 28 | ((get @common 'reg-finding!) ctx m)) 29 | -------------------------------------------------------------------------------- /test/clj_kondo/self_requiring_namespace_test.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.self-requiring-namespace-test 2 | (:require 3 | [clj-kondo.test-utils :refer [lint! assert-submaps2]] 4 | [clojure.test :refer [deftest testing is]])) 5 | 6 | (def conf {:linters {:self-requiring-namespace {:level :warning}}}) 7 | 8 | (deftest self-requiring-namespace-test 9 | (assert-submaps2 10 | '({:file "", :row 1, :col 19, :level :warning, :message "Namespace is requiring itself: foo"} {:file "", :row 1, :col 25, :level :warning, :message "Namespace is requiring itself: foo"} {:file "", :row 1, :col 25, :level :warning, :message "duplicate require of foo"}) 11 | (lint! "(ns foo (:require [foo] foo))" conf)) 12 | (assert-submaps2 13 | [{:file "", 14 | :row 1, 15 | :col 32, 16 | :level :warning, 17 | :message "duplicate require of foo"}] 18 | (lint! "(ns foo (:require-macros [foo] foo))" conf "--lang" "cljs"))) 19 | -------------------------------------------------------------------------------- /pod-test/clj_kondo/pod_test.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.pod-test 2 | (:require [babashka.pods :as pods] 3 | [clojure.string :as str] 4 | [clojure.test :refer [deftest is]])) 5 | 6 | (def pod-spec (if (= "native" (System/getenv "CLJ_KONDO_TEST_ENV")) 7 | ["./clj-kondo"] 8 | ["clojure" "-M:clj-kondo/dev"])) 9 | 10 | (pods/load-pod pod-spec) 11 | (require '[clj-kondo.core :as clj-kondo]) 12 | 13 | (deftest pod-test 14 | (is (= '{:linters {:unresolved-symbol {:exclude [(foo1.bar) (foo2.bar)]}}} 15 | (clj-kondo/merge-configs 16 | '{:linters {:unresolved-symbol {:exclude [(foo1.bar)]}}} 17 | '{:linters {:unresolved-symbol {:exclude [(foo2.bar)]}}}))) 18 | (is (str/includes? (with-out-str (clj-kondo/print! (clj-kondo/run! {:lint ["src"]}))) 19 | "errors"))) 20 | 21 | (when (= *file* (System/getProperty "babashka.file")) 22 | (clojure.test/run-tests 'clj-kondo.pod-test)) 23 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/cljc/cljs.analyzer.impl.namespaces.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~:filename","cljs/analyzer/impl/namespaces.cljc","~:clj",["^ ","~$check-and-remove-as-alias",["^ ","~:row",11,"~:col",1,"~:fixed-arities",["~#set",[1]],"~:name","^2","~:ns","~$cljs.analyzer.impl.namespaces","~:top-ns","^9","~:type","~:fn"],"~$check-as-alias-duplicates",["^ ","^3",31,"^4",1,"^5",["^6",[2]],"^7","^=","^8","^9","^:","^9","^;","^<"],"~$elide-aliases-from-libspecs",["^ ","^3",38,"^4",1,"^5",["^6",[1,2]],"^7","^>","^8","^9","^:","^9","^;","^<"],"~$elide-aliases-from-ns-specs",["^ ","^3",55,"^4",1,"^5",["^6",[1]],"^7","^?","^8","^9","^:","^9","^;","^<"]],"~:cljs",["^ ","^2",["^ ","^3",11,"^4",1,"^5",["^6",[1]],"^7","^2","^8","^9","^:","^9","^;","^<"],"^=",["^ ","^3",31,"^4",1,"^5",["^6",[2]],"^7","^=","^8","^9","^:","^9","^;","^<"],"^>",["^ ","^3",38,"^4",1,"^5",["^6",[1,2]],"^7","^>","^8","^9","^:","^9","^;","^<"],"^?",["^ ","^3",55,"^4",1,"^5",["^6",[1]],"^7","^?","^8","^9","^:","^9","^;","^<"]]] -------------------------------------------------------------------------------- /src/clj_kondo/impl/analyzer/babashka.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.impl.analyzer.babashka 2 | {:no-doc true} 3 | (:require 4 | [clj-kondo.impl.analyzer.common :as common] 5 | [clj-kondo.impl.utils :as utils])) 6 | 7 | (defn analyze-$ [ctx expr] 8 | (let [[child & children] (rest (:children expr)) 9 | [opts children] (if (= :map (utils/tag child)) 10 | [child children] 11 | [nil (cons child children)]) 12 | children (doall (keep (fn [child] 13 | (let [s (utils/sexpr child)] 14 | (when (and (seq? s) 15 | (= 'clojure.core/unquote (first s))) 16 | (first (:children child))))) 17 | children)) 18 | children (if opts 19 | (cons opts children) 20 | children)] 21 | (common/analyze-children ctx children))) 22 | -------------------------------------------------------------------------------- /test/clj_kondo/config_in_tag_test.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.config-in-tag-test 2 | (:require 3 | [clj-kondo.test-utils :refer 4 | [lint! assert-submaps]] 5 | [clojure.test :as t :refer [deftest is testing]])) 6 | 7 | (deftest config-in-tag-test 8 | (assert-submaps 9 | '({:file "", :row 1, :col 17, :level :error, :message "Unresolved symbol: x"}) 10 | (lint! "#jsx [:a {:href x}]" 11 | '{:linters {:unresolved-symbol {:level :error} 12 | :unused-binding {:level :warning}} 13 | :config-in-tag {jsx {:linters {:unresolved-symbol {:level :error}}}}})) 14 | (assert-submaps 15 | '({:file "", :row 2, :col 29, :level :error, :message "Unresolved symbol: x"}) 16 | (lint! "(ns dude {:clj-kondo/config '{:config-in-tag {jsx {:linters {:unresolved-symbol {:level :error}}}}}}) 17 | #jsx [:a {:href x}]" 18 | '{:linters {:unresolved-symbol {:level :error} 19 | :unused-binding {:level :warning}}}))) 20 | -------------------------------------------------------------------------------- /bb.edn: -------------------------------------------------------------------------------- 1 | {:paths ["script"] 2 | :deps {borkdude/gh-release-artifact 3 | #_{:local/root "../gh-release-artifact"} 4 | {:git/url "https://github.com/borkdude/gh-release-artifact" 5 | :git/sha "ce060c12a25b552b864dc90f8fb344a2eb91ea9d"} 6 | } 7 | :tasks {extract (shell "script/extract-var-info") 8 | test (shell "script/test") 9 | bump-release (shell "script/bump_version release") 10 | bump-post-release (shell "script/bump_version post-release") 11 | release-artifact clj-kondo.release-artifact/release 12 | quickdoc {:doc "Invoke quickdoc" 13 | :extra-deps {io.github.borkdude/quickdoc {:git/sha "32e726cd6d785d00e49d4e614a05f7436d3831c0"}} 14 | :task (exec 'quickdoc.api/quickdoc) 15 | :exec-args {:git/branch "master" 16 | :github/repo "https://github.com/clj-kondo/clj-kondo" 17 | :source-paths ["src/clj_kondo/core.clj"]}}}} 18 | -------------------------------------------------------------------------------- /analysis/src/clj_kondo/tools/unused_vars.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.tools.unused-vars 2 | (:require [clj-kondo.core :as clj-kondo] 3 | [clojure.set :as set] 4 | [clojure.string :as str])) 5 | 6 | (defn -main [& paths] 7 | (if (empty? paths) 8 | (println "Provide paths for analysis.") 9 | (let [analysis (:analysis (clj-kondo/run! {:lint paths 10 | :config {:analysis true}})) 11 | {:keys [:var-definitions :var-usages]} analysis 12 | defined-vars (set (map (juxt :ns :name) var-definitions)) 13 | used-vars (set (map (juxt :to :name) var-usages)) 14 | unused-vars (map (fn [[ns v]] 15 | (symbol (str ns) (str v))) 16 | (set/difference defined-vars used-vars))] 17 | (if (seq unused-vars) 18 | (do (println "The following vars are unused:") 19 | (println (str/join "\n" unused-vars))) 20 | (println "No unused vars found."))))) 21 | -------------------------------------------------------------------------------- /test/clj_kondo/analysis/instance_invocations_test.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.analysis.instance-invocations-test 2 | (:require 3 | [clj-kondo.core :as clj-kondo] 4 | [clj-kondo.impl.utils :refer [err]] 5 | [clj-kondo.test-utils :refer [assert-submaps]] 6 | [clojure.test :as t :refer [deftest is testing]])) 7 | 8 | (defn analyze 9 | ([paths] (analyze paths nil)) 10 | ([paths config] 11 | (:analysis 12 | (clj-kondo/run! (merge 13 | {:lint paths 14 | :config {:output {:canonical-paths true} 15 | :analysis {:instance-invocations true}}} 16 | config))))) 17 | 18 | (deftest invocations-test 19 | (assert-submaps 20 | [{:method-name "length", :filename "", :name-row 1, :name-col 2, :name-end-row 1, :name-end-col 9}] 21 | (-> (with-in-str "(.length \"hello\")" 22 | (clj-kondo/run! {:lint ["-"] :config {:analysis {:instance-invocations true}}})) 23 | :analysis :instance-invocations))) 24 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 365 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 30 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: expired 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as expired because it has not had 14 | recent activity. To prevent it from being closed, please leave a 15 | message. Additionally, consider giving the original message a thumbs up if 16 | this issue is important to you, so it will appear higher in [this 17 | list](https://github.com/clj-kondo/clj-kondo/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc). Thank 18 | you for your contributions! 19 | # Comment to post when closing a stale issue. Set to `false` to disable 20 | closeComment: false 21 | -------------------------------------------------------------------------------- /corpus/.clj-kondo/hooks/better_cond.clj_kondo: -------------------------------------------------------------------------------- 1 | (ns hooks.better-cond 2 | (:refer-clojure :exclude [cond])) 3 | 4 | (require '[clj-kondo.hooks-api :as api]) 5 | 6 | (defn process-pairs [pairs] 7 | (loop [[[lhs rhs :as pair] & pairs] pairs 8 | new-body [(api/token-node 'cond)]] 9 | (if pair 10 | (let [lhs-sexpr (api/sexpr lhs)] 11 | (clojure.core/cond 12 | (= 1 (count pair)) (api/list-node (conj new-body lhs)) 13 | (not (keyword? lhs-sexpr)) 14 | (recur pairs 15 | (conj new-body lhs rhs)) 16 | (= :let lhs-sexpr) 17 | (api/list-node (conj new-body (api/token-node :else) (api/list-node [(api/token-node 'let) rhs (process-pairs pairs)]))))) 18 | (api/list-node new-body)))) 19 | 20 | (def cond 21 | (fn [{:keys [:node]}] 22 | (let [expr (let [args (rest (:children node)) 23 | pairs (partition-all 2 args)] 24 | (process-pairs pairs))] 25 | {:node (with-meta expr 26 | (meta node))}))) 27 | -------------------------------------------------------------------------------- /script/compile.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | Rem set GRAALVM_HOME=C:\Users\IEUser\Downloads\graalvm\graalvm-ce-19.2.1 4 | Rem set PATH=%PATH%;C:\Users\IEUser\bin 5 | 6 | if "%GRAALVM_HOME%"=="" ( 7 | echo Please set GRAALVM_HOME 8 | exit /b 9 | ) 10 | 11 | set JAVA_HOME=%GRAALVM_HOME%\bin 12 | set PATH=%GRAALVM_HOME%\bin;%PATH% 13 | 14 | dir %GRAALVM_HOME%\bin 15 | 16 | set /P CLJ_KONDO_VERSION=< resources\CLJ_KONDO_VERSION 17 | 18 | echo Building clj-kondo %CLJ_KONDO_VERSION% 19 | 20 | set CLJ_KONDO_NATIVE=true 21 | 22 | call lein with-profiles +clojure-1.10.2 do clean, uberjar 23 | if %errorlevel% neq 0 exit /b %errorlevel% 24 | 25 | call %GRAALVM_HOME%\bin\gu.cmd install native-image 26 | 27 | Rem the --no-server option is not supported in GraalVM Windows. 28 | call %GRAALVM_HOME%\bin\native-image.cmd ^ 29 | "-jar" "target/clj-kondo-%CLJ_KONDO_VERSION%-standalone.jar" ^ 30 | "-H:+ReportExceptionStackTraces" ^ 31 | "--no-fallback" ^ 32 | "--verbose" ^ 33 | "-J-Xmx3g" 34 | 35 | if %errorlevel% neq 0 exit /b %errorlevel% 36 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/cljs/cljs.source-map.base64-vlq.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$encode-val",["^ ","~:row",31,"~:col",1,"~:fixed-arities",["~#set",[1]],"~:name","^0","~:ns","~$cljs.source-map.base64-vlq","~:top-ns","^7","~:arities",["^ ","~i1",["^ ","~:ret","~:string"]],"~:type","~:fn"],"~$encode",["^ ","^1",44,"^2",1,"^3",["^4",[1]],"^5","^>","^6","^7","^8","^7","^<","^="],"~$vlq-continuation-bit",["^ ","^1",17,"^2",1,"^5","^?","^6","^7","^8","^7"],"~$vlq-base-mask",["^ ","^1",16,"^2",1,"^5","^@","^6","^7","^8","^7","^<","~:number"],"~$decode",["^ ","^1",47,"^2",1,"^3",["^4",[1]],"^5","^B","^6","^7","^8","^7","^<","^="],"~$vlq-base-shift",["^ ","^1",14,"^2",1,"^5","^C","^6","^7","^8","^7","^<","~:pos-int"],"~$from-vlq-signed",["^ ","^1",24,"^2",1,"^3",["^4",[1]],"^5","^E","^6","^7","^8","^7","^<","^="],"~:filename","cljs/source_map/base64_vlq.cljs","~$to-vlq-signed",["^ ","^1",19,"^2",1,"^3",["^4",[1]],"^5","^G","^6","^7","^8","^7","^9",["^ ","~i1",["^ ","^:",["^4",["^A"]]]],"^<","^="],"~$vlq-base",["^ ","^1",15,"^2",1,"^5","^H","^6","^7","^8","^7"]] -------------------------------------------------------------------------------- /test/clj_kondo/plus_minus_one_test.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.plus-minus-one-test 2 | (:require 3 | [clj-kondo.test-utils :refer [assert-submaps2 lint!]] 4 | [clojure.test :refer [deftest is testing]])) 5 | 6 | (deftest plus-minus-one-test 7 | (assert-submaps2 8 | [{:file "", 9 | :row 1, 10 | :col 11, 11 | :level :warning, 12 | :message "Prefer (inc x) over (+ 1 x)"} 13 | {:file "", 14 | :row 1, 15 | :col 19, 16 | :level :warning, 17 | :message "Prefer (inc x) over (+ 1 x)"} 18 | {:file "", 19 | :row 1, 20 | :col 27, 21 | :level :warning, 22 | :message "Prefer (dec x) over (- x 1)"}] 23 | (lint! "(def x 1) (+ 1 x) (+ x 1) (- x 1) (- 1 x)" 24 | {:linters {:plus-one {:level :warning} 25 | :minus-one {:level :warning}}})) 26 | (is (empty? (lint! "(+ 1 2 3) (+ 2 1 3) (+ 2 3 1) (- 1 2) (- 2 1 2)" 27 | {:linters {:plus-one {:level :warning} 28 | :minus-one {:level :warning}}})))) 29 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/cljc/cljs.env.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~:filename","cljs/env.cljc","~:clj",["^ ","~$*compiler*",["^ ","~:row",44,"~:col",1,"~:name","^2","~:ns","~$cljs.env","~:top-ns","^7"],"~$default-compiler-env*",["^ ","^3",46,"^4",1,"~:fixed-arities",["~#set",[1]],"^5","^9","^6","^7","^8","^7","~:arities",["^ ","~i1",["^ ","~:ret","~:nilable/map"]],"~:type","~:fn"],"~$default-compiler-env",["^ ","^3",59,"^4",1,"^:",["^;",[0,1]],"^5","^A","^6","^7","^8","^7","^<",["^ ","~i0",["^ ","^=","~:atom"],"~i1",["^ ","^=","^B"]],"^?","^@"],"~$with-compiler-env",["^ ","^3",65,"^4",4,"~:macro",true,"~:varargs-min-arity",1,"^5","^C","^6","^7","^8","^7"],"~$ensure",["^ ","^3",80,"^4",4,"^D",true,"^E",0,"^5","^F","^6","^7","^8","^7"]],"~:cljs",["^ ","^2",["^ ","^3",44,"^4",1,"^5","^2","^6","^7","^8","^7"],"^9",["^ ","^3",46,"^4",1,"^:",["^;",[1]],"^5","^9","^6","^7","^8","^7","^<",["^ ","~i1",["^ ","^=","^>"]],"^?","^@"],"^A",["^ ","^3",59,"^4",1,"^:",["^;",[0,1]],"^5","^A","^6","^7","^8","^7","^<",["^ ","~i0",["^ ","^=","^B"],"~i1",["^ ","^=","^B"]],"^?","^@"]]] -------------------------------------------------------------------------------- /analysis/script/unused_vars.cljs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env plk -K 2 | 3 | (ns script.unused-vars 4 | (:require [cljs.reader :as edn] 5 | [clojure.set :as set] 6 | [clojure.string :as str] 7 | [planck.shell :refer [sh]])) 8 | 9 | (defn -main [& paths] 10 | (let [out (:out (apply sh "clj-kondo" "--config" "{:output {:format :edn}, :analysis true}" 11 | "--lint" paths)) 12 | analysis (:analysis (edn/read-string out)) 13 | {:keys [:var-definitions :var-usages]} analysis 14 | defined-vars (set (map (juxt :ns :name) var-definitions)) 15 | used-vars (set (map (juxt :to :name) var-usages)) 16 | unused-vars (map (fn [[ns v]] 17 | (symbol (str ns) (str v))) 18 | (set/difference defined-vars used-vars))] 19 | (if (seq unused-vars) 20 | (do (println "The following vars are unused:") 21 | (println (str/join "\n" unused-vars))) 22 | (println "No unused vars found.")))) 23 | 24 | (set! *main-cli-fn* -main) 25 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/clj/clojure.java.process.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$capture",["^ ","~:row",118,"~:col",1,"~:varargs-min-arity",1,"~:name","^0","~:ns","~$clojure.java.process","~:top-ns","^6","~:type","~:fn"],"~$from-file",["^ ","^1",51,"^2",1,"~:fixed-arities",["~#set",[1]],"^4","^:","^5","^6","^7","^6","^8","^9"],"~$ok?",["^ ","^1",111,"^2",1,"^;",["^<",[1]],"^4","^=","^5","^6","^7","^6","~:arities",["^ ","~i1",["^ ","~:ret","~:boolean"]],"^8","^9"],"~$null-file",["^ ","^1",34,"^2",1,"~:private",true,"^4","^A","^5","^6","^7","^6"],"~$io-thread-factory",["^ ","^1",130,"^2",1,"^B",true,"^4","^C","^5","^6","^7","^6"],"~$exec",["^ ","^1",163,"^2",1,"^3",0,"^4","^D","^5","^6","^7","^6","^8","^9"],"~:filename","clojure/java/process.clj","~$start",["^ ","^1",58,"^2",1,"^3",0,"^4","^F","^5","^6","^7","^6","^8","^9"],"~$io-executor",["^ ","^1",139,"^2",1,"^B",true,"^4","^G","^5","^6","^7","^6"],"~$to-file",["^ ","^1",41,"^2",1,"^3",1,"^4","^H","^5","^6","^7","^6","^8","^9"],"~$io-task",["^ ","^1",142,"^2",1,"^;",["^<",[1]],"^4","^I","^5","^6","^7","^6","^8","^9"]] -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/clj/clojure.walk.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$postwalk",["^ ","~:row",53,"~:col",1,"~:fixed-arities",["~#set",[2]],"~:name","^0","~:ns","~$clojure.walk","~:top-ns","^7","~:type","~:fn"],"~$keywordize-keys",["^ ","^1",94,"^2",1,"^3",["^4",[1]],"^5","^;","^6","^7","^8","^7","^9","^:"],"~$walk",["^ ","^1",35,"^2",1,"^3",["^4",[3]],"^5","^<","^6","^7","^8","^7","^9","^:"],"~$prewalk-replace",["^ ","^1",110,"^2",1,"^3",["^4",[2]],"^5","^=","^6","^7","^8","^7","^9","^:"],"~$stringify-keys",["^ ","^1",102,"^2",1,"^3",["^4",[1]],"^5","^>","^6","^7","^8","^7","^9","^:"],"~$prewalk",["^ ","^1",61,"^2",1,"^3",["^4",[2]],"^5","^?","^6","^7","^8","^7","^9","^:"],"~:filename","clojure/walk.clj","~$postwalk-demo",["^ ","^1",80,"^2",1,"^3",["^4",[1]],"^5","^A","^6","^7","^8","^7","^9","^:"],"~$prewalk-demo",["^ ","^1",87,"^2",1,"^3",["^4",[1]],"^5","^B","^6","^7","^8","^7","^9","^:"],"~$macroexpand-all",["^ ","^1",126,"^2",1,"^3",["^4",[1]],"^5","^C","^6","^7","^8","^7","^9","^:"],"~$postwalk-replace",["^ ","^1",118,"^2",1,"^3",["^4",[2]],"^5","^D","^6","^7","^8","^7","^9","^:"]] -------------------------------------------------------------------------------- /.circleci/script/tools.deps: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | err=0 4 | function _trap_error() { 5 | local exit_code="$1" 6 | if [ "$exit_code" -ne 2 ] && [ "$exit_code" -ne 3 ]; then 7 | (( err |= "$exit_code" )) 8 | fi 9 | } 10 | 11 | trap '_trap_error $?' ERR 12 | trap 'exit $err' SIGINT SIGTERM 13 | 14 | 15 | # Run as local root dependency 16 | rm -rf /tmp/proj 17 | mkdir -p /tmp/proj 18 | cd /tmp/proj 19 | clojure -Sdeps '{:deps {clj-kondo {:local/root "/home/circleci/repo"}}}' \ 20 | -m clj-kondo.main --lint /home/circleci/repo/src /home/circleci/repo/test 21 | 22 | # Run as git dependency 23 | rm -rf /tmp/proj 24 | mkdir -p /tmp/proj 25 | cd /tmp/proj 26 | 27 | github_user=${CIRCLE_PR_USERNAME} # for PRs 28 | github_user=${github_user:-${CIRCLE_PROJECT_USERNAME}} # for non-PR forks 29 | github_user=${github_user:-borkdude} 30 | clojure -Sdeps "{:deps {clj-kondo {:git/url \"https://github.com/$github_user/clj-kondo\" :sha \"$CIRCLE_SHA1\"}}}" \ 31 | -m clj-kondo.main --lint /home/circleci/repo/src /home/circleci/repo/test 32 | 33 | exit "$err" 34 | -------------------------------------------------------------------------------- /parser/clj_kondo/impl/rewrite_clj/node/forms.clj: -------------------------------------------------------------------------------- 1 | (ns ^{:no-doc true} clj-kondo.impl.rewrite-clj.node.forms 2 | (:require [clj-kondo.impl.rewrite-clj.node.protocols :as node])) 3 | 4 | ;; ## Node 5 | 6 | (defrecord FormsNode [children] 7 | node/Node 8 | (tag [_] 9 | :forms) 10 | (printable-only? [_] 11 | false) 12 | (sexpr [_] 13 | (let [es (node/sexprs children)] 14 | (if (next es) 15 | (list* 'do es) 16 | (first es)))) 17 | (length [_] 18 | (node/sum-lengths children)) 19 | (string [_] 20 | (node/concat-strings children)) 21 | 22 | node/InnerNode 23 | (inner? [_] 24 | true) 25 | (children [_] 26 | children) 27 | (replace-children [this children'] 28 | (assoc this :children children')) 29 | (leader-length [_] 30 | 0) 31 | 32 | Object 33 | (toString [this] 34 | (node/string this))) 35 | 36 | (node/make-printable! FormsNode) 37 | 38 | ;; ## Constructor 39 | 40 | (defn forms-node 41 | "Create top-level node wrapping multiple children 42 | (equals an implicit `do` on the top-level)." 43 | [children] 44 | (->FormsNode children)) 45 | -------------------------------------------------------------------------------- /corpus/schema/defmethod.clj: -------------------------------------------------------------------------------- 1 | (ns schema.defmethod 2 | (:require 3 | [integrant.core :as ig] 4 | [schema.core :as sc])) 5 | 6 | ;; no false positives from this: 7 | 8 | (sc/defmethod ig/init-key :config :- {:config/env sc/Keyword} 9 | [_ 10 | {:keys [:config/env]} :- {:config/env sc/Keyword}] 11 | {:config/env env}) 12 | 13 | ;; When dispatch-val is vector 14 | (sc/defmethod ig/init-key [:config1 :config2] :- {:config/env sc/Keyword} 15 | [_ 16 | {:keys [:config/env]} :- {:config/env sc/Keyword}] 17 | (let [a 1] 18 | {:config/env env 19 | :a a})) 20 | 21 | ;; Missing return schema 22 | (sc/defmethod ig/init-key [:config1 :config2] 23 | [_ 24 | {:keys [:config/env]} :- {:config/env sc/Keyword}] 25 | (let [a 1] 26 | {:config/env env 27 | :a a})) 28 | 29 | ;; With multiple arities 30 | (sc/defmethod ig/init-key [:config1 :config2] :- {:config/env sc/Keyword} 31 | ([_ 32 | {:keys [:config/env]} :- {:config/env sc/Keyword}] 33 | {:config/env env}) 34 | ([_ :- sc/Str 35 | _ :- sc/Int 36 | {:keys [:config/env]} :- {:config/env sc/Keyword}] 37 | {:config/env env})) 38 | -------------------------------------------------------------------------------- /script/compile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [ -z "$GRAALVM_HOME" ]; then 4 | echo "Please set GRAALVM_HOME" 5 | exit 1 6 | fi 7 | 8 | "$GRAALVM_HOME/bin/gu" install native-image || true 9 | 10 | export JAVA_HOME=$GRAALVM_HOME 11 | export PATH=$GRAALVM_HOME/bin:$PATH 12 | 13 | CLJ_KONDO_VERSION=$(cat resources/CLJ_KONDO_VERSION) 14 | 15 | export CLJ_KONDO_NATIVE=true 16 | 17 | lein with-profiles +clojure-1.10.2 "do" clean, uberjar 18 | 19 | args=( "-jar" "target/clj-kondo-$CLJ_KONDO_VERSION-standalone.jar" 20 | "-H:+ReportExceptionStackTraces" 21 | "--verbose" 22 | "--no-fallback" 23 | "-J-Xmx3g" "$@") 24 | 25 | if [ "$CLJ_KONDO_STATIC" = "true" ]; then 26 | args+=("--static") 27 | if [ "$CLJ_KONDO_MUSL" = "true" ]; then 28 | args+=("--libc=musl" 29 | # see https://github.com/oracle/graal/issues/3398 30 | "-H:CCompilerOption=-Wl,-z,stack-size=2097152") 31 | else 32 | # see https://github.com/oracle/graal/issues/3737 33 | args+=("-H:+StaticExecutableWithDynamicLibC") 34 | fi 35 | fi 36 | 37 | "$GRAALVM_HOME/bin/native-image" "${args[@]}" 38 | -------------------------------------------------------------------------------- /parser/clj_kondo/impl/rewrite_clj/node/uneval.clj: -------------------------------------------------------------------------------- 1 | (ns ^{:no-doc true} clj-kondo.impl.rewrite-clj.node.uneval 2 | (:require [clj-kondo.impl.rewrite-clj.node.protocols :as node])) 3 | 4 | ;; ## Node 5 | 6 | (defrecord UnevalNode [children] 7 | node/Node 8 | (tag [_] :uneval) 9 | (printable-only? [_] true) 10 | (sexpr [_] 11 | (throw (UnsupportedOperationException.))) 12 | (length [_] 13 | (+ 2 (node/sum-lengths children))) 14 | (string [_] 15 | (str "#_" (node/concat-strings children))) 16 | 17 | node/InnerNode 18 | (inner? [_] true) 19 | (children [_] children) 20 | (replace-children [this children'] 21 | (node/assert-single-sexpr children') 22 | (assoc this :children children')) 23 | (leader-length [_] 24 | 2) 25 | 26 | Object 27 | (toString [this] 28 | (node/string this))) 29 | 30 | (node/make-printable! UnevalNode) 31 | 32 | ;; ## Constructor 33 | 34 | (defn uneval-node 35 | "Create node representing an EDN uneval `#_` form." 36 | [children] 37 | (if (sequential? children) 38 | (do 39 | (node/assert-single-sexpr children) 40 | (->UnevalNode children)) 41 | (recur [children]))) 42 | -------------------------------------------------------------------------------- /src/clj_kondo/impl/analyzer/datalog.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.impl.analyzer.datalog 2 | {:no-doc true} 3 | (:require 4 | [clj-kondo.impl.findings :as findings] 5 | [clj-kondo.impl.utils :as utils :refer 6 | [node->line tag one-of tag sexpr]] 7 | [datalog.parser :as datalog])) 8 | 9 | (set! *warn-on-reflection* true) 10 | 11 | (defn analyze-datalog [ctx expr] 12 | (let [children (next (:children expr)) 13 | query-raw (first children) 14 | quoted? (when query-raw 15 | (= :quote (tag query-raw))) 16 | datalog-node (when quoted? 17 | (when-let [edn-node (first (:children query-raw))] 18 | (when (one-of (tag edn-node) [:vector :map]) 19 | edn-node)))] 20 | (when datalog-node 21 | (try 22 | (datalog/parse (sexpr datalog-node)) 23 | nil 24 | (catch Exception e 25 | (findings/reg-finding! ctx 26 | (node->line (:filename ctx) query-raw 27 | :datalog-syntax 28 | (.getMessage e)))))))) 29 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/cljs/cljs.loader.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$*module-manager*",["^ ","~:row",47,"~:col",1,"~:name","^0","~:ns","~$cljs.loader","~:top-ns","^5"],"~$loaded?",["^ ","^1",53,"^2",1,"~:fixed-arities",["~#set",[1]],"^3","^7","^4","^5","^6","^5","~:type","~:fn"],"~$to-tr-url",["^ ","^1",30,"^2",1,"^8",["^9",[1]],"^3","^<","^4","^5","^6","^5","^:","^;"],"~$set-loaded!",["^ ","^1",78,"^2",1,"^8",["^9",[1]],"^3","^=","^4","^5","^6","^5","^:","^;"],"~$prefetch",["^ ","^1",96,"^2",1,"^8",["^9",[1]],"^3","^>","^4","^5","^6","^5","^:","^;"],"~$load",["^ ","^1",64,"^2",1,"^8",["^9",[1,2]],"^3","^?","^4","^5","^6","^5","^:","^;"],"~$deps-for",["^ ","^1",21,"^2",1,"^8",["^9",[2]],"^3","^@","^4","^5","^6","^5","^:","^;"],"~$module-uris",["^ ","^1",16,"^2",1,"^3","^A","^4","^5","^6","^5","^:","~:any"],"~:filename","cljs/loader.cljs","~$to-js",["^ ","^1",34,"^2",1,"^8",["^9",[1]],"^3","^D","^4","^5","^6","^5","^:","^;"],"~$munge-kw",["^ ","^1",26,"^2",1,"^8",["^9",[1]],"^3","^E","^4","^5","^6","^5","^:","^;"],"~$module-infos",["^ ","^1",15,"^2",1,"^3","^F","^4","^5","^6","^5"],"~$create-module-manager",["^ ","^1",41,"^2",1,"^8",["^9",[0]],"^3","^G","^4","^5","^6","^5","^:","^;"]] -------------------------------------------------------------------------------- /parser/clj_kondo/impl/rewrite_clj/parser/namespaced_map.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.impl.rewrite-clj.parser.namespaced-map 2 | {:no-doc true} 3 | (:require 4 | [clj-kondo.impl.rewrite-clj.node :as node] 5 | [clj-kondo.impl.rewrite-clj.node.seq :refer [namespaced-map-node]] 6 | [clj-kondo.impl.rewrite-clj.reader :as reader] 7 | [clojure.string :as str])) 8 | 9 | (defn parse-map-ns 10 | ;; parse map namespace inside reader tag 11 | [reader] 12 | (reader/ignore reader) 13 | (let [colons (reader/read-while reader (fn [c] 14 | (= \: c))) 15 | aliased? (= ":" colons) 16 | s (str/trim (reader/read-until reader 17 | (fn [c] 18 | (= \{ c)))) 19 | k (if (= "" s) 20 | :__current-ns__ 21 | (keyword s))] 22 | (node/keyword-node k aliased?))) 23 | 24 | (defn parse-namespaced-map 25 | [reader read-next] 26 | (let [map-ns (parse-map-ns reader) 27 | aliased? (:namespaced? map-ns) 28 | the-map (read-next reader)] 29 | (namespaced-map-node map-ns aliased? 30 | [the-map]))) 31 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/clj/clojure.java.javadoc.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$javadoc",["^ ","~:row",92,"~:col",1,"~:fixed-arities",["~#set",[1]],"~:name","^0","~:ns","~$clojure.java.javadoc","~:top-ns","^7","~:type","~:fn"],"~$add-local-javadoc",["^ ","^1",47,"^2",1,"^3",["^4",[1]],"^5","^;","^6","^7","^8","^7","^9","^:"],"~$*core-java-api*",["^ ","^1",21,"^2",1,"^5","^<","^6","^7","^8","^7"],"~$*feeling-lucky-url*",["^ ","^1",16,"^2",1,"^5","^=","^6","^7","^8","^7"],"~$*local-javadocs*",["^ ","^1",19,"^2",1,"^5","^>","^6","^7","^8","^7"],"~:filename","clojure/java/javadoc.clj","~$add-remote-javadoc",["^ ","^1",53,"^2",1,"^3",["^4",[2]],"^5","^@","^6","^7","^8","^7","^9","^:"],"~$*remote-javadocs*",["^ ","^1",33,"^2",1,"^5","^A","^6","^7","^8","^7"],"~$*feeling-lucky*",["^ ","^1",17,"^2",1,"^5","^B","^6","^7","^8","^7"],"~$fill-in-module-name",["^ ","^1",60,"^2",1,"~:private",true,"^3",["^4",[2]],"^5","^C","^6","^7","^8","^7","~:arities",["^ ","~i2",["^ ","~:ret",["^4",["~:nilable/string","~:string"]],"~:args",["~#list",["^G","^G"]]]],"^9","^:"],"~$javadoc-url",["^ ","^1",70,"^2",1,"^D",true,"^3",["^4",[1]],"^5","^K","^6","^7","^8","^7","^E",["^ ","~i1",["^ ","^I",["^J",["^G"]]]],"^9","^:"]] -------------------------------------------------------------------------------- /script/diff: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | mkdir -p /tmp/clj-kondo-diff 4 | 5 | cp="$(clojure -Spath -A:cljs)" 6 | read -r -d '' config <<'EOF' || true 7 | {;; :analysis {:keywords true} 8 | :linters 9 | {:redundant-fn-wrapper {:level :warning} 10 | :not-a-function 11 | {:skip-args [clojure.pprint/defdirectives 12 | cljs.pprint/defdirectives 13 | clojure.data.json/codepoint-case]} 14 | :def-fn {:level :warning}}} 15 | EOF 16 | 17 | out="/tmp/clj-kondo-diff/branch.txt" 18 | 19 | export CLJ_KONDO_DEV=true 20 | 21 | echo "Linting and writing output to $out" 22 | rm -rf .clj-kondo/.cache 23 | clojure -M:clj-kondo/dev --lint "$cp" --cache --config "$config" --parallel > "$out" 24 | 25 | cd /tmp 26 | rm -rf clj-kondo 27 | git clone https://github.com/clj-kondo/clj-kondo.git 28 | cd clj-kondo 29 | 30 | if [ -n "$1" ]; then 31 | echo "git reset $1 --hard" 32 | git reset $1 --hard 33 | fi 34 | 35 | out="/tmp/clj-kondo-diff/master.txt" 36 | 37 | echo "Linting and writing output to $out" 38 | clojure -M:clj-kondo/dev --lint "$cp" --cache --config "$config" --parallel > "$out" 39 | 40 | diff /tmp/clj-kondo-diff/branch.txt /tmp/clj-kondo-diff/master.txt 41 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/cljc/cljs.spec.gen.alpha.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~:filename","cljs/spec/gen/alpha.cljc","~:clj",["^ ","~$dynaload",["^ ","~:row",14,"~:col",1,"~:macro",true,"~:fixed-arities",["~#set",[1]],"~:name","^2","~:ns","~$cljs.spec.gen.alpha","~:top-ns","^:"],"~$delay",["^ ","^3",25,"^4",1,"^5",true,"~:varargs-min-arity",0,"^8","^<","^9","^:","^;","^:"],"~$lazy-combinator",["^ ","^3",32,"^4",1,"^5",true,"^6",["^7",[1]],"^8","^>","^9","^:","^;","^:"],"~$lazy-combinators",["^ ","^3",43,"^4",1,"^5",true,"^=",0,"^8","^?","^9","^:","^;","^:"],"~$lazy-prim",["^ ","^3",51,"^4",1,"^5",true,"^6",["^7",[1]],"^8","^@","^9","^:","^;","^:"],"~$lazy-prims",["^ ","^3",62,"^4",1,"^5",true,"^=",0,"^8","^A","^9","^:","^;","^:"]],"~:cljs",["^ ","^2",["^ ","^3",14,"^4",1,"^5",true,"^6",["^7",[1]],"^8","^2","^9","^:","^;","^:"],"^<",["^ ","^3",25,"^4",1,"^5",true,"^=",0,"^8","^<","^9","^:","^;","^:"],"^>",["^ ","^3",32,"^4",1,"^5",true,"^6",["^7",[1]],"^8","^>","^9","^:","^;","^:"],"^?",["^ ","^3",43,"^4",1,"^5",true,"^=",0,"^8","^?","^9","^:","^;","^:"],"^@",["^ ","^3",51,"^4",1,"^5",true,"^6",["^7",[1]],"^8","^@","^9","^:","^;","^:"],"^A",["^ ","^3",62,"^4",1,"^5",true,"^=",0,"^8","^A","^9","^:","^;","^:"]]] -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | [ Leave this text in if you want to promote it. ] 11 | 12 | To upvote this issue, give it a thumbs up. See [this list](https://github.com/clj-kondo/clj-kondo/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc) for the most upvoted issues. 13 | 14 | [ To keep development of this project going, consider sponsoring. If you are 15 | already a sponsor, thank you! You can remove this message. ] 16 | 17 | **Is your feature request related to a problem? Please describe.** 18 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 19 | 20 | **Describe the solution you'd like** 21 | A clear and concise description of what you want to happen. 22 | 23 | **Describe alternatives you've considered** 24 | A clear and concise description of any alternative solutions or features you've considered. 25 | 26 | **Additional context** 27 | Add any other context or screenshots about the feature request here. 28 | 29 | [ Optional: if you are interested in doing a PR yourself, please leave a note. ] 30 | -------------------------------------------------------------------------------- /parser/clj_kondo/impl/rewrite_clj/node/integer.clj: -------------------------------------------------------------------------------- 1 | (ns ^{:no-doc true} clj-kondo.impl.rewrite-clj.node.integer 2 | (:require [clj-kondo.impl.rewrite-clj.node.protocols :as node])) 3 | 4 | ;; ## Node 5 | 6 | (defrecord IntNode [value base] 7 | node/Node 8 | (tag [_] :token) 9 | (printable-only? [_] false) 10 | (sexpr [_] value) 11 | (length [this] 12 | (count (node/string this))) 13 | (string [_] 14 | (let [sign (if (< value 0) 15 | "-") 16 | abs-value (cond-> value (< value 0) -) 17 | s (.toString (biginteger abs-value) base) 18 | prefix (case (long base) 19 | 8 "0" 20 | 10 "" 21 | 16 "0x" 22 | (str base "r"))] 23 | (str sign prefix s))) 24 | 25 | Object 26 | (toString [this] 27 | (node/string this))) 28 | 29 | (node/make-printable! IntNode) 30 | 31 | ;; ## Constructor 32 | 33 | (defn integer-node 34 | "Create node for an EDN integer with the given base." 35 | ([value] 36 | (integer-node value 10)) 37 | ([value base] 38 | {:pre [(integer? value) 39 | (integer? base) 40 | (< 1 base 37)]} 41 | (->IntNode value base))) 42 | -------------------------------------------------------------------------------- /parser/clj_kondo/impl/rewrite_clj/node/keyword.clj: -------------------------------------------------------------------------------- 1 | (ns ^{:no-doc true} clj-kondo.impl.rewrite-clj.node.keyword 2 | (:require [clj-kondo.impl.rewrite-clj.node.protocols :as node])) 3 | 4 | ;; ## Node 5 | 6 | (defrecord KeywordNode [k namespaced?] 7 | node/Node 8 | (tag [_] :token) 9 | (printable-only? [_] false) 10 | (sexpr [_] 11 | (if (and namespaced? 12 | (not (namespace k))) 13 | (keyword 14 | (name (ns-name *ns*)) 15 | (name k)) 16 | k)) 17 | (length [this] 18 | (let [c (inc (count (name k)))] 19 | (if namespaced? 20 | (inc c) 21 | (if-let [nspace (namespace k)] 22 | (+ 1 c (count nspace)) 23 | c)))) 24 | (string [_] 25 | (str (when namespaced? ":") 26 | (pr-str k))) 27 | 28 | Object 29 | (toString [this] 30 | (node/string this))) 31 | 32 | (node/make-printable! KeywordNode) 33 | 34 | ;; ## Constructor 35 | 36 | (defn keyword-node 37 | "Create node representing a keyword. If `namespaced?` is given as `true` 38 | a keyword à la `::x` or `::ns/x` (i.e. namespaced/aliased) is generated." 39 | [k & [namespaced?]] 40 | {:pre [(keyword? k)]} 41 | (->KeywordNode k namespaced?)) 42 | -------------------------------------------------------------------------------- /corpus/hooks/expectations.clj: -------------------------------------------------------------------------------- 1 | (ns hooks.expectations 2 | {:clj-kondo/config '{:hooks {:analyze-call {expectations.clojure.test/more-of " 3 | 4 | (require '[clj-kondo.hooks-api :as api]) 5 | 6 | (fn [{:keys [:node]}] 7 | (let [children (rest (:children node))] 8 | {:node (api/list-node (list* (api/token-node 'let) 9 | (api/vector-node 10 | [(first children) (api/token-node nil)]) 11 | (rest children)))})) 12 | 13 | "}}}} 14 | (:require [expectations.clojure.test :as t])) 15 | 16 | (t/expecting "numeric behavior" 17 | (t/expect (t/more-of {:keys [a b]} ;; bindings are recognized 18 | even? a 19 | odd? b) 20 | {:a (* 2 13) :b (* 3 13)}) 21 | (t/expect pos? (* -3 -5))) 22 | 23 | (t/expecting "numeric behavior" 24 | (t/expect (t/more-of {:keys [a b]} ;; unused binding b 25 | even? a 26 | odd? b') ;; unresolved binding b' 27 | {:a (* 2 13) :b (* 3 13)}) 28 | (t/expect pos? (* -3 -5))) 29 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/clj/clojure.java.shell.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$sh",["^ ","~:row",79,"~:col",1,"~:varargs-min-arity",0,"~:name","^0","~:ns","~$clojure.java.shell","~:top-ns","^6","~:type","~:fn"],"~$as-env-strings",["^ ","^1",52,"^2",1,"~:private",true,"~:fixed-arities",["~#set",[1]],"^4","^:","^5","^6","^7","^6","^8","^9"],"~$parse-args",["^ ","^1",45,"^2",1,"^;",true,"^<",["^=",[1]],"^4","^>","^5","^6","^7","^6","~:arities",["^ ","~i1",["^ ","~:ret","~:vector"]],"^8","^9"],"~$stream-to-enc",["^ ","^1",73,"^2",1,"^;",true,"^<",["^=",[2]],"^4","^B","^5","^6","^7","^6","^8","^9"],"~$stream-to-string",["^ ","^1",66,"^2",1,"^;",true,"^<",["^=",[1,2]],"^4","^C","^5","^6","^7","^6","^8","^9"],"~:filename","clojure/java/shell.clj","~$*sh-dir*",["^ ","^1",18,"^2",1,"^4","^E","^5","^6","^7","^6"],"~$with-sh-dir",["^ ","^1",21,"^2",1,"~:macro",true,"^3",1,"^4","^F","^5","^6","^7","^6"],"~$aconcat",["^ ","^1",35,"^2",1,"^;",true,"^3",1,"^4","^H","^5","^6","^7","^6","^8","^9"],"~$*sh-env*",["^ ","^1",19,"^2",1,"^4","^I","^5","^6","^7","^6"],"~$with-sh-env",["^ ","^1",28,"^2",1,"^G",true,"^3",1,"^4","^J","^5","^6","^7","^6"],"~$stream-to-bytes",["^ ","^1",60,"^2",1,"^;",true,"^<",["^=",[1]],"^4","^K","^5","^6","^7","^6","^8","^9"]] -------------------------------------------------------------------------------- /analysis/src/clj_kondo/tools/find_var.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.tools.find-var 2 | (:require 3 | [clj-kondo.core :as clj-kondo] 4 | [clojure.string :as str])) 5 | 6 | (defn -main [var & paths] 7 | (let [[var-ns var-name] (map symbol (str/split var #"/")) 8 | analysis (:analysis (clj-kondo/run! {:lint paths 9 | :config {:analysis true}})) 10 | {:keys [var-definitions var-usages]} analysis 11 | defined (keep (fn [{:keys [ns name] :as d}] 12 | (when (and (= var-ns ns) 13 | (= var-name name)) 14 | d)) 15 | var-definitions) 16 | usages (keep (fn [{:keys [to name] :as d}] 17 | (when (and (= var-ns to) 18 | (= var-name name)) 19 | d)) 20 | var-usages)] 21 | (doseq [{:keys [filename row col]} (sort-by (juxt :filename :row :col) defined)] 22 | (println (str var " is defined at " filename ":" row ":" col))) 23 | (doseq [{:keys [filename row col]} (sort-by (juxt :filename :row :col) usages)] 24 | (println (str var " is used at " filename ":" row ":" col))))) 25 | -------------------------------------------------------------------------------- /test/clj_kondo/warn_on_reflection_test.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.warn-on-reflection-test 2 | (:require 3 | [clj-kondo.test-utils :refer [lint! assert-submaps]] 4 | [clojure.test :as t :refer [deftest is testing]])) 5 | 6 | (deftest warn-on-reflection-test 7 | (assert-submaps 8 | [] 9 | (lint! "(ns foo) (defn foo [])" 10 | '{:linters {:warn-on-reflection {:level :warning}}})) 11 | (assert-submaps 12 | '({:file "", :row 1, :col 1, :level :warning, :message "Var *warn-on-reflection* is not set in this namespace."}) 13 | (lint! "(ns foo) (defn foo [])" 14 | '{:linters {:warn-on-reflection {:level :warning 15 | :warn-only-on-interop false}}})) 16 | (assert-submaps 17 | '({:file "", :row 1, :col 23, :level :warning, :message "Var *warn-on-reflection* is not set in this namespace."}) 18 | (lint! "(ns foo) (defn foo [] (.foo ))" 19 | '{:linters {:warn-on-reflection {:level :warning}}})) 20 | (assert-submaps 21 | '({:file "", :row 1, :col 23, :level :warning, :message "Var *warn-on-reflection* is not set in this namespace."}) 22 | (lint! "(ns foo) (defn foo [] (Thread/sleep 100))" 23 | '{:linters {:warn-on-reflection {:level :warning}}}))) 24 | -------------------------------------------------------------------------------- /parser/clj_kondo/impl/rewrite_clj/node/string.clj: -------------------------------------------------------------------------------- 1 | (ns ^{:no-doc true} clj-kondo.impl.rewrite-clj.node.string 2 | (:require [clj-kondo.impl.rewrite-clj.node.protocols :as node] 3 | [clj-kondo.impl.toolsreader.v1v2v2.clojure.tools.reader.edn :as edn] 4 | [clojure.string :as string])) 5 | 6 | ;; ## Node 7 | 8 | (defn- wrap-string 9 | [s] 10 | (format "\"%s\"" s)) 11 | 12 | (defn- join-lines 13 | [lines] 14 | (string/join "\n" lines)) 15 | 16 | (defrecord StringNode [lines] 17 | node/Node 18 | (tag [_] 19 | (if (next lines) 20 | :multi-line 21 | :token)) 22 | (printable-only? [_] 23 | false) 24 | (sexpr [_] 25 | (join-lines 26 | (map 27 | (comp edn/read-string wrap-string) 28 | lines))) 29 | (length [_] 30 | (+ 2 (reduce + (map count lines)))) 31 | (string [_] 32 | (wrap-string (join-lines lines))) 33 | 34 | Object 35 | (toString [this] 36 | (node/string this))) 37 | 38 | (node/make-printable! StringNode) 39 | 40 | ;; ## Constructors 41 | 42 | (defn string-node 43 | "Create node representing a string value. 44 | Takes either a seq of strings or a single one." 45 | [lines] 46 | (if (string? lines) 47 | (->StringNode [lines]) 48 | (->StringNode lines))) 49 | -------------------------------------------------------------------------------- /corpus/java/sources/foo/bar/AwesomeClass.java: -------------------------------------------------------------------------------- 1 | package foo.bar; 2 | 3 | import java.io.File; 4 | import java.util.List; 5 | import java.security.NoSuchAlgorithmException; 6 | import java.security.spec.InvalidKeySpecException; 7 | 8 | public class AwesomeClass { 9 | 10 | private String foo1; 11 | private final Double foo2 = 1.0; 12 | private final static Double foo3 = 1.0; 13 | private long[] foo4 = new long[2]; 14 | 15 | public Double bar1; 16 | public final Double bar2 = 1.0; 17 | public static final Double bar3 = 1.0; 18 | 19 | public AwesomeClass(double a) { 20 | System.out.println("Initializing..." + a); 21 | } 22 | 23 | public int coolSum1(double a, double b) { 24 | try { 25 | return (int) a + (int) b; 26 | } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { 27 | return -1; 28 | } 29 | } 30 | 31 | /* 32 | * Some cool doc 33 | * @param filenames 34 | * @return list of files 35 | */ 36 | public static File[] coolParse(List filenames) { 37 | return null; 38 | } 39 | 40 | public Foo foo() { 41 | class Foo { 42 | private int a; 43 | } 44 | return Foo(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /corpus/macroexpand2.cljs: -------------------------------------------------------------------------------- 1 | (ns macroexpand2 2 | {:clj-kondo/config 3 | '{:hooks 4 | {:macroexpand {macroexpand2/$ macroexpand2/$ 5 | macroexpand2/form-env-macro macroexpand2/form-env-macro 6 | macroexpand2/private-defn macroexpand2/private-defn}}}}) 7 | 8 | (def sh (js/require "shelljs")) 9 | 10 | (defmacro $ [op & args] 11 | (list* (symbol (str "." op)) 'sh args)) 12 | 13 | (prn (str ($ which "git"))) 14 | (prn (str ($ pwd))) 15 | ($ cd "..") 16 | (-> ($ ls) prn) 17 | 18 | ;; so far no errors 19 | 20 | ($ which foobar) ;; foobar should be reported unresolved 21 | 22 | (defmacro form-env-macro [_] 23 | (list* 'clojure.core/+ 24 | (list* 'clojure.core/+ 25 | [(when (= 'form-env-macro (first &form)) "foo") 26 | (when (contains? &env 'x) :foo)]))) 27 | 28 | (let [x 1] 29 | x 30 | ;; Expected number, received string 31 | ;; Expected number, received keyword 32 | (form-env-macro (inc x))) 33 | 34 | (let [y 1] 35 | ;; Expected number, received nil (no x in scope) 36 | ;; Expected number, received keyword 37 | y 38 | (form-env-macro (inc x))) 39 | 40 | (defmacro private-defn [sym] 41 | `(defn ~(with-meta sym {:private true}) [])) 42 | 43 | (private-defn private-var) 44 | -------------------------------------------------------------------------------- /script/setup-musl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | if [[ -z "${CLJ_KONDO_STATIC:-}" ]]; then 6 | echo "CLJ_KONDO_STATIC wasn't set, skipping musl installation." 7 | exit 0 8 | fi 9 | 10 | if [[ -z "${CLJ_KONDO_MUSL:-}" ]]; then 11 | echo "CLJ_KONDO_MUSL wasn't set, skipping musl installation." 12 | exit 0 13 | fi 14 | 15 | if [[ "${CLJ_KONDO_ARCH:-"x86_64"}" != "x86_64" ]]; then 16 | echo "GraalVM only supports building static binaries on x86_64." 17 | exit 1 18 | fi 19 | 20 | apt-get update -y && apt-get install musl-tools -y 21 | 22 | ZLIB_VERSION="1.2.11" 23 | 24 | curl -O -sL "https://www.zlib.net/fossils/zlib-${ZLIB_VERSION}.tar.gz" 25 | 26 | echo "c3e5e9fdd5004dcb542feda5ee4f0ff0744628baf8ed2dd5d66f8ca1197cb1a1 zlib-${ZLIB_VERSION}.tar.gz" | 27 | sha256sum --check 28 | tar xf "zlib-${ZLIB_VERSION}.tar.gz" 29 | 30 | arch=${CLJ_KONDO_ARCH:-"x86_64"} 31 | echo "ARCH: $arch" 32 | 33 | cd "zlib-${ZLIB_VERSION}" 34 | CC=musl-gcc ./configure --static --prefix="/usr/local" 35 | make CC=musl-gcc 36 | make install 37 | cd .. 38 | 39 | # Install libz.a in the correct place so ldd can find it 40 | install -Dm644 "/usr/local/lib/libz.a" "/usr/lib/$arch-linux-musl/libz.a" 41 | 42 | ln -s /usr/bin/musl-gcc /usr/bin/x86_64-linux-musl-gcc 43 | -------------------------------------------------------------------------------- /analysis/src/clj_kondo/tools/circular_dependencies.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.tools.circular-dependencies 2 | (:require [clj-kondo.core :as clj-kondo] 3 | [com.stuartsierra.dependency :as dep])) 4 | 5 | (defn -main [& paths] 6 | (let [analysis (:analysis (clj-kondo/run! {:lint paths 7 | :config {:analysis {:var-usages false 8 | :var-definitions {:shallow true}}} 9 | :skip-lint true})) 10 | {:keys [:namespace-usages]} analysis] 11 | (reduce (fn [graph {:keys [:from :to :filename :row :col]}] 12 | (try (dep/depend graph from to) 13 | (catch Exception e 14 | (let [ed (ex-data e)] 15 | (if (= :com.stuartsierra.dependency/circular-dependency 16 | (:reason ed)) 17 | (do (println (str filename ":" row ":" col ":") 18 | "circular dependency from namespace" 19 | from "to" to) 20 | graph) 21 | (throw e)))))) 22 | (dep/graph) 23 | namespace-usages))) 24 | -------------------------------------------------------------------------------- /corpus/spec_syntax.clj: -------------------------------------------------------------------------------- 1 | (ns spec-syntax 2 | (:require 3 | [clojure.spec.alpha :as s] 4 | [clojure.string :as str])) 5 | 6 | (s/fdef my-inc ;; symbol isn't reported as unresolved 7 | :args (s/cat :x int?)) 8 | 9 | (s/fdef 1 :args) ;; expected symbol, missing value for :args 10 | 11 | (s/fdef foo :xargs (s/cat :x int?)) 12 | 13 | (defn my-inc [x] 14 | (+ 1 x)) 15 | 16 | ;; this takes care of "using" clojure.string, so it's not reported as unused anymore 17 | (s/fdef str/starts-with? :args (s/cat :s string? 18 | :substr string?)) 19 | 20 | (s/fdef xstr/starts-with? ,,,) ;; unresolved symbol, but should not give warning by default, #1532 21 | 22 | (s/def ::a any?) 23 | (s/def ::bar (s/keys 24 | ;; fine 25 | :opt [::a] 26 | :opt-un [::a] 27 | :req [::a] 28 | :req-un [::a] 29 | :gen (fn []) 30 | ;; unknown 31 | ::opt [::a] 32 | ::opt-un [::a] 33 | ::req [::a] 34 | ::req-un [::a] 35 | ::gen (fn []))) 36 | 37 | (require '[spec-keys :as sk]) ;; namespace is used because of below s/keys call 38 | 39 | (s/keys :req [::sk/my-key]) 40 | 41 | (s/def foobar int?) ;; no unresolved-symbol 42 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/clj/clojure.data.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$vectorize",["^ ","~:row",25,"~:col",1,"~:private",true,"~:fixed-arities",["~#set",[1]],"~:name","^0","~:ns","~$clojure.data","~:top-ns","^8","~:type","~:fn"],"~$diff",["^ ","^1",17,"^2",1,"^4",["^5",[2]],"^6","^<","^7","^8","^9","^8","^:","^;"],"~$atom-diff",["^ ","^1",19,"^2",1,"^3",true,"^4",["^5",[2]],"^6","^=","^7","^8","^9","^8","~:arities",["^ ","~i2",["^ ","~:ret",["^5",["~:vector"]]]],"^:","^;"],"~$equality-partition",["^ ","^1",71,"^2",3,"^4",["^5",[1]],"^6","^A","^7","^8","^9","^8"],"~$EqualityPartition",["^ ","^1",69,"^2",1,"^6","^B","^7","^8","^9","^8"],"~$diff-associative",["^ ","^1",51,"^2",1,"^3",true,"^4",["^5",[3]],"^6","^C","^7","^8","^9","^8","^:","^;"],"~$diff-associative-key",["^ ","^1",35,"^2",1,"^3",true,"^4",["^5",[3]],"^6","^D","^7","^8","^9","^8","^>",["^ ","~i3",["^ ","^?","^@"]],"^:","^;"],"~$diff-similar",["^ ","^1",75,"^2",3,"^4",["^5",[2]],"^6","^E","^7","^8","^9","^8"],"~$Diff",["^ ","^1",73,"^2",1,"^6","^F","^7","^8","^9","^8"],"~:filename","clojure/data.clj","~$as-set-value",["^ ","^1",102,"^2",1,"^3",true,"^4",["^5",[1]],"^6","^H","^7","^8","^9","^8","^:","^;"],"~$diff-sequential",["^ ","^1",62,"^2",1,"^3",true,"^4",["^5",[2]],"^6","^I","^7","^8","^9","^8","^>",["^ ","~i2",["^ ","^?","^@"]],"^:","^;"]] -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/cljs/clojure.data.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$vectorize",["^ ","~:row",23,"~:col",1,"~:private",true,"~:fixed-arities",["~#set",[1]],"~:name","^0","~:ns","~$clojure.data","~:top-ns","^8","~:type","~:fn"],"~$diff",["^ ","^1",15,"^2",1,"^4",["^5",[2]],"^6","^<","^7","^8","^9","^8","^:","^;"],"~$atom-diff",["^ ","^1",17,"^2",1,"^3",true,"^4",["^5",[2]],"^6","^=","^7","^8","^9","^8","~:arities",["^ ","~i2",["^ ","~:ret",["^5",["~:vector"]]]],"^:","^;"],"~$diff-set",["^ ","^1",69,"^2",1,"^3",true,"^4",["^5",[2]],"^6","^A","^7","^8","^9","^8","^>",["^ ","~i2",["^ ","^?","^@"]],"^:","^;"],"~$equality-partition",["^ ","^1",77,"^2",3,"^4",["^5",[1]],"^6","^B","^7","^8","^9","^8"],"~$EqualityPartition",["^ ","^1",75,"^2",1,"^6","^C","^7","^8","^9","^8"],"~$diff-associative",["^ ","^1",49,"^2",1,"^3",true,"^4",["^5",[3,2]],"^6","^D","^7","^8","^9","^8","^:","^;"],"~$diff-associative-key",["^ ","^1",33,"^2",1,"^3",true,"^4",["^5",[3]],"^6","^E","^7","^8","^9","^8","^>",["^ ","~i3",["^ ","^?","^@"]],"^:","^;"],"~$diff-similar",["^ ","^1",81,"^2",3,"^4",["^5",[2]],"^6","^F","^7","^8","^9","^8"],"~$Diff",["^ ","^1",79,"^2",1,"^6","^G","^7","^8","^9","^8"],"~:filename","clojure/data.cljs","~$diff-sequential",["^ ","^1",62,"^2",1,"^3",true,"^4",["^5",[2]],"^6","^I","^7","^8","^9","^8","^>",["^ ","~i2",["^ ","^?","^@"]],"^:","^;"]] -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/clj/clojure.inspector.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$inspect-table",["^ ","~:row",100,"~:col",1,"~:fixed-arities",["~#set",[1]],"~:name","^0","~:ns","~$clojure.inspector","~:top-ns","^7","~:type","~:fn"],"~$atom?",["^ ","^1",19,"^2",1,"^3",["^4",[1]],"^5","^;","^6","^7","^8","^7","~:arities",["^ ","~i1",["^ ","~:ret","~:boolean"]],"^9","^:"],"~$list-provider",["^ ","^1",112,"^2",1,"^5","^?","^6","^7","^8","^7"],"~$is-leaf",["^ ","^1",31,"^2",1,"^5","^@","^6","^7","^8","^7"],"~$old-table-model",["^ ","^1",72,"^2",1,"^3",["^4",[1]],"^5","^A","^6","^7","^8","^7","^9","^:"],"~$tree-model",["^ ","^1",56,"^2",1,"^3",["^4",[1]],"^5","^B","^6","^7","^8","^7","^9","^:"],"~$inspect",["^ ","^1",154,"^2",1,"^3",["^4",[1]],"^5","^C","^6","^7","^8","^7","^9","^:"],"~$collection-tag",["^ ","^1",22,"^2",1,"^3",["^4",[1]],"^5","^D","^6","^7","^8","^7","^<",["^ ","~i1",["^ ","^=",["^4",["~:keyword"]]]],"^9","^:"],"~:filename","clojure/inspector.clj","~$get-child-count",["^ ","^1",33,"^2",1,"^5","^G","^6","^7","^8","^7"],"~$inspect-tree",["^ ","^1",91,"^2",1,"^3",["^4",[1]],"^5","^H","^6","^7","^8","^7","^9","^:"],"~$get-child",["^ ","^1",32,"^2",1,"^5","^I","^6","^7","^8","^7"],"~$list-model",["^ ","^1",129,"^2",1,"^3",["^4",[1]],"^5","^J","^6","^7","^8","^7","^9","^:"],"~$table-model",["^ ","^1",139,"^2",1,"^5","^K","^6","^7","^8","^7"]] -------------------------------------------------------------------------------- /.circleci/script/performance: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | err=0 4 | function _trap_error() { 5 | local exit_code="$1" 6 | if [ "$exit_code" -ne 2 ] && [ "$exit_code" -ne 3 ]; then 7 | echo "EXIT CODE :( $exit_code" 8 | (( err |= "$exit_code" )) 9 | fi 10 | } 11 | 12 | trap '_trap_error $?' ERR 13 | trap 'exit $err' SIGINT SIGTERM 14 | 15 | 16 | rm -rf performance.txt 17 | echo -e "==== Build initial cache" | tee -a performance.txt 18 | cp="$(clojure -R:cljs -Spath)" 19 | read -r -d '' config <<'EOF' || true 20 | {:linters 21 | {:not-a-function 22 | {:skip-args [clojure.pprint/defdirectives 23 | cljs.pprint/defdirectives 24 | clojure.data.json/codepoint-case]}}} 25 | EOF 26 | 27 | (time ./clj-kondo --lint "$cp" --cache --config "$config" --parallel) 2>&1 | tee -a performance.txt 28 | 29 | echo -e "\n==== Lint a single file (emulate in-editor usage)" | tee -a performance.txt 30 | (time ./clj-kondo --lint src/clj_kondo/impl/core.clj --cache) 2>&1 | tee -a performance.txt 31 | 32 | count=$(find . -name "*.clj*" -type f | wc -l | tr -d ' ') 33 | echo -e "\n==== Launch clj-kondo for each file in project ($count)" | tee -a performance.txt 34 | (time find src -name "*.clj*" -type f -exec ./clj-kondo --lint {} --cache \; ) 2>&1 | tee -a performance.txt 35 | 36 | exit "$err" 37 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/clj/clojure.core.protocols.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$IKVReduce",["^ ","~:row",175,"~:col",1,"~:name","^0","~:ns","~$clojure.core.protocols","~:top-ns","^5"],"~$iter-reduce",["^ ","^1",33,"^2",1,"~:private",true,"~:fixed-arities",["~#set",[3,2]],"^3","^7","^4","^5","^6","^5","~:type","~:fn"],"~$Navigable",["^ ","^1",194,"^2",1,"^3","^=","^4","^5","^6","^5"],"~$nav",["^ ","^1",197,"^2",3,"^9",["^:",[3]],"^3","^>","^4","^5","^6","^5"],"~$Datafiable",["^ ","^1",182,"^2",1,"^3","^?","^4","^5","^6","^5"],"~$CollReduce",["^ ","^1",13,"^2",1,"^3","^@","^4","^5","^6","^5"],"~$interface-or-naive-reduce",["^ ","^1",68,"^2",1,"^8",true,"^9",["^:",[3]],"^3","^A","^4","^5","^6","^5","^;","^<"],"~$datafy",["^ ","^1",185,"^2",3,"^9",["^:",[1]],"^3","^B","^4","^5","^6","^5"],"~$coll-reduce",["^ ","^1",17,"^2",3,"^9",["^:",[3,2]],"^3","^C","^4","^5","^6","^5"],"~$internal-reduce",["^ ","^1",22,"^2",3,"^9",["^:",[3]],"^3","^D","^4","^5","^6","^5"],"~$seq-reduce",["^ ","^1",24,"^2",1,"^8",true,"^9",["^:",[3,2]],"^3","^E","^4","^5","^6","^5","^;","^<"],"~:filename","clojure/core/protocols.clj","~$InternalReduce",["^ ","^1",19,"^2",1,"^3","^G","^4","^5","^6","^5"],"~$kv-reduce",["^ ","^1",180,"^2",3,"^9",["^:",[3]],"^3","^H","^4","^5","^6","^5"],"~$naive-seq-reduce",["^ ","^1",55,"^2",1,"^8",true,"^9",["^:",[3]],"^3","^I","^4","^5","^6","^5","^;","^<"]] -------------------------------------------------------------------------------- /corpus/jdbc/cjj_test.clj: -------------------------------------------------------------------------------- 1 | (ns jdbc.cjj-test 2 | (:require [clojure.java.jdbc :as jdbc])) 3 | 4 | (defn tx-check 5 | [] 6 | (let [db {:dbtype "some-db" :dbname "kondo"}] 7 | ;; should not flag tx 8 | (jdbc/with-db-transaction [tx (jdbc/get-connection db)] 9 | (jdbc/execute! tx ["select * from table where foo = ?" 123])) 10 | ;; should not flag tx or binding arity 11 | (jdbc/with-db-transaction [tx (jdbc/get-connection db) {}] 12 | (jdbc/execute! tx ["select * from table where foo = ?" 123])))) 13 | 14 | (defn con-check 15 | [] 16 | (let [db {:dbtype "some-db" :dbname "kondo"}] 17 | ;; should not flag con 18 | (jdbc/with-db-connection [con db] 19 | (jdbc/query con ["select * from table where foo = ?" 123])) 20 | ;; should not flag con or binding arity 21 | (jdbc/with-db-connection [con db {}] 22 | (jdbc/query con ["select * from table where foo = ?" 123])))) 23 | 24 | (defn meta-check 25 | [] 26 | (let [db {:dbtype "some-db" :dbname "kondo"}] 27 | ;; should not flag m-con 28 | (jdbc/with-db-metadata [m-con db] 29 | (jdbc/metadata-query (.getTables m-con nil nil nil (into-array String [\"TABLE\"])))) 30 | ;; should not flag m-con or binding arity 31 | (jdbc/with-db-metadata [m-con db {}] 32 | (jdbc/metadata-query (.getTables m-con nil nil nil (into-array String [\"TABLE\"])))))) 33 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/cljs/clojure.browser.net.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$xpc-config-fields",["^ ","~:row",74,"~:col",1,"~:name","^0","~:ns","~$clojure.browser.net","~:top-ns","^5","~:type",["^ ","^7","~:map","~:val",["^ "]]],"~$xhr-connection",["^ ","^1",82,"^2",1,"~:fixed-arities",["~#set",[0]],"^3","^:","^4","^5","^6","^5","^7","~:fn"],"~$xpc-connection",["^ ","^1",117,"^2",1,"^;",["^<",[0,1]],"^3","^>","^4","^5","^6","^5","^7","^="],"~$ICrossPageChannel",["^ ","^1",87,"^2",1,"^3","^?","^4","^5","^6","^5"],"~$IWebSocket",["^ ","^1",144,"^2",1,"^3","^@","^4","^5","^6","^5"],"~$*timeout*",["^ ","^1",20,"^2",1,"^3","^A","^4","^5","^6","^5","^7","~:pos-int"],"~$connect",["^ ","^1",32,"^2",3,"^;",["^<",[1,4,3,2]],"^3","^C","^4","^5","^6","^5"],"~:filename","clojure/browser/net.cljs","~$event-types",["^ ","^1",22,"^2",1,"^3","^E","^4","^5","^6","^5","^7",["^ ","^7","^8","^9",["^ "]]],"~$websocket-connection",["^ ","^1",175,"^2",1,"^;",["^<",[0,1,2]],"^3","^F","^4","^5","^6","^5","^7","^="],"~$close",["^ ","^1",43,"^2",3,"^;",["^<",[1]],"^3","^G","^4","^5","^6","^5"],"~$register-service",["^ ","^1",88,"^2",3,"^;",["^<",[4,3]],"^3","^H","^4","^5","^6","^5"],"~$IConnection",["^ ","^1",31,"^2",1,"^3","^I","^4","^5","^6","^5"],"~$open?",["^ ","^1",145,"^2",3,"^;",["^<",[1]],"^3","^J","^4","^5","^6","^5"],"~$transmit",["^ ","^1",37,"^2",3,"^;",["^<",[4,6,3,2,5]],"^3","^K","^4","^5","^6","^5"]] -------------------------------------------------------------------------------- /src/clj_kondo/impl/analyzer/core_async.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.impl.analyzer.core-async 2 | {:no-doc true} 3 | (:require 4 | [clj-kondo.impl.utils :refer [tag]])) 5 | 6 | (defn analyze-alt-val [ctx analyze-expression** extract-bindings expr] 7 | (let [[fst-child & rest-children] (:children expr)] 8 | (if (and 9 | fst-child 10 | (= :list (tag expr)) 11 | (= :vector (tag fst-child))) 12 | ;; analyze syntax like: ([v ch] 'do_something_with_v_or_ch) 13 | ;; NOTE: this syntax also supports destructuring: 14 | ;; (async/alt!! (doto (async/chan) (async/put! {:a 1})) ([{:keys [:a]} ch] [a ch])) 15 | (let [bindings (extract-bindings ctx fst-child) 16 | ctx (update ctx :bindings into bindings)] 17 | (mapcat #(analyze-expression** ctx %) rest-children)) 18 | (analyze-expression** ctx expr)))) 19 | 20 | (defn analyze-alt! [ctx expr] 21 | (let [{:keys [:analyze-expression** 22 | :extract-bindings]} ctx 23 | children (next (:children expr)) 24 | pairs (partition-all 2 children)] 25 | (for [[k v] pairs 26 | analyzed-value (concat (analyze-expression** ctx k) 27 | (analyze-alt-val ctx analyze-expression** 28 | extract-bindings v))] 29 | analyzed-value))) 30 | 31 | (comment 32 | ) 33 | -------------------------------------------------------------------------------- /test/clj_kondo/missing_body_in_when_test.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.missing-body-in-when-test 2 | (:require 3 | [clj-kondo.test-utils :refer [lint! assert-submaps]] 4 | [clojure.test :as t :refer [deftest is testing]])) 5 | 6 | (deftest missing-body-in-when-error-test 7 | (testing "test linting error of missing body in when for clojure" 8 | (assert-submaps 9 | '({:file "", :row 1, :col 1, :level :warning, 10 | :message "Missing body in when"}) 11 | (lint! "(when (> 1 0))"))) 12 | (testing "test linting error of missing body in when for cljs" 13 | (assert-submaps 14 | '({:file "", :row 1, :col 1, :level :warning, 15 | :message "Missing body in when"}) 16 | (lint! "(when true)" "--lang" "cljs"))) 17 | (testing "test linting error of missing body in nested when" 18 | (assert-submaps 19 | '({:file "", :row 1, :col 15, :level :warning, 20 | :message "Missing body in when"}) 21 | (lint! "(when (> 1 0) (when (= 1 2)))")))) 22 | 23 | (deftest missing-body-in-when-valid-test 24 | (testing "test linting when with condition and body" 25 | (is (empty? (lint! "(when (> 1 0) (prn 1))")))) 26 | (testing "test linting when with multiple expresion in body" 27 | (is (empty? (lint! "(when true (prn 1) (+ 1 1))")))) 28 | (testing "test linting nested when" 29 | (is (empty? (lint! "(when (> 1 0) (when (prn 1) (+ 1 1)))"))))) 30 | -------------------------------------------------------------------------------- /test/clj_kondo/jdbc_test.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.jdbc-test 2 | (:require 3 | [clj-kondo.test-utils :refer [assert-submaps lint!]] 4 | [clojure.java.io :as io] 5 | [clojure.test :refer [deftest is]] 6 | [missing.test.assertions])) 7 | 8 | (deftest next-jdbc-test 9 | (is (empty? (lint! (io/file "corpus" "jdbc" "next_test.clj") 10 | {:linters {:unresolved-symbol {:level :error}}})))) 11 | 12 | (deftest clojure-java-jdbc-test 13 | (is (empty? (lint! (io/file "corpus" "jdbc" "cjj_test.clj") 14 | {:linters {:unresolved-symbol {:level :error}}})))) 15 | 16 | (deftest detected-issues-test 17 | (assert-submaps '({:file "corpus/jdbc/next.clj", :row 9, :col 24, :level :error, 18 | :message "with-transaction binding form requires exactly 2 or 3 forms"} 19 | {:file "corpus/jdbc/next.clj", :row 12, :col 24, :level :error, 20 | :message "with-transaction binding form requires exactly 2 or 3 forms"} 21 | {:file "corpus/jdbc/next.clj", :row 16, :col 3, :level :error, 22 | :message "with-transaction requires a vector for its binding"} 23 | {:file "corpus/jdbc/next.clj", :row 18, :col 25, :level :error, 24 | :message "with-transaction binding form requires a symbol"}) 25 | (lint! (io/file "corpus" "jdbc" "next.clj")))) 26 | -------------------------------------------------------------------------------- /parser/clj_kondo/impl/rewrite_clj/parser.clj: -------------------------------------------------------------------------------- 1 | (ns ^{:no-doc true} clj-kondo.impl.rewrite-clj.parser 2 | (:require [clj-kondo.impl.rewrite-clj.node :as node] 3 | [clj-kondo.impl.rewrite-clj.parser.core :as p] 4 | [clj-kondo.impl.rewrite-clj.reader :as reader])) 5 | 6 | ;; ## Parser Core 7 | 8 | (defn parse 9 | "Parse next form from the given reader." 10 | [reader] 11 | (p/parse-next reader)) 12 | 13 | (defn parse-all 14 | "Parse all forms from the given reader." 15 | [reader] 16 | (let [nodes (->> (repeatedly #(parse reader)) 17 | (take-while identity) 18 | (doall))] 19 | (with-meta 20 | (node/forms-node nodes) 21 | (meta (first nodes))))) 22 | 23 | ;; ## Specialized Parsers 24 | 25 | (defn parse-string 26 | "Parse first form in the given string." 27 | [s] 28 | (parse (reader/string-reader s))) 29 | 30 | (defn parse-string-all 31 | "Parse all forms in the given string." 32 | [s] 33 | (parse-all (reader/string-reader s))) 34 | 35 | (defn parse-file 36 | "Parse first form from the given file." 37 | [f] 38 | (let [r (reader/file-reader f)] 39 | (with-open [_ ^java.io.Closeable (.-rdr r)] 40 | (parse r)))) 41 | 42 | (defn parse-file-all 43 | "Parse all forms from the given file." 44 | [f] 45 | (let [r (reader/file-reader f)] 46 | (with-open [_ ^java.io.Closeable (.-rdr r)] 47 | (parse-all r)))) 48 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/cljs/cljs.spec.gen.alpha.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$gen-for-pred",["^ ","~:row",144,"~:col",1,"~:fixed-arities",["~#set",[1]],"~:name","^0","~:ns","~$cljs.spec.gen.alpha","~:top-ns","^7","~:type","~:fn"],"~$qualified?",["^ ","^1",85,"^2",1,"~:private",true,"^3",["^4",[1]],"^5","^;","^6","^7","^8","^7","~:arities",["^ ","~i1",["^ ","~:ret","~:boolean"]],"^9","^:"],"~$cat",["^ ","^1",78,"^2",1,"~:varargs-min-arity",0,"^5","^@","^6","^7","^8","^7","^9","^:"],"~$LazyVar",["^ ","^1",17,"^2",1,"^5","^B","^6","^7","^8","^7"],"~$generator?",["^ ","^1",45,"^2",3,"^<",true,"^3",["^4",[1]],"^5","^C","^6","^7","^8","^7","^9","^:"],"~$for-all*-ref",["^ ","^1",34,"^2",1,"^<",true,"^5","^D","^6","^7","^8","^7"],"~$->LazyVar",["^ ","^1",17,"^2",1,"^3",["^4",[2]],"^5","^E","^6","^7","^8","^7"],"~$quick-check",["^ ","^1",30,"^2",1,"^A",0,"^5","^F","^6","^7","^8","^7","^9","^:"],"~$for-all*",["^ ","^1",37,"^2",1,"^A",0,"^5","^G","^6","^7","^8","^7","^9","^:"],"~:filename","cljs/spec/gen/alpha.cljs","~$generate",["^ ","^1",51,"^2",3,"^3",["^4",[1]],"^5","^I","^6","^7","^8","^7","^9","^:"],"~$delay-impl",["^ ","^1",56,"^2",1,"^3",["^4",[1]],"^5","^J","^6","^7","^8","^7","^9","^:"],"~$gen-builtins",["^ ","^1",87,"^2",1,"^<",true,"^5","^K","^6","^7","^8","^7"],"~$quick-check-ref",["^ ","^1",27,"^2",1,"^<",true,"^5","^L","^6","^7","^8","^7"],"~$generator",["^ ","^1",48,"^2",3,"^<",true,"^3",["^4",[1]],"^5","^M","^6","^7","^8","^7","^9","^:"]] -------------------------------------------------------------------------------- /test/clj_kondo/impl/config_test.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.impl.config-test 2 | (:require [clj-kondo.impl.config :refer [merge-config!]] 3 | [clojure.test :refer [deftest is testing]])) 4 | 5 | (deftest merge-replace-config-test 6 | (testing "^:replace top-level value" 7 | (is (= {:linters {:b 2} 8 | :lint-as {'b 'y}} 9 | (merge-config! {:linters {:a 1} 10 | :lint-as {'a 'x}} 11 | ^:replace {:linters {:b 2} 12 | :lint-as {'b 'y}})))) 13 | 14 | (testing "^:replace merging supports nested values" 15 | (is (= {:linters {:b 2} 16 | :lint-as {'a 'x 'b 'y}} 17 | (merge-config! {:linters {:a 1} 18 | :lint-as {'a 'x}} 19 | {:linters ^:replace {:b 2} 20 | :lint-as {'b 'y}}))))) 21 | 22 | (deftest merge-set-and-vecs-test 23 | (-> (merge-config! '{:linters {:unresolved-namespace {:exclude #{foo bar}}}} 24 | '{:linters {:unresolved-namespace {:exclude [baz]}}}) 25 | :linters :unresolved-namespace :exclude 26 | (= '#{foo bar baz}) 27 | is) 28 | (-> (merge-config! '{:linters {:unresolved-namespace {:exclude [foo bar]}}} 29 | '{:linters {:unresolved-namespace {:exclude #{baz}}}}) 30 | :linters :unresolved-namespace :exclude 31 | (= '[foo bar baz]) 32 | is)) 33 | -------------------------------------------------------------------------------- /project.clj.template: -------------------------------------------------------------------------------- 1 | (defproject clj-kondo "{{version}}" 2 | :description "A linter for Clojure that sparks joy." 3 | :url "https://github.com/clj-kondo/clj-kondo" 4 | :scm {:name "git" 5 | :url "https://github.com/clj-kondo/clj-kondo"} 6 | :license {:name "Eclipse Public License 1.0" 7 | :url "http://opensource.org/licenses/eclipse-1.0.php"} 8 | :source-paths ["src" "parser" "inlined"] 9 | :dependencies {{dependencies|safe}} 10 | ;; :global-vars {*print-namespace-maps* false} 11 | :profiles {:clojure-1.9.0 {:dependencies [[org.clojure/clojure "1.9.0"]]} 12 | :clojure-1.10.2 {:dependencies [[org.clojure/clojure "1.10.2"]]} 13 | :test {:dependencies {{test-dependencies|safe}} 14 | :source-paths ["src" "parser" "inlined" "extract"]} 15 | :uberjar {:dependencies [[com.github.clj-easy/graal-build-time "0.1.0"]] 16 | :jvm-opts ["-Dclojure.compiler.direct-linking=true" 17 | "-Dclojure.spec.skip-macros=true"] 18 | :main clj-kondo.main 19 | :aot [clj-kondo.main]}} 20 | :aliases {"clj-kondo" ["run" "-m" "clj-kondo.main"]} 21 | :deploy-repositories [["clojars" {:url "https://clojars.org/repo" 22 | :username :env/clojars_user 23 | :password :env/clojars_pass 24 | :sign-releases false}]]) 25 | -------------------------------------------------------------------------------- /test/clj_kondo/impl/macroexpand_test.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.impl.macroexpand-test 2 | (:require 3 | [clj-kondo.impl.macroexpand :as macroexpand] 4 | [clj-kondo.impl.utils :refer [parse-string tag sexpr]] 5 | [clojure.test :as t :refer [deftest is testing]])) 6 | 7 | (defn location [node] 8 | (let [m (meta node)] 9 | (when (and (:row m) (:col m)) 10 | m))) 11 | 12 | (deftest expand->-test 13 | (testing "Expanded -> expression preserves location" 14 | (is (every? location 15 | (filter #(= :list (tag %)) 16 | (tree-seq :children :children 17 | (macroexpand/expand-> {} 18 | (parse-string "(-> 1 inc inc)"))))))) 19 | (testing "with metadata" 20 | (is (= '(clojure.string/includes? (str "foo") "foo") 21 | (sexpr 22 | (macroexpand/expand-> {} 23 | (parse-string "(-> \"foo\" ^String str (clojure.string/includes? \"foo\"))"))))))) 24 | 25 | (deftest expand-fn-test 26 | (testing 27 | "Expanded function literals have a location for the function they call" 28 | (let [fn-body (macroexpand/expand-fn 29 | (parse-string "#(valid? %)"))] 30 | (is 31 | (every? location 32 | (filter #(= :list (tag %)) 33 | (tree-seq :children :children 34 | fn-body))))))) 35 | 36 | 37 | ;;;; Scratch 38 | 39 | (comment 40 | ) 41 | -------------------------------------------------------------------------------- /test/clj_kondo/impl/extract_var_info_test.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.impl.extract-var-info-test 2 | (:require 3 | [clj-kondo.impl.extract-var-info :as extract-var-info] 4 | [clojure.test :refer [deftest is testing]])) 5 | 6 | (deftest eastwood-var-info-test 7 | (let [var-info (extract-var-info/eastwood-var-info)] 8 | (is (contains? var-info 'clojure.core/*out*)))) 9 | 10 | (deftest extract-clojure-core-vars-test 11 | (let [vars (extract-var-info/extract-clojure-core-vars)] 12 | (is (contains? vars 'future)) 13 | (is (contains? vars 'transduce)) 14 | (testing "clojure 1.11.0-alpha2" 15 | (is (contains? vars 'update-vals)) 16 | (is (contains? vars 'update-keys))))) 17 | 18 | (deftest extract-cljs-core-vars-test 19 | (let [vars (extract-var-info/extract-cljs-core-vars)] 20 | (is (contains? vars 'clj->js)) 21 | (is (contains? vars 'transduce)) 22 | (is (contains? vars 'eval)) 23 | (is (contains? vars '*target*)))) 24 | 25 | (deftest default-java-imports-test 26 | (let [java-imports (extract-var-info/extract-default-imports)] 27 | (is (contains? java-imports 'Class)) 28 | (is (contains? java-imports 'Object)) 29 | (is (contains? java-imports 'String)) 30 | (is (contains? java-imports 'Exception)))) 31 | 32 | ;;;; Scratch 33 | 34 | (comment 35 | (def eastwood (extract-var-info/eastwood-var-info)) 36 | (get eastwood 'clojure.core/*out*) 37 | (def vars (extract-var-info/extract-clojure-core-vars)) 38 | vars 39 | ) 40 | -------------------------------------------------------------------------------- /test/clj_kondo/unused_alias_test.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.unused-alias-test 2 | (:require 3 | [clj-kondo.test-utils :refer [lint! assert-submaps2]] 4 | [clojure.test :refer [deftest testing is]])) 5 | 6 | (def conf {:linters {:unused-alias {:level :warning} 7 | :unused-namespace {:level :off}}}) 8 | 9 | (deftest unused-alias-test 10 | (assert-submaps2 11 | '({:file "", :row 1, :col 34, :level :warning, :message "Unused alias: b"} 12 | {:file "", 13 | :row 2, 14 | :col 39, 15 | :level :warning, 16 | :message "Unused alias: bz"}) 17 | (lint! "(ns foo (:require [bar :as-alias b] 18 | [baz :as bz]))" conf)) 19 | (assert-submaps2 20 | '({:file "", :row 5, :col 31, :level :warning, :message "Unused alias: u"}) 21 | (lint! " 22 | (ns foo (:require [bar :as-alias b] 23 | [baz :as bz] 24 | [quuz :as q] 25 | [unused :as u])) 26 | `b/dude 27 | ::bz/dude 28 | q/dude" conf)) 29 | (testing "using alias as object in CLJS" 30 | (is (empty? 31 | (lint! " 32 | (ns foo (:require [\"dayjs\" :as dayjs])) dayjs" conf 33 | "--lang" "cljs")))) 34 | (testing "namespaced map" 35 | (is (empty? 36 | (lint! "(ns foo (:require [bar :as-alias b])) #::b{:baz 1}" conf)))) 37 | (testing "namespaced keyword" 38 | (is (empty? 39 | (lint! "(ns foo (:require [bar :as-alias b])) ::b/foo" conf))))) 40 | -------------------------------------------------------------------------------- /.cirrus.yml: -------------------------------------------------------------------------------- 1 | macos_instance: 2 | image: ghcr.io/cirruslabs/macos-monterey-base:latest 3 | 4 | task: 5 | env: 6 | LEIN_ROOT: "true" 7 | GRAALVM_VERSION: "22.3.0" 8 | GRAALVM_HOME: ${HOME}/graalvm-ce-java11-22.3.0/Contents/Home 9 | CLJ_KONDO_PLATFORM: macos # used in release script 10 | CLJ_KONDO_ARCH: aarch64 11 | CLJ_KONDO_TEST_ENV: native 12 | GITHUB_TOKEN: ENCRYPTED[a616fae871722a21c7dfbabdf4c226f4601dc31dab23d594c7a448760a0af4b195f252d3528f688c7899a3efa672a101] 13 | script: | 14 | sudo .circleci/script/install-clojure /usr/local 15 | sudo bash < <(curl -s https://raw.githubusercontent.com/babashka/babashka/master/install) 16 | sudo .circleci/script/install-leiningen 17 | 18 | pushd ~ 19 | if ! [ -d graalvm-ce-java11-22.3.0 ]; then 20 | curl -O -sL https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-22.3.0/graalvm-ce-java11-darwin-aarch64-22.3.0.tar.gz 21 | tar xzf graalvm-ce-java11-darwin-aarch64-22.3.0.tar.gz 22 | fi 23 | popd 24 | 25 | export PATH=$GRAALVM_HOME/bin:$PATH 26 | export JAVA_HOME=$GRAALVM_HOME 27 | 28 | java -version 29 | 30 | script/compile 31 | script/test 32 | 33 | VERSION=$(cat resources/CLJ_KONDO_VERSION) 34 | arch=${CLJ_KONDO_ARCH:-amd64} 35 | archive="clj-kondo-$VERSION-$CLJ_KONDO_PLATFORM-$arch.zip" 36 | zip "$archive" clj-kondo 37 | bb release-artifact "$archive" || true 38 | 39 | binaries_artifacts: 40 | path: "clj-kondo-*.zip" 41 | -------------------------------------------------------------------------------- /test/clj_kondo/analysis/edn_test.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.analysis.edn-test 2 | (:require 3 | [clj-kondo.core :as clj-kondo] 4 | [clj-kondo.impl.utils :refer [err]] 5 | [clj-kondo.test-utils :refer [assert-submap2]] 6 | [clojure.java.io :as io] 7 | [clojure.test :as t :refer [deftest is testing]])) 8 | 9 | (defn analyze 10 | ([paths] (analyze paths nil)) 11 | ([paths config] 12 | (clj-kondo/run! (merge 13 | {:lint paths 14 | :config {:output {:canonical-paths true} 15 | :analysis {:keywords true :symbols true}}} 16 | config)))) 17 | 18 | (deftest edn-keyword-analysis-test 19 | (let [file (io/file "corpus" "edn" "analysis.edn") 20 | {:keys [analysis findings]} (analyze [file] {})] 21 | (is (empty? findings)) 22 | (assert-submap2 23 | {:keywords 24 | [{:name "a" :row 1 :col 2 :end-row 1 :end-col 4 :filename (.getCanonicalPath file)} 25 | {:name "b" :row 1 :col 7 :end-row 1 :end-col 9 :filename (.getCanonicalPath file)} 26 | {:name "foo" :row 1 :col 10 :end-row 1 :end-col 14 :filename (.getCanonicalPath file)} 27 | {:name "c" :row 1 :col 15 :end-row 1 :end-col 17 :filename (.getCanonicalPath file)} 28 | {:name "exec-fn" :row 2 :col 2}]} 29 | analysis) 30 | (assert-submap2 31 | {:symbols 32 | [{:symbol 'clojure.core/inc :row 2 :col 11 :end-row 2 :end-col 27 :filename (.getCanonicalPath file)}]} 33 | analysis))) 34 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/clj/clojure.xml.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$tag",["^ ","~:row",22,"~:col",1,"~:name","^0","~:ns","~$clojure.xml","~:top-ns","^5"],"~$sax-parser",["^ ","^1",75,"^2",1,"~:fixed-arities",["~#set",[0]],"^3","^7","^4","^5","^6","^5","~:type","~:fn"],"~$*sb*",["^ ","^1",18,"^2",1,"^3","^<","^4","^5","^6","^5"],"~$*state*",["^ ","^1",17,"^2",1,"^3","^=","^4","^5","^6","^5"],"~$*stack*",["^ ","^1",15,"^2",1,"^3","^>","^4","^5","^6","^5"],"~$*current*",["^ ","^1",16,"^2",1,"^3","^?","^4","^5","^6","^5"],"~$content",["^ ","^1",24,"^2",1,"^3","^@","^4","^5","^6","^5"],"~$content-handler",["^ ","^1",26,"^2",1,"^3","^A","^4","^5","^6","^5","^:",["^ ","~:call",["^ ","~:filename","clojure/xml.clj","^:","^B","~:lang","~:clj","~:base-lang","^E","~:resolved-ns","~$clojure.core","^4","^5","^3","~$new","~:arity",2],"^1",33,"^2",5,"~:end-row",73,"~:end-col",14]],"^C","clojure/xml.clj","~$disable-external-entities",["^ ","^1",81,"^2",1,"^8",["^9",[1]],"^3","^M","^4","^5","^6","^5","^:","^;"],"~$parse",["^ ","^1",106,"^2",1,"^8",["^9",[1,2]],"^3","^N","^4","^5","^6","^5","^:","^;"],"~$startparse-sax",["^ ","^1",92,"^2",1,"^8",["^9",[2]],"^3","^O","^4","^5","^6","^5","^:","^;"],"~$emit",["^ ","^1",143,"^2",1,"^8",["^9",[1]],"^3","^P","^4","^5","^6","^5","^:","^;"],"~$startparse-sax-safe",["^ ","^1",99,"^2",1,"^8",["^9",[2]],"^3","^Q","^4","^5","^6","^5","^:","^;"],"~$attrs",["^ ","^1",23,"^2",1,"^3","^R","^4","^5","^6","^5"],"~$emit-element",["^ ","^1",127,"^2",1,"^8",["^9",[1]],"^3","^S","^4","^5","^6","^5","^:","^;"]] -------------------------------------------------------------------------------- /test/clj_kondo/keyword_binding_test.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.keyword-binding-test 2 | (:require 3 | [clj-kondo.test-utils :refer [lint! assert-submaps2]] 4 | [clojure.test :as t :refer [deftest is testing]])) 5 | 6 | (def example-with-keyword-bindings 7 | "(ns test 8 | {:clj-kondo/config 9 | '{:linters {:keyword-binding {:level :warning}}}}) 10 | (let [{:keys [:a :b]} {:a 1 :b 2}] (println a b))") 11 | 12 | (deftest multiple-keywords-test 13 | (assert-submaps2 14 | [{:file "", :row 4, :col 18, :level :warning, :message "Keyword binding should be a symbol: :a"} 15 | {:file "", :row 4, :col 21, :level :warning, :message "Keyword binding should be a symbol: :b"}] 16 | 17 | (lint! example-with-keyword-bindings))) 18 | 19 | (def mixed-example 20 | "(ns test 21 | {:clj-kondo/config 22 | '{:linters {:keyword-binding {:level :warning}}}}) 23 | (let [{:keys [:a b]} {:a 1 :b 2}] (println a b))") 24 | 25 | (deftest mixed-keyword-and-symbols-test 26 | (assert-submaps2 27 | [{:file "", :row 4, :col 18, :level :warning, :message "Keyword binding should be a symbol: :a"}] 28 | (lint! mixed-example))) 29 | 30 | (deftest namespaced-keyword-test 31 | (testing "keywords with namespaced are ignored" 32 | (assert-submaps2 33 | [{:file "", :row 1, :col 24, :level :warning, :message "Keyword binding should be a symbol: :baz"}] 34 | (lint! "(let [{:keys [:foo/bar :baz ::baz]} {}] bar)" 35 | {:linters {:keyword-binding {:level :warning}}})))) 36 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/cljs/clojure.browser.event.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~$expose",["^ ","~:row",81,"~:col",1,"~:fixed-arities",["~#set",[1]],"~:name","^0","~:ns","~$clojure.browser.event","~:top-ns","^7","~:type","~:fn"],"~$listen-once",["^ ","^1",55,"^2",1,"^3",["^4",[4,3]],"^5","^;","^6","^7","^8","^7","^9","^:"],"~$unlisten",["^ ","^1",64,"^2",1,"^3",["^4",[4,3]],"^5","^<","^6","^7","^8","^7","^9","^:"],"~$has-listener",["^ ","^1",96,"^2",1,"^3",["^4",[3]],"^5","^=","^6","^7","^8","^7","^9","^:"],"~$all-listeners",["^ ","^1",92,"^2",1,"^3",["^4",[3]],"^5","^>","^6","^7","^8","^7","^9","^:"],"~$get-listener",["^ ","^1",91,"^2",1,"^3",["^4",[5]],"^5","^?","^6","^7","^8","^7","^9","^:"],"~$unique-event-id",["^ ","^1",94,"^2",1,"^3",["^4",[1]],"^5","^@","^6","^7","^8","^7","^9","^:"],"~:filename","clojure/browser/event.cljs","~$fire-listeners",["^ ","^1",84,"^2",1,"^3",["^4",[4]],"^5","^B","^6","^7","^8","^7","^9","^:"],"~$event-types",["^ ","^1",17,"^2",3,"^3",["^4",[1]],"^5","^C","^6","^7","^8","^7"],"~$total-listener-count",["^ ","^1",87,"^2",1,"^3",["^4",[0]],"^5","^D","^6","^7","^8","^7","^9","^:"],"~$IEventType",["^ ","^1",16,"^2",1,"^5","^E","^6","^7","^8","^7"],"~$listen",["^ ","^1",46,"^2",1,"^3",["^4",[4,3]],"^5","^F","^6","^7","^8","^7","^9","^:"],"~$unlisten-by-key",["^ ","^1",73,"^2",1,"^3",["^4",[1]],"^5","^G","^6","^7","^8","^7","^9","^:"],"~$remove-all",["^ ","^1",100,"^2",1,"^3",["^4",[3]],"^5","^H","^6","^7","^8","^7","^9","^:"],"~$dispatch-event",["^ ","^1",77,"^2",1,"^3",["^4",[2]],"^5","^I","^6","^7","^8","^7","^9","^:"]] -------------------------------------------------------------------------------- /script/clj_kondo/release_artifact.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.release-artifact 2 | (:require [borkdude.gh-release-artifact :as ghr] 3 | [clojure.java.shell :refer [sh]] 4 | [clojure.string :as str])) 5 | 6 | (defn current-branch [] 7 | (or (System/getenv "APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH") 8 | (System/getenv "APPVEYOR_REPO_BRANCH") 9 | (System/getenv "CIRCLE_BRANCH") 10 | (-> (sh "git" "rev-parse" "--abbrev-ref" "HEAD") 11 | :out 12 | str/trim))) 13 | 14 | (defn release [& args] 15 | (let [current-version (-> (slurp "resources/CLJ_KONDO_VERSION") 16 | str/trim) 17 | ght (System/getenv "GITHUB_TOKEN") 18 | file (first args) 19 | branch (current-branch) 20 | release-branch? (contains? #{"master" "main"} branch)] 21 | (when-not ght 22 | (println "Skipping: not GITHUB_TOKEN")) 23 | (when-not release-branch? 24 | (println "Skipping: not on release branch")) 25 | (if (and ght release-branch?) 26 | (do (assert file "File name must be provided") 27 | (ghr/overwrite-asset {:org "clj-kondo" 28 | :repo "clj-kondo" 29 | :file file 30 | :tag (str "v" current-version) 31 | :overwrite (str/ends-with? current-version "SNAPSHOT") 32 | :sha256 true})) 33 | (println "Skipping release artifact (no GITHUB_TOKEN or not on main branch)")) 34 | nil)) 35 | -------------------------------------------------------------------------------- /src/clj_kondo/impl/linters/edn_utils.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.impl.linters.edn-utils 2 | {:no-doc true} 3 | (:require [clj-kondo.impl.utils :refer [sexpr]])) 4 | 5 | (set! *warn-on-reflection* true) 6 | 7 | (defn sexpr-keys [map-node] 8 | (let [children (:children map-node) 9 | keys (take-nth 2 children) 10 | keys (map sexpr keys) 11 | vals (take-nth 2 (rest children))] 12 | (zipmap keys vals))) 13 | 14 | (defn key-nodes [map-node] 15 | (if (identical? :namespaced-map (:tag map-node)) 16 | (let [nspace-k (-> map-node :ns :k) 17 | map-node (first (:children map-node)) 18 | knodes (take-nth 2 (:children map-node))] 19 | (map #(assoc % :namespace nspace-k) knodes)) 20 | (take-nth 2 (:children map-node)))) 21 | 22 | (defn val-nodes [map-node] 23 | (if (identical? :namespaced-map (:tag map-node)) 24 | (let [map-node (first (:children map-node)) 25 | vnodes (take-nth 2 (rest (:children map-node)))] 26 | vnodes) 27 | (take-nth 2 (rest (:children map-node))))) 28 | 29 | (defn name-for-type [form] 30 | (cond 31 | (nil? form) "nil" 32 | (symbol? form) "symbol" 33 | (int? form) "int" 34 | (float? form) "float" 35 | (keyword? form) "keyword" 36 | (char? form) "char" 37 | (string? form) "string" 38 | (map? form) "map" 39 | (vector? form) "vector" 40 | (list? form) "list" 41 | (set? form) "set" 42 | :else (.getName (class form)))) 43 | 44 | (defn node-map [raw-node] 45 | (zipmap (-> raw-node key-nodes) 46 | (-> raw-node val-nodes))) 47 | -------------------------------------------------------------------------------- /test/clj_kondo/clojure_data_xml_test.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.clojure-data-xml-test 2 | (:require 3 | [babashka.fs :as fs] 4 | [clj-kondo.core :as clj-kondo] 5 | [clj-kondo.test-utils :refer [lint! #_assert-submaps]] 6 | [clojure.test :refer [deftest is testing]] 7 | [clojure.tools.deps.alpha :as deps])) 8 | 9 | (deftest alias-uri-test 10 | (is (empty? (lint! " 11 | (ns foo 12 | (:require [clojure.data.xml :refer [alias-uri]])) 13 | 14 | (alias-uri 'xh \"http://xml.weather.yahoo.com/ns/rss/1.0\") 15 | 16 | ::xh/location 17 | 18 | (alias-uri 19 | :U \"uri-u:\" 20 | :D \"DAV:\" 21 | 'V \"uri-v:\" 22 | \"W\" \"uri-w:\") 23 | 24 | ::U/foo 25 | ::D/foo 26 | ::V/foo 27 | ::W/foo 28 | 29 | ")))) 30 | 31 | 32 | (defn lint!! [cache-dir s] 33 | (with-in-str s 34 | (-> (clj-kondo/run! {:lint ["-"] 35 | :cache cache-dir}) 36 | :findings))) 37 | 38 | (deftest unresolved-var-test 39 | (let [cache (str (fs/create-temp-dir)) 40 | deps '{:deps {;; org.clojure/clojure {:mvn/version "1.9.0"} 41 | org.clojure/data.xml {:mvn/version "0.2.0-alpha6"}} 42 | :mvn/repos {"central" {:url "https://repo1.maven.org/maven2/"} 43 | "clojars" {:url "https://repo.clojars.org/"}}} 44 | jar (-> (deps/resolve-deps deps nil) 45 | (get-in ['org.clojure/data.xml :paths 0]))] 46 | (clj-kondo/run! {:lint [jar] :cache-dir cache}) 47 | (is (empty? (lint!! cache " 48 | (ns foo 49 | (:require [clojure.data.xml :refer [element]])) 50 | element"))))) 51 | -------------------------------------------------------------------------------- /test/clj_kondo/impl/copy_config_test.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.impl.copy-config-test 2 | (:require [clj-kondo.core :as clj-kondo] 3 | [clojure.java.io :as io] 4 | [clojure.test :as t :refer [deftest is testing]]) 5 | (:import [java.nio.file Files])) 6 | 7 | (deftest copy-config-from-jar 8 | (let [tmp-dir (.toFile (Files/createTempDirectory 9 | "config" 10 | (into-array java.nio.file.attribute.FileAttribute [])))] 11 | (binding [*err* (java.io.StringWriter.)] 12 | (clj-kondo/run! {:lint [(io/file "corpus" "exports" "clj-kondo.config.jar")] 13 | :config-dir tmp-dir 14 | :copy-configs true})) 15 | (is (.exists (io/file tmp-dir "clj-kondo" "slingshot"))) 16 | (is (= "{:hooks 17 | {:analyze-call {slingshot.slingshot/try+ clj-kondo.slingshot.try-plus/try+}}} 18 | " (slurp (io/file tmp-dir "clj-kondo" "slingshot" "config.edn")))))) 19 | 20 | (deftest copy-config-from-dir 21 | (let [tmp-dir (.toFile (Files/createTempDirectory 22 | "config" 23 | (into-array java.nio.file.attribute.FileAttribute [])))] 24 | (binding [*err* (java.io.StringWriter.)] 25 | (clj-kondo/run! {:lint [(io/file "corpus" "exports" "dir")] 26 | :config-dir tmp-dir 27 | :copy-configs true})) 28 | (is (.exists (io/file tmp-dir "clj-kondo" "slingshot"))) 29 | (is (= "{:hooks 30 | {:analyze-call {slingshot.slingshot/try+ clj-kondo.slingshot.try-plus/try+}}} 31 | " (slurp (io/file tmp-dir "clj-kondo" "slingshot" "config.edn")))))) 32 | -------------------------------------------------------------------------------- /resources/clj_kondo/impl/cache/built_in/cljc/cljs.tagged-literals.transit.json: -------------------------------------------------------------------------------- 1 | ["^ ","~:filename","cljs/tagged_literals.cljc","~:clj",["^ ","~$read-queue",["^ ","~:row",13,"~:col",1,"~:fixed-arities",["~#set",[1]],"~:name","^2","~:ns","~$cljs.tagged-literals","~:top-ns","^9","~:arities",["^ ","~i1",["^ ","~:ret","~:list"]],"~:type","~:fn"],"~$read-uuid",["^ ","^3",24,"^4",4,"^5",["^6",[1]],"^7","^@","^8","^9","^:","^9","^>","^?"],"~$read-inst",["^ ","^3",44,"^4",4,"^5",["^6",[1]],"^7","^A","^8","^9","^:","^9","^>","^?"],"~$valid-js-literal-key?",["^ ","^3",63,"^4",1,"^5",["^6",[1]],"^7","^B","^8","^9","^:","^9","^;",["^ ","~i1",["^ ","^<",["^6",["~:nil","~:boolean"]]]],"^>","^?"],"~$JSValue",["^ ","^3",68,"^4",1,"^7","^E","^8","^9","^:","^9"],"~$->JSValue",["^ ","^3",68,"^4",1,"^5",["^6",[1]],"^7","^F","^8","^9","^:","^9"],"~$read-js",["^ ","^3",70,"^4",1,"^5",["^6",[1]],"^7","^G","^8","^9","^:","^9","^>","^?"],"~$*cljs-data-readers*",["^ ","^3",87,"^4",1,"^7","^H","^8","^9","^:","^9"]],"~:cljs",["^ ","^2",["^ ","^3",13,"^4",1,"^5",["^6",[1]],"^7","^2","^8","^9","^:","^9","^;",["^ ","~i1",["^ ","^<","^="]],"^>","^?"],"^@",["^ ","^3",34,"^4",4,"^5",["^6",[1]],"^7","^@","^8","^9","^:","^9","^>","^?"],"^A",["^ ","^3",54,"^4",4,"^5",["^6",[1]],"^7","^A","^8","^9","^:","^9","^>","^?"],"^B",["^ ","^3",63,"^4",1,"^5",["^6",[1]],"^7","^B","^8","^9","^:","^9","^;",["^ ","~i1",["^ ","^<",["^6",["^C","^D"]]]],"^>","^?"],"^E",["^ ","^3",68,"^4",1,"^7","^E","^8","^9","^:","^9"],"^F",["^ ","^3",68,"^4",1,"^5",["^6",[1]],"^7","^F","^8","^9","^:","^9"],"^G",["^ ","^3",70,"^4",1,"^5",["^6",[1]],"^7","^G","^8","^9","^:","^9","^>","^?"],"^H",["^ ","^3",87,"^4",1,"^7","^H","^8","^9","^:","^9"]]] -------------------------------------------------------------------------------- /.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 | # End of https://www.gitignore.io/api/emacs,clojure 65 | /clj-kondo 66 | **/.clj-kondo/.cache 67 | performance.txt 68 | clj-kondo*.snap 69 | .planck_cache 70 | 71 | # IntelliJ 72 | .idea 73 | *.iml 74 | 75 | .DS_Store 76 | !corpus/**/*.jar 77 | !corpus/java/classes/foo/bar/AwesomeClass.class 78 | corpus/config_dir/.cache 79 | /.clj-kondo/clj-kondo 80 | /.lsp/.cache 81 | clj-kondo.build_artifacts.txt 82 | /.clj-kondo/rewrite-clj 83 | .clj-kondo/marick 84 | corpus/namespace_config/.clj-kondo/inline-configs 85 | .lock 86 | corpus/.clj-kondo/inline-configs 87 | tmp 88 | .eastwood 89 | 90 | # Calva 91 | .calva 92 | corpus/issue-2223/.clj-kondo 93 | corpus/issue-2239/.clj-kondo/.cache 94 | src/scratch.clj 95 | -------------------------------------------------------------------------------- /analysis/src/clj_kondo/tools/pprint.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.tools.pprint 2 | (:require 3 | [clj-kondo.core :as clj-kondo] 4 | [clojure.pprint :as p])) 5 | 6 | (defn- private-fixed-arity [_x _y _z]) 7 | 8 | (defmacro macro-varargs-arity [_x & _xs]) 9 | 10 | (defn -main [& [format & paths]] 11 | (if (empty? paths) 12 | (println "usage: <(edn | table)> <(true | false)> +") 13 | (let [format (case format "edn" :edn "table" :table) 14 | analysis (:analysis (clj-kondo/run! 15 | {:lint paths :config 16 | {:analysis true}})) 17 | {:keys [:namespace-definitions 18 | :namespace-usages 19 | :var-definitions 20 | :var-usages]} analysis] 21 | (case format 22 | :table 23 | (do 24 | (print ":namespace-definitions") 25 | (p/print-table [:filename :row :col :name :doc :author :added :deprecated :no-doc] 26 | namespace-definitions) 27 | (println) 28 | (print ":namespace-usages") 29 | (p/print-table [:filename :row :col :from :to] 30 | namespace-usages) 31 | (println) 32 | (print ":var-definitions") 33 | (p/print-table [:filename :row :col :ns :name :doc :fixed-arities :varargs-min-arity :private :macro :added :deprecated] 34 | var-definitions) 35 | (println) 36 | (print ":var-usages") 37 | (p/print-table var-usages)) 38 | :edn 39 | (p/pprint analysis) 40 | (println "use table or edn as first argument"))))) 41 | -------------------------------------------------------------------------------- /analysis/src/clj_kondo/tools/private_vars.clj: -------------------------------------------------------------------------------- 1 | (ns clj-kondo.tools.private-vars 2 | (:require [clojure.set :as set] 3 | [clj-kondo.core :as clj-kondo])) 4 | 5 | (defn -main [& paths] 6 | (let [analysis (:analysis (clj-kondo/run! {:lint paths :config {:analysis true}})) 7 | {:keys [:var-definitions :var-usages]} analysis 8 | private-var-defs (->> var-definitions 9 | (filter :private) 10 | (group-by (juxt :ns :name))) 11 | private-var-usages (->> var-usages 12 | (filter (fn [{:keys [:to :name]}] 13 | (contains? private-var-defs [to name]))) 14 | (group-by (juxt :to :name))) 15 | unused-keys (set/difference (set (keys private-var-defs)) 16 | (set (keys private-var-usages))) 17 | unused (->> (select-keys private-var-defs unused-keys) 18 | vals 19 | (map first)) 20 | illegal (keep (fn [{:keys [:from :to] :as v}] 21 | (when (not= from to) 22 | v)) 23 | (apply concat (vals private-var-usages)))] 24 | (doseq [{:keys [:ns :name :filename :row :col]} unused] 25 | (println (str filename ":" row ":" col " warning: " ns "/" name " is private but never used"))) 26 | (doseq [{:keys [:from :to :name :filename :row :col]} illegal] 27 | (println (str filename ":" row ":" col " warning: " to "/" name 28 | " is private and cannot be accessed from namespace " 29 | from))))) 30 | --------------------------------------------------------------------------------