Permalink
Browse files

lein plugin and refactorings

  • Loading branch information...
1 parent c3410c9 commit 266c8f7d4cbec96b6e3a2d6338aa0c1f8c5620f6 @jonase committed Apr 19, 2012
View
@@ -2,6 +2,7 @@ pom.xml
*jar
/lib/
/classes/
+/checkouts/
.lein-failures
.lein-deps-sum
*~
View
@@ -5,7 +5,7 @@
Eastwood is a clojure lint tool which uses the
[analyze](https://github.com/frenchy64/analyze) library to inspect
namespaces and report anomalies. Currently it works with projects
-running Clojure 1.3.0.
+running Clojure 1.3.0 and newer.
## What's there?
@@ -14,11 +14,12 @@ Eastwood warns when it finds
- deprecated java instance methods, static fields, static methods and
constructors
- deprecated clojure vars
-- unused let-locals and function arguments
+- unused function arguments
- unused private vars
- reflection
- naked (:use ...)
- misplaced docstrings
+- keyword typos
## How to use?
View
@@ -1,27 +1,6 @@
-(defproject eastwood "0.0.1"
+(defproject jonase/eastwood "0.0.1-SNAPSHOT"
:description "A Clojure lint tool"
- :dependencies [[org.clojure/clojure "1.3.0"]
- [analyze "0.1.3-SNAPSHOT"]
-
- ;; clojure.data.json OK
- [org.clojure/data.json "0.1.1"]
-
- ;; clojure.core.match: lots of reflection warnings -- eastwood fault
- [org.clojure/core.match "0.2.0-alpha8"]
-
- ;; clojure.core.logic: 2x misplaced docstrings, subst? never used + lots of reflection
- [org.clojure/core.logic "0.6.7"]
-
- ;; clojure.data.finger-tree: lots of reflection
- [org.clojure/data.finger-tree "0.0.1"]
-
- ;; clojure.tools.logging: OK
- [org.clojure/tools.logging "0.2.3"]
-
- ;; clojure.java.jdbc: using deprecated replicate + naked use.
- ;; clojure.java.jdbc.internal: reflection getCause
- [org.clojure/java.jdbc "0.1.1"]
-
- ;; clojure.data.csv: OK
- [org.clojure/data.csv "0.1.0"]
- ])
+ :dependencies [[analyze "0.1.6-SNAPSHOT"]
+ [org.clojure/tools.namespace "0.1.2"]
+ [org.clojars.brenton/google-diff-match-patch "0.1"]]
+ :eval-in-leiningen true)
View
@@ -1,39 +0,0 @@
-(ns brittle.core
- (:import [java.awt Frame])
- (:use clojure.java.io)) ; Naked use
-
-#_(def *rebind-me* nil) ; non-dynamic, already checked by the compiler
-
-(def ^:private unused) ; Never used
-
-(defn inc2 [x] ; Misplaced docstring
- "inc by 2"
- (inc (inc x)))
-
-(defn len [s] ; reflects
- (replicate 1 0) ; deprecated
- (.length s)
- (.method s 0))
-
-(defn foo [x] ; <- never used
- (let [a 0 x 1] ; <- a is never used
- x))
-
-
-
-(defn hour [] ;; Deprecated x 2!
- Frame/TEXT_CURSOR
- (.getHours (java.util.Date. 2012 21 12)))
-
-
-(defn defndef [y]
- (def a 3))
-
-;; This is often ok.
-(defmacro ^:private defindefmacro []
- (def b nil))
-
-(defrecord SomeRecord [])
-
-(defprotocol SomeProto
- (hmm [a b c]))
View
@@ -1,50 +1,63 @@
-(set! *warn-on-reflection* false)
-
(ns eastwood.core
- (:require [analyze.core :as analyze]
+ (:require [clojure.java.io :as io]
+ [analyze.core :as analyze]
[clojure.string :as string]
[clojure.set :as set]
+ [clojure.pprint :as pp]
+ [clojure.tools.namespace :as clj-ns]
[eastwood.linters.misc :as misc]
[eastwood.linters.deprecated :as deprecated]
[eastwood.linters.unused :as unused]
- [eastwood.linters.reflection :as reflection]))
-
+ [eastwood.linters.reflection :as reflection]
+ [eastwood.linters.typos :as typos])
+ (:import [java.io PushbackReader]
+ [clojure.lang LineNumberingPushbackReader]))
-(defn analyze [ns-sym]
- (let [source-file (-> (name ns-sym)
- (string/replace "." "/")
- (string/replace "-" "_")
- (str ".clj"))]
- (analyze/analyze-path source-file ns-sym)))
+(reset! analyze/JAVA-OBJ true)
+(reset! analyze/CHILDREN true)
(def ^:private linters
{:naked-use misc/naked-use
:misplaced-docstrings misc/misplaced-docstrings
:def-in-def misc/def-in-def
- ;; :non-dynamic-earmuffs misc/non-dynamic-earmuffs ; checked by compiler
:reflection reflection/reflection
:deprecations deprecated/deprecations
- ;; :unused-locals unused/unused-locals ; Currently too slow to be practical
:unused-fn-args unused/unused-fn-args
- :unused-private-vars unused/unused-private-vars})
-
-(def ^:private all-linters (set (keys linters)))
+ :unused-private-vars unused/unused-private-vars
+ :keyword-typos typos/keyword-typos})
+
+(def ^:private default-linters
+ #{:naked-use
+ :misplaced-docstrings
+ :def-in-def
+ :deprecations
+ :unused-fn-args
+ :keyword-typos
+ :unused-private-vars
+ :reflection})
(defn- lint [exprs kw]
- (println "==" kw "==")
((linters kw) exprs))
-(defn lint-ns [ns-sym & {:keys [only exclude]
- :or {only (disj all-linters :unused-locals)
- exclude nil}}]
- (let [linters (set/difference (set only) (set exclude))
- exprs (analyze ns-sym)]
- (doseq [linter linters]
- (lint exprs linter))))
+(defn lint-ns [ns-sym linters]
+ (println "== Linting" ns-sym "==")
+ (let [exprs (analyze/analyze-path ns-sym)]
+ (doseq [linter linters
+ result (lint exprs linter)]
+ (pp/pprint result)
+ (println))))
+
+(defn run-eastwood [opts]
+ (pp/pprint opts)
+ (let [namespaces (set (or (:namespaces opts)
+ (mapcat #(-> % io/file clj-ns/find-namespaces-in-dir)
+ (:source-paths opts))))
+ excluded-namespaces (set (:exclude-namespaces opts))
+ namespaces (set/difference namespaces excluded-namespaces)
+ linters (set (or (:linters opts)
+ default-linters))
+ excluded-linters (set (:exclude-linters opts))
+ linters (set/difference linters excluded-linters)]
+ (doseq [namespace namespaces]
+ (lint-ns namespace linters))))
-;; (lint-ns 'brittle.core)
-;; (lint-ns 'clojure.core.logic)
-;; (lint-ns 'clojure.java.jdbc.internal)
-;; (lint-ns 'clojure.core.match)
-;; (lint-ns 'clojure.data.csv)
-;; (lint-ns 'clojure.data.json)
@@ -1,5 +1,3 @@
-;; TODO: Check for deprecated Interfaces, Classes and Exceptions?
-
(ns eastwood.linters.deprecated
(:use [analyze.util :only [expr-seq print-expr]]))
@@ -23,33 +21,35 @@
(.isAnnotationPresent field java.lang.Deprecated))))
(defmethod deprecated :new [expr]
- (-> expr
- :Expr-obj
- .ctor
- (.isAnnotationPresent java.lang.Deprecated)))
+ (let [ctor (-> expr
+ :Expr-obj
+ .ctor)]
+ (when ctor
+ (.isAnnotationPresent ctor java.lang.Deprecated))))
(defmethod deprecated :default [_] false)
-(defmulti report-deprecated :op)
+(defmulti msg :op)
-(defmethod report-deprecated :var [expr]
- (printf "Var '%s' is deprecated.\n"
+(defmethod msg :var [expr]
+ (format "Var '%s' is deprecated."
(:var expr)))
-(defmethod report-deprecated :instance-method [expr]
- (printf "Instance method '%s' is deprecated.\n"
+(defmethod msg :instance-method [expr]
+ (format "Instance method '%s' is deprecated."
(-> expr :Expr-obj .method)))
-(defmethod report-deprecated :static-field [expr]
- (printf "Static field '%s' is deprecated.\n"
+(defmethod msg :static-field [expr]
+ (format "Static field '%s' is deprecated."
(-> expr :Expr-obj .field)))
-(defmethod report-deprecated :new [expr]
- (printf "Constructor '%s' is deprecated.\n"
+(defmethod msg :new [expr]
+ (format "Constructor '%s' is deprecated."
(-> expr :Expr-obj .ctor)))
(defn deprecations [exprs]
- (doseq [expr exprs
- dexpr (filter deprecated (expr-seq expr))]
- (report-deprecated dexpr)))
-
+ (for [expr exprs
+ dexpr (filter deprecated (expr-seq expr))]
+ {:linter :deprecated
+ :msg (msg dexpr)
+ :line (-> dexpr :env :line)}))
@@ -2,67 +2,68 @@
(:use analyze.util))
;; Naked use
-(defn- report-on-naked-use [use-expr]
- (doseq [s (map :val (:args use-expr))
- :when (symbol? s)]
- (println "Naked use of" (name s) "in" (-> use-expr :env :ns :name))))
(defn- use? [expr]
(and (= :invoke (:op expr))
(= :var (-> expr :fexpr :op))
(= 'use (-> expr :fexpr :var meta :name))))
(defn naked-use [exprs]
- (doseq [expr (mapcat expr-seq exprs)]
- (when (use? expr)
- (report-on-naked-use expr))))
-
-
+ (for [expr (mapcat expr-seq exprs)
+ :when (use? expr)
+ :let [s (filter symbol? (map :val (:args expr)))]
+ :when (not-empty s)]
+ {:linter :naked-use
+ :msg (format "Naked use of %s in %s" (seq s) (-> expr :env :ns :name))
+ :line (-> expr :env :line)}))
;; Missplaced docstring
-(defn- check-def [expr]
+(defn- misplaced-docstring? [expr]
(when (= :fn-expr (-> expr :init :op))
- (doseq [method (-> expr :init :methods)]
- (let [body (:body method)]
- (when (and (= :do (:op body))
- (< 1 (count (-> body :exprs))))
- (let [first-exp (-> body :exprs first)]
- (when (and (= :literal (:op first-exp))
- (string? (:val first-exp)))
- (println "Possibly misplaced docstring," (-> expr :var)))))))))
+ (some true?
+ (for [method (-> expr :init :methods)
+ :let [body (:body method)]
+ :when (and (= :do (:op body))
+ (< 1 (count (-> body :exprs))))
+ :let [first-expr (-> body :exprs first)]]
+ (= :string
+ (-> body :exprs first :op))))))
(defn misplaced-docstrings [exprs]
- (doseq [expr (mapcat expr-seq exprs)
- :when (= (:op expr) :def)]
- (check-def expr)))
+ (for [expr (mapcat expr-seq exprs)
+ :when (and (= (:op expr) :def)
+ (misplaced-docstring? expr))]
+ {:linter :misplaced-docstring
+ :msg (format "Possibly misplaced docstring, %s" (:var expr))
+ :line (-> expr :env :line)}))
;; Nondynamic earmuffed var
+
(defn- earmuffed? [sym]
(let [s (name sym)]
(and (< 2 (count s))
(.startsWith s "*")
(.endsWith s "*"))))
-(defn- report-earmuffed-def [expr]
- (let [v (:var expr)
- s (.sym v)]
- (when (and (earmuffed? s)
- (not (:is-dynamic expr)))
- (println "Should" v "be marked dynamic?"))))
-
(defn non-dynamic-earmuffs [exprs]
- (doseq [expr (mapcat expr-seq exprs)
- :when (= (:op expr) :def)]
- (report-earmuffed-def expr)))
+ (for [expr (mapcat expr-seq exprs)
+ :when (= (:op expr) :def)
+ :let [v (:var expr)
+ s (.sym v)]
+ :when (and (earmuffed? s)
+ (not (:is-dynamic expr)))]
+ {:linter :non-dynamic-earmuffs
+ :msg (format "%s should be marked dynamic" v)
+ :line (-> expr :env :line)}))
;; Def-in-def
(defn def-in-def [exprs]
- (doseq [expr (mapcat expr-seq exprs)
- :when (and (= (:op expr) :def)
- (-> expr :var meta :macro not))]
- (when (some #(= (:op %) :def) (rest (expr-seq expr)))
- (println "There is a def inside" (:var expr)))))
-
-
+ (for [expr (mapcat expr-seq exprs)
+ :when (and (= (:op expr) :def)
+ (-> expr :var meta :macro not)
+ (some #(= (:op %) :def) (rest (expr-seq expr))))]
+ {:linter :def-in-def
+ :msg (format "There is a def inside %s" (:var expr))
+ :line (-> expr :env :line)}))
Oops, something went wrong.

0 comments on commit 266c8f7

Please sign in to comment.