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:
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 "";