Boot tasks to check, analyze and inspect Clojure/Script code.
It relies on universe tested kibit, eastwood, yagni, bikeshed and other titans.
To be able to reach out to multiple code analyzers as well as compose them as Boot tasks:
(require '[tolitius.boot-check :as check])
(deftask check-sources []
(set-env! :source-paths #{"src" "test"})
(comp
(check/with-yagni)
(check/with-eastwood)
(check/with-kibit)
(check/with-bikeshed)))
You can choose the tools (tasks) that apply, i.e. use one or several, and boot-check
will do the rest: integration with analyzers, dependencies, reports, etc..
All these tasks will run inside Boot pods.
kibit is a static code analyzer for Clojure, ClojureScript, cljx and other Clojure variants.
To check your code directly from shell:
$ boot check/with-kibit
latest report from kibit.... [You Rock!]
In case there are problems:
(defn when-vs-if []
(if 42 42 nil))
(defn vec-vs-into []
(into [] 42))
kibit will show suggestions:
$ boot check/with-kibit
At ../.boot/cache/tmp/../fun/boot-check/yeg/-grrwi1/test/with_kibit.clj:4:
Consider using:
(when 42 42)
instead of:
(if 42 42 nil)
At ../.boot/cache/tmp/../fun/boot-check/yeg/-grrwi1/test/with_kibit.clj:7:
Consider using:
(vec 42)
instead of:
(into [] 42)
WARN: kibit found some problems:
{:problems #{{:expr (if 42 42 nil), :line 4, :column 3, :alt (when 42 42)}
{:expr (into [] 42), :line 7, :column 3, :alt (vec 42)}}}
To use boot-check
tasks within build.boot
is easy:
(require '[tolitius.boot-check :as check])
(deftask check-sources []
(set-env! :source-paths #{"src" "test"})
(comp
(check/with-kibit)))
$ boot check/with-kibit -h
Static code analyzer for Clojure, ClojureScript, cljx and other Clojure variants.
This task will run all the kibit checks within a pod.
At the moment it takes no arguments, but behold..! it will. (files, rules, reporters, etc..)
Options:
-h, --help Print this help info.
-t, --throw-on-errors throw an exception if the check does not pass
yagni is a static code analyzer that helps you find unused code in your applications and libraries.
To check your code directly from shell:
$ boot check/with-yagni
latest report from yagni.... [You Rock!]
if Yagni finds unused code it will gladly report the news:
WARN: could not find any references to the following:
tolitius.yagni/check
test.with-yagni/func-the-second
test.with-yagni/other-func
tolitius.yagni/report
test.with-kibit/vec-vs-into
test.with-yagni/-main
WARN: the following have references to them, but their parents do not:
tolitius.yagni/yagni-deps
tolitius.yagni/pp
test.with-kibit/when-vs-if
test.with-yagni/func
test.with-yagni/notafunc
To use boot-check
tasks within build.boot
is easy:
(require '[tolitius.boot-check :as check])
(deftask check-sources []
(set-env! :source-paths #{"src" "test"})
(comp
(check/with-yagni)))
$ boot check/with-yagni -h
Static code analyzer for Clojure that helps you find unused code in your applications and libraries.
This task will run all the yagni checks within a pod.
Options:
-h, --help Print this help info.
-o, --options OPTIONS OPTIONS sets yagni options EDN map.
-t, --throw-on-errors throw an exception if the check does not pass
Yagni works by searching your codebase from an initial set of entrypoints. As libraries, multi-main programs, and certain other types of projects either tend to have no :main
or many entrypoint methods, you can instead, optionally, enumerate a list of entrypoints
for your project in options:
(check/with-yagni :options {:entry-points ["test.with-yagni/-main"
"test.with-yagni/func-the-second"
42]})))
check out the example in the boot.build
of this project.
eastwood is a Clojure lint tool that uses the tools.analyzer and tools.analyzer.jvm libraries to inspect namespaces and report possible problems.
To check your code directly from shell:
$ boot check/with-eastwood
latest report from eastwood.... [You Rock!]
if eastwood finds problems it will gladly report the news:
== Linting test.with-kibit ==
... /test/with_kibit.clj:4:3: constant-test: Test expression is always logical true or always logical false: 42 in form (if 42 42 nil)
== Linting test.with-eastwood ==
... /test/with_eastwood.clj:5:8: def-in-def: There is a def of a nested inside def nested-def
== Warnings: 2 (not including reflection warnings) Exceptions thrown: 0
WARN: eastwood found some problems ^^^
To use boot-check
tasks within build.boot
is easy:
(require '[tolitius.boot-check :as check])
(deftask check-sources []
(set-env! :source-paths #{"src" "test"})
(comp
(check/with-eastwood)))
$ boot check/with-eastwood -h
Clojure lint tool that uses the tools.analyzer and tools.analyzer.jvm libraries to inspect namespaces and report possible problems
This task will run all the eastwood checks within a pod.
At the moment it takes no arguments, but behold..! it will. (linters, namespaces, etc.)
Options:
-h, --help Print this help info.
-t, --throw-on-errors throw an exception if the check does not pass
bikeshed is a Clojure "checkstyle/pmd" tool that designed to tell you your code is bad, and that you should feel bad.
To check your code directly from shell:
$ boot check/with-bikeshed
latest report from bikeshed.... [You Rock!]
if bikeshed finds problems it will gladly report the news:
Checking for lines longer than 80 characters.
Badly formatted files:
../tolitius/boot_check.clj:8: [boot.core :as core :refer [deftask user-files tmp-file set-env! get-env]]
../tolitius/boot_check.clj:25: "Static code analyzer for Clojure, ClojureScript, cljx and other Clojure variants.
../tolitius/boot_check.clj:29: At the moment it takes no arguments, but behold..! it will. (files, rules, reporters, etc..)"
../tolitius/boot_check.clj:30: ;; [f files FILE #{sym} "the set of files to check."] ;; TODO: convert these to "tmp-dir/file"
Checking for lines with trailing whitespace.
Badly formatted files:
../tolitius/boot/helper.clj:6: (mapv #(.getAbsolutePath %)
../tolitius/checker/bikeshed.clj:7: '[[lein-bikeshed "0.2.0" :exclusions [org.clojure/tools.cli
../tolitius/checker/yagni.clj:33: (let [graph# (binding [*ns* (the-ns *ns*)]
../tolitius/boot/helper.clj:6: (mapv #(.getAbsolutePath %)
../tolitius/checker/bikeshed.clj:7: '[[lein-bikeshed "0.2.0" :exclusions [org.clojure/tools.cli
../tolitius/checker/yagni.clj:33: (let [graph# (binding [*ns* (the-ns *ns*)]
Checking for files ending in blank lines.
No files found.
Checking for redefined var roots in source directories.
No with-redefs found.
Checking whether you keep up with your docstrings.
9/50 [18.00%] functions have docstrings.
Use -v to list functions without docstrings
WARN: bikeshed found some problems ^^^
To use boot-check
tasks within build.boot
is easy:
(require '[tolitius.boot-check :as check])
(deftask check-sources []
(set-env! :source-paths #{"src" "test"})
(comp
(check/with-bikeshed)))
$ boot check/with-bikeshed -h
This task is backed by 'lein-bikeshed' which is designed to tell you your code is bad, and that you should feel bad
This task will run all the bikeshed checks within a pod.
At the moment it takes no arguments, but behold..! it will. ('-m, --max-line-length', etc.)
Options:
-h, --help Print this help info.
-o, --options OPTIONS OPTIONS sets bikeshed options EDN map.
-t, --throw-on-errors throw an exception if the check does not pass
Bikeshed takes a couple of options:
(check/with-bikeshed :options {:verbose true
:max-line-length 42})
or
$ boot check/with-bikeshed -o '{:max-line-length 4}'
check out the example in the boot.build of this project.
All tasks (i.e. for kibit, yagni, eastwood, bikeshed, etc.) accept an optional flag:
-t, --throw-on-errors throw an exception if the check does not pass
that if set will report all the problems found with the task, and then throw an exception.
Here are some examples:
boot.user=> (set-env! :source-paths #{"src" "test"})
boot.user=> (boot (check/with-kibit "-t"))
... reporting problems here then throws:
clojure.lang.ExceptionInfo: kibit checks fail
boot.user=> *e
#error {
:cause "kibit checks fail"
:data {:causes ({:expr (if 42 42 nil), :line 4, :column 3, :alt (when 42 42)} {:expr (into [] 42), :line 7, :column 3, :alt (vec 42)})}
...}
boot.user=> (boot (check/with-yagni "-t"))
... reporting problems here then throws:
clojure.lang.ExceptionInfo: yagni checks fail
boot.user=> *e
#error {
:cause "yagni checks fail"
:data {:causes {:no-refs #{tolitius.boot-check/with-eastwood test.with-yagni/other-func tolitius.boot-check/with-yagni tolitius.boot-check/with-bikeshed tolitius.boot-check/with-kibit test.with-eastwood/nested-def test.with-kibit/vec-vs-into test.with-eastwood/always-true}, :no-parent-refs #{tolitius.boot.helper/make-pod-pool tolitius.boot.helper/fileset->paths tolitius.checker.yagni/yagni-deps tolitius.checker.yagni/entry-points-file tolitius.checker.bikeshed/bikeshed-deps tolitius.checker.yagni/create-entry-points test.with-kibit/when-vs-if tolitius.checker.yagni/check tolitius.checker.yagni/pp tolitius.boot.helper/tmp-dir-paths test.with-eastwood/a tolitius.checker.yagni/report tolitius.checker.yagni/check-graph tolitius.checker.kibit/kibit-deps tolitius.checker.eastwood/check tolitius.checker.kibit/check tolitius.boot-check/pod-deps tolitius.boot-check/with-throw test.with-yagni/func tolitius.checker.bikeshed/check tolitius.checker.eastwood/eastwood-deps tolitius.boot-check/bootstrap}}}
...}
boot.user=> (boot (check/with-eastwood "-t"))
... reporting problems here then throws:
clojure.lang.ExceptionInfo: eastwood checks fail
boot.user=> *e
#error {
:cause "eastwood checks fail"
:data {:causes {:err nil, :warning-count 12, :exception-count 0}}
...}
In case of Eastwood warnings are not returned, just their number of them. They are however reported (printed) as found.
boot.user=> (boot (check/with-bikeshed "-t"))
... reporting problems here then throws:
boot.user=> *e
#error {
:cause "bikeshed checks fail"
:data {:causes true}
...}
In case of Bikeshed, no errors / warnings are retured, since its own internal checks just return true/false values. But the exception is raised nevertheless to indicate that some checks have failed.
Here is a boot check demo project which can be cloned and played with.
Copyright © 2016 toliitus
Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.