Initial commit
This commit is contained in:
commit
c2c2d791eb
9 changed files with 167 additions and 0 deletions
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
/pom.xml
|
||||
*jar
|
||||
/lib
|
||||
/classes
|
||||
/native
|
||||
/.lein-failures
|
||||
/checkouts
|
||||
/.lein-deps-sum
|
||||
*~
|
48
README.md
Normal file
48
README.md
Normal file
|
@ -0,0 +1,48 @@
|
|||
# kibit
|
||||
|
||||
*There's a function for that!*
|
||||
|
||||
`kibit` is a static code analyzer for Clojure which uses the
|
||||
[`core.logic`](https://github.com/clojure/core.logic) unifier to
|
||||
search for patterns of code for which there might exist a more
|
||||
idiomatic function or macro. For example if kibit finds the code
|
||||
|
||||
(if (some test)
|
||||
(some action)
|
||||
nil)
|
||||
|
||||
it will make the suggestion to use the `while` macro instead of `if`.
|
||||
|
||||
## Usage
|
||||
|
||||
Add
|
||||
|
||||
:dev-dependencies [...
|
||||
[kibit "0.0.1"]
|
||||
...]
|
||||
|
||||
to your `project.clj` file and run
|
||||
|
||||
lein kibit
|
||||
|
||||
to analyze your namespaces.
|
||||
|
||||
## Contributing
|
||||
|
||||
It is very easy to write new patterns for `kibit` to look for. Take a
|
||||
look at [`arithmetic.clj`] how new patterns are created. If you know
|
||||
of a reaccuring pattern of code that can be simplified, please
|
||||
consider sending me a pull request.
|
||||
|
||||
Bugs can be reported using the github bug tracker.
|
||||
|
||||
## TODO
|
||||
|
||||
* Figure out how to report line numbers.
|
||||
* More rules
|
||||
|
||||
## License
|
||||
|
||||
Copyright (C) 2012 Jonas Enlund
|
||||
|
||||
Distributed under the Eclipse Public License, the same as Clojure.
|
7
project.clj
Normal file
7
project.clj
Normal file
|
@ -0,0 +1,7 @@
|
|||
(defproject kibit "0.0.1-SNAPSHOT"
|
||||
:description "There's a function for that!"
|
||||
:dependencies [[org.clojure/clojure "1.3.0"]
|
||||
[org.clojure/core.logic "0.6.7"]
|
||||
[org.clojure/tools.namespace "0.1.2"]]
|
||||
:eval-in-leiningen true
|
||||
:warn-on-reflection false)
|
21
src/kibit/arithmetic.clj
Normal file
21
src/kibit/arithmetic.clj
Normal file
|
@ -0,0 +1,21 @@
|
|||
(ns kibit.arithmetic)
|
||||
|
||||
(def rules
|
||||
'{(+ ?x 1) inc
|
||||
(+ 1 ?x) inc
|
||||
(- ?x 1) dec
|
||||
|
||||
(= 0 ?x) zero?
|
||||
(= ?x 0) zero?
|
||||
(== 0 ?x) zero?
|
||||
(== ?x 0) zero?
|
||||
|
||||
(< 0 ?x) pos?
|
||||
(> ?x 0) pos?
|
||||
(<= 1 ?x) pos?
|
||||
|
||||
(< ?x 0) neg?})
|
||||
|
||||
|
||||
|
||||
|
7
src/kibit/control_structures.clj
Normal file
7
src/kibit/control_structures.clj
Normal file
|
@ -0,0 +1,7 @@
|
|||
(ns kibit.control-structures)
|
||||
|
||||
(def rules
|
||||
'{(if ?x ?y nil) when
|
||||
(if ?x nil ?y) when-not
|
||||
(if (not ?x) ?y ?z) if-not})
|
||||
|
48
src/kibit/core.clj
Normal file
48
src/kibit/core.clj
Normal file
|
@ -0,0 +1,48 @@
|
|||
(ns kibit.core
|
||||
(:require [clojure.core.logic :as l]
|
||||
[clojure.java.io :as io]
|
||||
[clojure.string :as string]
|
||||
[kibit.arithmetic :as arith]
|
||||
[kibit.control-structures :as control]
|
||||
[kibit.misc :as misc])
|
||||
(:import [java.io PushbackReader]))
|
||||
|
||||
(def all-rules (merge control/rules
|
||||
arith/rules
|
||||
misc/rules))
|
||||
|
||||
(defn src [path]
|
||||
(if-let [res (io/resource path)]
|
||||
(PushbackReader. (io/reader res))
|
||||
(throw (RuntimeException. (str "File not found: " path)))))
|
||||
|
||||
(defn source-file [ns-sym]
|
||||
(-> (name ns-sym)
|
||||
(string/replace "." "/")
|
||||
(string/replace "-" "_")
|
||||
(str ".clj")))
|
||||
|
||||
(defn read-ns [r]
|
||||
(lazy-seq
|
||||
(let [form (read r false ::eof)]
|
||||
(when-not (= form ::eof)
|
||||
(cons form (read-ns r))))))
|
||||
|
||||
(defn check [expr]
|
||||
(doseq [[rule alt] all-rules]
|
||||
(when (and (sequential? expr)
|
||||
(l/unifier expr rule))
|
||||
(println "[Kibit] Consider" alt "instead of" expr))))
|
||||
|
||||
(defn expr-seq [expr]
|
||||
(tree-seq sequential?
|
||||
seq
|
||||
expr))
|
||||
|
||||
(defn check-ns
|
||||
([ns-sym rules]
|
||||
(with-open [reader (-> ns-sym source-file src)]
|
||||
(doseq [form (mapcat expr-seq (read-ns reader))]
|
||||
(check form))))
|
||||
([ns-sym]
|
||||
(check-ns ns-sym all-rules)))
|
5
src/kibit/misc.clj
Normal file
5
src/kibit/misc.clj
Normal file
|
@ -0,0 +1,5 @@
|
|||
(ns kibit.misc)
|
||||
|
||||
(def rules
|
||||
'{(apply str (interpose ?x ?y)) clojure.string/join
|
||||
(apply concat (apply map ?x ?y)) mapcat})
|
16
src/leiningen/kibit.clj
Normal file
16
src/leiningen/kibit.clj
Normal file
|
@ -0,0 +1,16 @@
|
|||
(ns leiningen.kibit
|
||||
(:require [clojure.tools.namespace :as ns]
|
||||
[clojure.java.io :as io]
|
||||
[kibit.core :as kibit]))
|
||||
|
||||
(defn kibit [project]
|
||||
(let [namespaces (-> project
|
||||
:source-path
|
||||
io/file
|
||||
ns/find-namespaces-in-dir)]
|
||||
(doseq [ns-sym namespaces]
|
||||
(try
|
||||
(println "==" ns-sym "==")
|
||||
(kibit/check-ns ns-sym)
|
||||
(catch RuntimeException e (println ns-sym "not found.")))
|
||||
(println "done."))))
|
6
test/kibit/test/core.clj
Normal file
6
test/kibit/test/core.clj
Normal file
|
@ -0,0 +1,6 @@
|
|||
(ns kibit.test.core
|
||||
(:use [kibit.core])
|
||||
(:use [clojure.test]))
|
||||
|
||||
(deftest replace-me ;; FIXME: write
|
||||
(is false "No tests have been written."))
|
Loading…
Reference in a new issue