Permalink
Browse files

still working on unused locals

  • Loading branch information...
1 parent 86f62da commit 58a54ed66acbf69ebbdb252fcb1485f5753eb0a8 @jonase committed Jan 18, 2012
Showing with 110 additions and 29 deletions.
  1. +7 −7 project.clj
  2. +25 −1 src/brittle/core.clj
  3. +28 −6 src/eastwood/linters/unused.clj
  4. +50 −15 src/eastwood/util.clj
View
@@ -4,24 +4,24 @@
[analyze "0.1.3-SNAPSHOT"]
;; clojure.data.json OK
- #_[org.clojure/data.json "0.1.1"]
+ [org.clojure/data.json "0.1.1"]
;; clojure.core.match: lots of reflection warnings -- eastwood fault
- #_[org.clojure/core.match "0.2.0-alpha8"]
+ [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"]
+ [org.clojure/core.logic "0.6.7"]
;; clojure.data.finger-tree: lots of reflection
- #_[org.clojure/data.finger-tree "0.0.1"]
+ [org.clojure/data.finger-tree "0.0.1"]
;; clojure.tools.logging: OK
- #_[org.clojure/tools.logging "0.2.3"]
+ [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"]
+ [org.clojure/java.jdbc "0.1.1"]
;; clojure.data.csv: OK
- #_[org.clojure/data.csv "0.1.0"]
+ [org.clojure/data.csv "0.1.0"]
])
View
@@ -15,10 +15,34 @@
(.length s)
(.method s 0))
-(defn foo [x] ; <- never used
+(defn foo [x] ; <- x is never used
(let [a 0 x 1] ; <- a is never used
x))
+;; All are used here
+(defn bar [y]
+ (let [y y]
+ y))
+
+;; and here
+(defn baz [e]
+ (let [g e
+ f g]
+ f))
+
+(defn foobar [x]
+ (letfn [(a [] x)]))
+
+(defn make-cols-unique []
+ (loop [[col-name] nil]
+ col-name))
+
+(fn [] (loop [[a] nil] a))
+
+;;(defn make-cols-unique []
+;; (loop [[col-name] nil
+;; unique-cols nil]
+;; col-name))
(defn hour [] ;; Deprecated x 2!
@@ -4,13 +4,13 @@
(:use analyze.core analyze.util))
(defn unused-locals* [binding-expr]
- (let [lbs (util/local-bindings binding-expr)
+ (let [lbs (util/bound-locals binding-expr)
free (apply set/union (map util/free-locals
(:children binding-expr)))]
(set/difference lbs free)))
(defn binding-expr? [expr]
- (#{:let :let-fn :fn-method} (:op expr)))
+ (#{:let :letfn :fn-method} (:op expr)))
(defmulti report (fn [expr locals] (:op expr)))
@@ -19,7 +19,7 @@
(println "Unused let-locals:" locals)
(println "Unused let-local:" (first locals))))
-(defmethod report :let-fn [expr locals]
+(defmethod report :letfn [expr locals]
(if (> (count locals) 1)
(println "Unused letfn arguments:" locals)
(println "Unused letfn argument:" (first locals))))
@@ -29,12 +29,12 @@
(println "Unused fn arguments:" locals)
(println "Unused fn argument:" (first locals))))
+(def ^:dynamic ignore-local? #{'_ '&form '&env})
+
(defn unused-locals [exprs]
(doseq [expr (mapcat expr-seq exprs)]
(when (binding-expr? expr)
- (when-let [ul (seq (for [{sym :sym} (unused-locals* expr)
- :when (not= '_ sym)]
- sym))]
+ (when-let [ul (seq (remove ignore-local? (unused-locals* expr)))]
(report expr ul)))))
;; Unused private vars
@@ -57,3 +57,25 @@
(doseq [pvar pdefs
:when (nil? (vfreq pvar))]
(println "Private var" pvar "is never used"))))
+
+
+(comment ;; Fails on
+ (def ignore [:children :Expr-obj :ObjMethod-obj :LocalBinding-obj :BindingInit-obj :env :method :init])
+ (def src '(loop [[a] nil] a))
+ (def boilerplate {:ns {:name 'user} :context :eval})
+ (def analyzed (analyze-one boilerplate src))
+ (unused-locals [analyzed])
+
+ (apply print-expr analyzed ignore)
+
+ (fn [] (let [G__12015 nil
+ vec__12016 G__12015
+ a (nth vec__12016 0 nil)]
+ (do (let [G__12121 ?]
+ (do (let [vec__12123 ?
+ a ?]
+ (do 1 a)))))))
+
+
+
+ )
View
@@ -3,21 +3,56 @@
;; TODO: Profile!
-(defn local-bindings [expr]
- (condp = (:op expr)
- :fn-method (if-let [rest-param (:rest-param expr)]
- (conj (set (:required-params expr)) rest-param)
- (set (:required-params expr)))
- :let (set (map :local-binding (:binding-inits expr)))
- :let-fn (set (map :local-binding (:binding-inits expr)))
- #{}))
-
-
-(defn free-locals [expr]
- (if (= (:op expr) :local-binding-expr)
- #{(:local-binding expr)}
- (set/difference (apply set/union (map free-locals
+(defmulti bound-locals :op)
+(defmulti free-locals :op)
+
+(defmethod bound-locals :fn-method [expr]
+ (let [required (set (map :sym (:required-params expr)))]
+ (if-let [rest (:sym (:rest-param expr))]
+ (conj required rest)
+ required)))
+
+(defmethod bound-locals :let [expr]
+ (set (map (comp :sym :local-binding) (:binding-inits expr))))
+
+(defmethod bound-locals :letfn [expr]
+ (set (map (comp :sym :local-binding) (:binding-inits expr))))
+
+(defmethod free-locals :local-binding-expr [expr]
+ #{(:sym (:local-binding expr))})
+
+(defn tails [seq]
+ (take-while (complement empty?)
+ (iterate rest seq)))
+
+(declare free-locals*)
+;; Tricky because the second 'a in (let [a a] a) is free.
+(defmethod free-locals :let [expr]
+ (let [free-in-bindings
+ (let [local-bindings (map :local-binding (:binding-inits expr))]
+ (loop [[lb & lbs] local-bindings
+ flocals (free-locals (:init (first local-bindings)))]
+ (if lbs
+ (let [free (apply set/union (map (comp free-locals :init) lbs))]
+ (recur lbs (disj (set/union free flocals) (:sym lb))))
+ flocals)))]
+ (set/union
+ (set/difference (free-locals (:body expr))
+ (bound-locals expr))
+ free-in-bindings)))
+
+(defn free-locals* [expr]
+ (set/difference (apply set/union (map free-locals
(:children expr)))
- (local-bindings expr))))
+ (bound-locals expr)))
+
+(defmethod free-locals :fn-method [expr]
+ (free-locals* expr))
+
+(defmethod free-locals :letfn [expr]
+ (free-locals* expr))
+(defmethod free-locals :default [expr]
+ (apply set/union (map free-locals (:children expr))))
+(def binding-expr? #{:fn-method :let :letfn})

0 comments on commit 58a54ed

Please sign in to comment.