commit 29ce9f703ee411b789141afd83ebadc1c1910e84
parent 83e5dced4a175b582ee1cc56fc0479416678f05e
Author: Lou Woell <lou.woell@posteo.de>
Date:   Tue, 17 Feb 2026 11:53:43 +0100

[find-refs] find enums and enum members

Diffstat:
Mcmd/harehelper/+test/find_references.ha | 42++++++++++++++++++++++++++++++++++++++++--
Mcmd/harehelper/find_refs.ha | 65++++++++++++++++++++++++++++++++++++++---------------------------
Mcmd/harehelper/util.ha | 34++++++++++++++++++++++++++++++++++
3 files changed, 112 insertions(+), 29 deletions(-)

diff --git a/cmd/harehelper/+test/find_references.ha b/cmd/harehelper/+test/find_references.ha @@ -1,17 +1,18 @@ use os; +use os::{amode}; use hare::module; use path::{init}; use dirs; use fmt::*; use s = strings; use hare::parse; -use hare::unparse; +use up = hare::unparse; def this_file = "cmd/harehelper/+test/find_references.ha"; fn test_xrefs (refs: []xref) void = { for (let ref .. refs) { - let ref_id = unparse::identstr(ref.name); + let ref_id = up::identstr(ref.name); defer free(ref_id); errorfln("needle: {}, haystack: {}", ref_id, ref.context)!; @@ -99,3 +100,40 @@ fn test_scan_tree (id: str, in: str = "") void = { test_scan_tree("fmt::errorfln", this_file); }; + +@test fn scan_tree_stdlib_wildcard_enum () void = { + + let dummy: alignment = alignment::RIGHT; + + test_scan_tree("fmt::alignment", this_file); +}; + +@test fn scan_tree_stdlib_wildcard_enum_member () void = { + + test_scan_tree("fmt::alignment::RIGHT", this_file); +}; + +@test fn scan_tree_stdlib_alias_enum () void = { + + let dummy: up::synkind = up::synkind::IDENT; + + test_scan_tree("hare::unparse::synkind", this_file); +}; + +@test fn scan_tree_stdlib_alias_enum_member () void = { + + test_scan_tree("hare::unparse::synkind::IDENT", this_file); +}; + + +@test fn scan_tree_stdlib_member_enum () void = { + + let dummy: amode = amode::F_OK; + + test_scan_tree("os::amode", this_file); +}; + +@test fn scan_tree_stdlib_member_enum_member () void = { + + test_scan_tree("os::amode::F_OK", this_file); +}; diff --git a/cmd/harehelper/find_refs.ha b/cmd/harehelper/find_refs.ha @@ -12,8 +12,6 @@ // You should have received a copy of the GNU General Public License along with // this program. If not, see <https://www.gnu.org/licenses/>. -// TODO: Handle enums. - use bufio; use fmt; use fs; @@ -178,9 +176,9 @@ fn next_ref (lex: *lex::lexer) (xref | done | error) = { fn scan_file (path: str, id: ast::ident) ([]xref | error) = { const input = os::open(path)?; - defer io::close(input)!; +defer io::close(input)!; - const sc = bufio::newscanner(input); +const sc = bufio::newscanner(input); defer bufio::finish(&sc); const lexer = lex::init(&sc, path, lex::flag::COMMENTS); @@ -190,44 +188,57 @@ fn scan_file (path: str, id: ast::ident) ([]xref | error) = { const ns = ident_ns(id); let found = false; - let id_f: ast::ident = ["", ""]; - if (len(id) > 1) for :imps (let i .. imps) { - found = ast::ident_eq(ns, i.ident); - if (found) { + // A given id may have multiple names in any given context. To + // accommodate this we need to iterate over *all* imports and collect + // possible names. + let names: []ast::ident = []; + defer { + for (let i .. names) free(i); + free(names); + }; + + if (len(id) == 1) { + // ident_replace is here to uphold the memory contract of names, + // appending id directly would prohibit the cleanup defer above. + append(names, ident_replace([], [], id))!; + } else for :imps (let i .. imps) { + found = ident_in(i.ident, id); + if (found) { match (i.bindings) { - case void => - id_f = id[len(id)-2..]; + case void => void; + append(names, ident_replace(i.ident, ident_last(i.ident), id))!; case let a: ast::import_alias => - id_f[0] = a; - id_f[1] = ident_last(id)[0]; + append(names, ident_replace(i.ident, [a], id))!; case let members: ast::import_members => - id_f = ident_last(id); + let id_temp = ident_replace(i.ident, [], id); for (let m .. members) { - if(id_f[0] == m) break :imps; + if (m == id_temp[0]) { + append(names, id_temp)!; + continue :imps; + }; }; - continue :imps; + free(id_temp); case ast::import_wildcard => - id_f = ident_last(id); + append(names, ident_replace(i.ident, [], id))!; }; - - break; }; }; let res: []xref = []; for (let ref => next_ref(&lexer)?) { - if(ast::ident_eq(id_f, ref.name) || ast::ident_eq(id, ref.name)) { - ref.context = match (get_line(lexer.in.src, &ref)) { - case let e: error => - yield strerror(e); - case let s: str => - yield s; + for (let i .. names) { + if (ast::ident_eq(i, ref.name)) { + ref.context = match (get_line(lexer.in.src, &ref)) { + case let e: error => + yield strerror(e); + case let s: str => + yield s; + }; + + append(res, ref)!; }; - append(res, ref)!; - } else { - finish_xref(&ref); }; }; diff --git a/cmd/harehelper/util.ha b/cmd/harehelper/util.ha @@ -27,6 +27,40 @@ use strings; def HAREPATH = "."; +fn ident_in(ns: ast::ident, id: ast::ident) bool = { + + if (len(ns) > len(id)) return false; + + for (let i: size = 0; i < len(ns); i += 1) { + if (ns[i] != id[i]) return false; + }; + + return true; +}; + +fn ident_replace(old_ns: ast::ident, new_ns: ast::ident, id: ast::ident) ast::ident = { + + assert(ident_in(old_ns, id)); + + let i: size = 0; + + let new: ast::ident = []; + + for (i < len(old_ns); i += 1) { + if (id[i] != old_ns[i]) break; + }; + + for (let j: size = 0; j < len(new_ns); j += 1) { + append(new, new_ns[j])!; + }; + + for (let j: size = 0; j < len(id[i..]); j += 1) { + append(new, id[i+j])!; + }; + + return new; +}; + fn printident(i: ast::ident, trailing: bool = false) void = { unparse::ident(os::stdout, i)!; let trail = if (trailing) "::" else "";