Share your thoughts in the 2024 State of Clojure Survey!

Welcome! Please see the About page for a little more info on how this works.

0 votes
in tools.namespace by
retagged by

Issue
Tools.namespace find 1.4.1 does not handle sources that don't include a ns declaration.

Reproduction
Given file ./src/foo.clj:

;; no ns declaration here

And test script ./test.clj:

(require '[clojure.tools.namespace.find :as f]
         '[clojure.java.io :as io])

(println "found nses:" (f/find-ns-decls-in-dir (io/file "src")))

We can see that tools.namespace 1.4.0 returns an empty sequence for this scenario:

$ clj -Sdeps '{:deps {org.clojure/tools.namespace {:mvn/version "1.4.0"}}}' -M --report stderr test.clj
found nses: ()

But version 1.4.1 throws an exception for this same test:

$ clj -Sdeps '{:deps {org.clojure/tools.namespace {:mvn/version "1.4.1"}}}' -M --report stderr test.clj
{:clojure.main/message
 "Execution error (NullPointerException) at clojure.tools.namespace.find/find-ns-decls-in-dir$fn (find.clj:93).\nCannot invoke \"clojure.lang.IObj.withMeta(clojure.lang.IPersistentMap)\" because \"x\" is null\n",
 :clojure.main/triage
 {:clojure.error/class java.lang.NullPointerException,
  :clojure.error/line 93,
  :clojure.error/cause
  "Cannot invoke \"clojure.lang.IObj.withMeta(clojure.lang.IPersistentMap)\" because \"x\" is null",
  :clojure.error/symbol
  clojure.tools.namespace.find/find-ns-decls-in-dir$fn,
  :clojure.error/source "find.clj",
  :clojure.error/phase :execution},
 :clojure.main/trace
 {:via
  [{:type clojure.lang.Compiler$CompilerException,
    :message
    "Syntax error macroexpanding at (/home/lee/proj/oss/-verify/tns-test/test.clj:4:1).",
    :data
    {:clojure.error/phase :execution,
     :clojure.error/line 4,
     :clojure.error/column 1,
     :clojure.error/source
     "/home/lee/proj/oss/-verify/tns-test/test.clj"},
    :at [clojure.lang.Compiler load "Compiler.java" 7665]}
   {:type java.lang.NullPointerException,
    :message
    "Cannot invoke \"clojure.lang.IObj.withMeta(clojure.lang.IPersistentMap)\" because \"x\" is null",
    :at [clojure.core$with_meta__5485 invokeStatic "core.clj" 220]}],
  :trace
  [[clojure.core$with_meta__5485 invokeStatic "core.clj" 220]
   [clojure.core$with_meta__5485 invoke "core.clj" 219]
   [clojure.tools.namespace.find$find_ns_decls_in_dir$fn__1437
    invoke
    "find.clj"
    93]
   [clojure.core$keep$fn__8649 invoke "core.clj" 7409]
   [clojure.lang.LazySeq sval "LazySeq.java" 42]
   [clojure.lang.LazySeq seq "LazySeq.java" 51]
   [clojure.lang.RT seq "RT.java" 535]
   [clojure.core$seq__5467 invokeStatic "core.clj" 139]
   [clojure.core$print_sequential invokeStatic "core_print.clj" 53]
   [clojure.core$fn__7391 invokeStatic "core_print.clj" 174]
   [clojure.core$fn__7391 invoke "core_print.clj" 174]
   [clojure.lang.MultiFn invoke "MultiFn.java" 234]
   [clojure.core$pr_on invokeStatic "core.clj" 3675]
   [clojure.core$pr invokeStatic "core.clj" 3678]
   [clojure.core$pr invoke "core.clj" 3678]
   [clojure.lang.AFn applyToHelper "AFn.java" 154]
   [clojure.lang.RestFn applyTo "RestFn.java" 132]
   [clojure.core$apply invokeStatic "core.clj" 667]
   [clojure.core$pr invokeStatic "core.clj" 3691]
   [clojure.core$pr doInvoke "core.clj" 3678]
   [clojure.lang.RestFn applyTo "RestFn.java" 139]
   [clojure.core$apply invokeStatic "core.clj" 667]
   [clojure.core$prn invokeStatic "core.clj" 3715]
   [clojure.core$prn doInvoke "core.clj" 3715]
   [clojure.lang.RestFn applyTo "RestFn.java" 137]
   [clojure.core$apply invokeStatic "core.clj" 667]
   [clojure.core$println invokeStatic "core.clj" 3734]
   [clojure.core$println doInvoke "core.clj" 3734]
   [clojure.lang.RestFn invoke "RestFn.java" 421]
   [user$eval1480 invokeStatic "test.clj" 4]
   [user$eval1480 invoke "test.clj" 4]
   [clojure.lang.Compiler eval "Compiler.java" 7194]
   [clojure.lang.Compiler load "Compiler.java" 7653]
   [clojure.lang.Compiler loadFile "Compiler.java" 7591]
   [clojure.main$load_script invokeStatic "main.clj" 475]
   [clojure.main$script_opt invokeStatic "main.clj" 535]
   [clojure.main$script_opt invoke "main.clj" 530]
   [clojure.main$main invokeStatic "main.clj" 664]
   [clojure.main$main doInvoke "main.clj" 616]
   [clojure.lang.RestFn applyTo "RestFn.java" 137]
   [clojure.lang.Var applyTo "Var.java" 705]
   [clojure.main main "main.java" 40]],
  :cause
  "Cannot invoke \"clojure.lang.IObj.withMeta(clojure.lang.IPersistentMap)\" because \"x\" is null",
  :phase :execution}}

Execution error (NullPointerException) at clojure.tools.namespace.find/find-ns-decls-in-dir$fn (find.clj:93).
Cannot invoke "clojure.lang.IObj.withMeta(clojure.lang.IPersistentMap)" because "x" is null

Usage Scenario

Cljdoc uses tools.namespace to find namespaces during API analysis.

Various libs include all sorts of weirdnesses including namespace-less sources.

Previous versions of tools.namespace handled this weirdness for us.

We can certainly work around this if it is decided that tools.namespace should not handle namespace-less sources.

closed with the note: Fixed in tools.namespace 1.4.2

1 Answer

0 votes
by

Thanks for this report. Created https://clojure.atlassian.net/browse/TNS-59

by
Thanks Fogus! I ticked your answer as "best answer", but then realized that might be misleading for an open issue.
...