You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Claim slice 6b: language-neutral qualified-call resolver, Elixir and Clojure #390
and an indexed f definition in my.app.util, the call should resolve to that definition rather than to unrelated bare-name candidates.
Scope
Generalise src/elixir_resolve.h's module table into one shared resolver driven by per-language alias/import/require facts. Preserve Elixir as an existing user and add Clojure as the first new user. Avoid a parallel Clojure-only module resolver or language-specific fallback guard.
Pin the supported Clojure namespace/require forms with fixtures before widening them; only indexed, statically established targets can supply resolved edges. Ambiguous, external, or unsupported forms must retain honest unresolved/declined behavior and disclosures.
The maintainers' slice 6a stops the wrong edge for an unplaced qualifier; 6b adds the right edge for a proven module target. Coordinate landing order with 6a and flip #349's retained str/join known-gap arm when the shared refusal lands. Implementing 6a is not claimed here.
Acceptance
Red-first alias/require and qualified-call fixtures, including same-name decoys, missing modules, and ambiguous targets.
The example above reaches my.app.util's f through the shared resolver.
Elixir's existing output stays byte-identical on the regression fixtures and before/after corpus comparisons.
No unrelated language's resolution changes; cold/warm cache parity, deterministic XML, and correct extraction-version / mirror / hash updates.
Document the supported namespace forms and remaining resolution limits.
Related: #349, the slice 5a/8a continuation #389, and the CONTRIBUTING feature-family map. Macro expansion, dynamic invocation, and protocol dispatch are outside this slice.
@tag-und-nacht thank you, and confirmed: slice 6b is yours. #349 laid the groundwork: Clojure writes a generic Reference::qualifier from the parser's namespace field, and the str/join gap is pinned as an arm, not guarded locally. Slice 6a (the refusal) is ours and not yet scheduled. 6b does not have to wait for it. Leave the str/join arm as it is until 6a flips it.
Where to start.ElixirResolver (src/elixir_resolve.h) has a Module::name table, Elixir-only fact readers (ElixirCallable/ElixirDefault/ElixirImport bindings, importsInForce), and an implicit-module fallback (Kernel). It has four consumers, and all of them must keep their output: buildGraph (graph.h), editcheck.h (the foldArity keying), mcpverbs.h and verbs_navigate.h. One possible seam:
structQualifiedCallLang { // per-language DATA, one constexpr row per Lang
Lang lang; bool foldsArity; std::string_view implicitModule; // "Kernel", "clojure.core"
};
structQualifiedCallTable { // shared; ElixirResolver becomes its first userQualifiedCallTable( const IngestResult&, std::span<const QualifiedCallLang> );
voidresolve( const Reference&, std::vector<NodeId>& out ) const; // empty = refuse, counted
};
Elixir expands aliases at ingest (aliasInForce, ingest_elixir.h), so the qualifier already holds a module by the time the resolver sees it. Doing the same for Clojure keeps the shared table free of Lang:: branches.
Pitfalls
A definition's namespace comes from the file's parsed (ns …) form, never from its path. Check whether Clojure symbols carry it today. A per-file module fact may be the first thing you need.
Aliases and requires: :as, :refer [..], :refer :all, :use, and require/alias outside ns. Pin each one you support. Anything else stays unresolved, and is counted, not laddered.
Labels come from parser nodes only. Namespaced keywords (::u/k, :a.b/k) are not calls. Interop like Math/abs names a class, so it resolves to nothing.
The implicit clojure.core row overlaps a shared builtin-name guard we plan separately: an unqualified (get m :a) binds an in-repo get, and Dart, Lua, Ruby and GDScript do the same. Please don't add a Clojure-only builtin list.
Evidence we'll ask for. Red-first arms in clojurecheck, plus elixirnamearitycheck, elixircheck, resolverhonestycheck, editcheckcheck, rustqualcheck and stdqualcheck green. Provide byte identity as in "Definition of done" item 2: the Elixir fixtures plus one real Elixir repository, covering --callers/--callees/--impact/--edit-check, plus one untouched-language repository, with the argv list and an empty diff. Update the family-6 map row and the "No language-neutral qualified-call resolver exists yet" sentence.
Activity
joyful-ii-V-I commentedon Oct 14, 2026
@tag-und-nacht thank you, and confirmed: slice 6b is yours. #349 laid the groundwork: Clojure writes a generic
Reference::qualifierfrom the parser'snamespacefield, and thestr/joingap is pinned as an arm, not guarded locally. Slice 6a (the refusal) is ours and not yet scheduled. 6b does not have to wait for it. Leave thestr/joinarm as it is until 6a flips it.Where to start.
ElixirResolver(src/elixir_resolve.h) has aModule::nametable, Elixir-only fact readers (ElixirCallable/ElixirDefault/ElixirImportbindings,importsInForce), and an implicit-module fallback (Kernel). It has four consumers, and all of them must keep their output:buildGraph(graph.h),editcheck.h(thefoldAritykeying),mcpverbs.handverbs_navigate.h. One possible seam:Elixir expands aliases at ingest (
aliasInForce,ingest_elixir.h), so the qualifier already holds a module by the time the resolver sees it. Doing the same for Clojure keeps the shared table free ofLang::branches.Pitfalls
(ns …)form, never from its path. Check whether Clojure symbols carry it today. A per-file module fact may be the first thing you need.:as,:refer [..],:refer :all,:use, andrequire/aliasoutsidens. Pin each one you support. Anything else stays unresolved, and is counted, not laddered.::u/k,:a.b/k) are not calls. Interop likeMath/absnames a class, so it resolves to nothing.clojure.corerow overlaps a shared builtin-name guard we plan separately: an unqualified(get m :a)binds an in-repoget, and Dart, Lua, Ruby and GDScript do the same. Please don't add a Clojure-only builtin list.Evidence we'll ask for. Red-first arms in
clojurecheck, pluselixirnamearitycheck,elixircheck,resolverhonestycheck,editcheckcheck,rustqualcheckandstdqualcheckgreen. Provide byte identity as in "Definition of done" item 2: the Elixir fixtures plus one real Elixir repository, covering--callers/--callees/--impact/--edit-check, plus one untouched-language repository, with the argv list and an empty diff. Update the family-6 map row and the "No language-neutral qualified-call resolver exists yet" sentence.