Initial commit

This commit is contained in:
Jonas Enlund 2012-03-04 18:41:53 +02:00
commit c2c2d791eb
9 changed files with 167 additions and 0 deletions

9
.gitignore vendored Normal file
View file

@ -0,0 +1,9 @@
/pom.xml
*jar
/lib
/classes
/native
/.lein-failures
/checkouts
/.lein-deps-sum
*~

48
README.md Normal file
View 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
View 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
View 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?})

View 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
View 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
View 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
View 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
View 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."))