clojure.tools.analyzer.query-test自2015年11月3日起未能通过测试,它从未在CI中运行。
git bisect确定提交a61b1699c15911d17f834745521a9837a8916eec引入了此错误。
我检查了CI,最新的构建没有执行query-test,可能是因为找不到datomic.Datom。
请见
https://build.clojure.org/job/tools.analyzer/728/console
预期
(let [ast (ast/prewalk (ast (defn x [] "misplaced docstring" 1))
index-vector-nodes)]
(q '[:find ?docstring
:where
[?def :op :def]
[?def :init ?fn]
[?fn :methods ?method]
[?method :body ?body]
[?body :statements ?statement]
[?statement :val ?docstring]
[?statement :type :string]
[?statement :idx 0]]
[ast]))
#{["misplaced docstring"]}
实际
(let [ast (ast/prewalk (ast (defn x [] "misplaced docstring" 1))
index-vector-nodes)]
(q '[:find ?docstring
:where
[?def :op :def]
[?def :init ?fn]
[?fn :methods ?method]
[?method :body ?body]
[?body :statements ?statement]
[?statement :val ?docstring]
[?statement :type :string]
[?statement :idx 0]]
[ast]))
#{}
完整repl重现
(require
'[clojure.tools.analyzer :as ana]
'[clojure.tools.analyzer.ast :as ast]
'[clojure.tools.analyzer.jvm :as ana.jvm]
'[clojure.tools.analyzer.env :refer [with-env]]
'[clojure.tools.analyzer.ast :refer :all]
'[clojure.test :refer [deftest is]]
'[clojure.tools.analyzer.ast.query :refer [q]]
'[clojure.tools.analyzer.ast :as ast]
'[clojure.tools.analyzer.utils :refer [compile-if]]
'[clojure.tools.analyzer.passes.index-vector-nodes :refer [index-vector-nodes]]
'[clojure.tools.analyzer.utils :refer [resolve-sym]])
(require '[clojure.tools.analyzer.passes.elide-meta :refer [elides elide-meta]])
(ns-unmap 'user 'macroexpand-1)
(defn desugar-host-expr [[op & expr :as form]]
(if (symbol? op)
(let [opname (name op)]
(cond
(= (first opname) \.) ; (.foo bar ..)
(let [[target & args] expr
args (list* (symbol (subs opname 1)) args)]
(with-meta (list '. target (if (= 1 (count args)) ;; we don't know if (.foo bar) ia
(first args) args)) ;; a method call or a field access
(meta form)))
(= (last opname) \.) ;; (class. ..)
(with-meta (list* 'new (symbol (subs opname 0 (dec (count opname)))) expr)
(meta form))
:else 表单))
表单))
(defn macroexpand-1 [表单 环境]
(if (seq? 表单)
(let [操作 (first 表单)]
(if (ana/specials 操作)
表单
(let [v (resolve-sym 操作 环境)]
(if (and (not (-> 环境 :locals (get 操作))) ;; locals cannot be macros
(:macro (meta v)))
(apply v 表单 环境 (rest 表单)) ; (m &form &env & args)
(desugar-host-expr 表单)))))
表单))
(defmacro foo [] 1)
(def e {:context :ctx/expr
:locals {}
:ns 'user})
(def e1 (atom {:namespaces {'user {:mappings (into (ns-map 'clojure.core)
{'foo '#'foo})
:aliases {}
:ns 'user}
'clojure.core {:mappings (ns-map 'clojure.core)
:aliases {}
:ns 'clojure.core}}}))
(defmacro ast [表单]
`(binding [ana/macroexpand-1 macroexpand-1
ana/create-var ~(fn [sym 环境]
(doto (intern (:ns 环境) sym)
(reset-meta! (meta sym))))
ana/parse ana/-parse
ana/var? ~var?
elides {:all #{:line :column :file :source-span}}]
(with-env e1
(postwalk (ana/analyze '~表单 e) elide-meta))))
(defmacro mexpand [表单]
`(with-env e1
(macroexpand-1 '~表单 e)))
(let [ast (ast/prewalk (ast (defn x [] "misplaced docstring" 1))
index-vector-nodes)]
(q '[:find ?docstring
:where
[?def :op :def]
[?def :init ?fn]
[?fn :methods ?method]
[?method :body ?body]
[?body :statements ?statement]
[?statement :val ?docstring]
[?statement :type :string]
[?statement :idx 0]]
[ast]))
数据逻辑查询似乎仍然有效。
(let [ast (ast/prewalk (ast (defn x [] "misplaced docstring" 1))
index-vector-nodes)]
(q '[:find ?fn
:where
[?def :op :def]
[?def :init ?fn]]
[ast]))
#{[:op :with-meta, :env {:context :ctx/expr, :locals {}, :ns user}, :form (fn*([] "misplaced docstring" 1)), :meta {:op :map, :env {:context :ctx/expr, :locals {}, :ns user}, :keys [{:op :const, :env {:context :ctx/expr, :locals {}, :ns user}, :type :keyword, :literal? true, :val :rettag, :form :rettag, :idx 0}], :vals [{:op :const, :env {:context :ctx/expr, :locals {}, :ns user}, :type :nil, :literal? true, :val nil, :form nil, :idx 0}], :form {:rettag nil}, :children [:keys :vals]}, :expr {:op :fn, :env {:context :ctx/expr, :locals {}, :ns user}, :form (fn*([] "misplaced docstring" 1)), :variadic? false, :max-fixed-arity 0, :methods [{:children [:params :body], :loop-id loop_4101, :params [], :fixed-arity 0, :op :fn-method, :env {:context :ctx/expr, :locals {}, :ns user, :once false}, :variadic? false, :form ([] "misplaced docstring" 1)), :idx 0, :body {:op :do, :env {:context :ctx/return, :locals {}, :ns user, :once false, :loop-id loop_4101, :loop-locals 0}, :form (do "misplaced docstring" 1), :statements [{:op :const, :env {:context :ctx/statement, :locals {}, :ns user, :once false, :loop-id loop_4101, :loop-locals 0}, :type :string, :literal? true, :val "misplaced docstring", :form "misplaced docstring", :idx 0}], :ret {:op :const, :env {:context :ctx/return, :locals {}, :ns user, :once false, :loop-id loop_4101, :loop-locals 0}, :type :number, :literal? true, :val 1, :form 1}, :children [:statements :ret], :body? true}}], :once false, :children [:methods]}, :children [:meta :expr], :raw-forms ((clojure.core/fn([] "misplaced docstring" 1)))}]