commit f40e6746b23ec54e1db13af3dabc8f3594a1113f
parent b3f3605b8147b052944563e991d16437f2c45529
Author: Lou Woell <lou.woell@posteo.de>
Date:   Thu,  9 Oct 2025 19:54:28 +0200

[harehelper] more robust error handling

Diffstat:
Mcmd/harehelper/+test/find_references.ha | 5++---
Mcmd/harehelper/+test/list_test.ha | 9++++++++-
Mcmd/harehelper/find_refs.ha | 45++++++++++++++++++++++++---------------------
Mcmd/harehelper/list_modules.ha | 6+++---
Mcmd/harehelper/list_symbols.ha | 22++++++++++++----------
Mcmd/harehelper/locate.ha | 10++++++----
Mcmd/harehelper/resolve.ha | 33+++++++++++++++++++++------------
Mcmd/harehelper/util.ha | 32++++++++++++++++++++++++--------
8 files changed, 100 insertions(+), 62 deletions(-)

diff --git a/cmd/harehelper/+test/find_references.ha b/cmd/harehelper/+test/find_references.ha @@ -14,7 +14,7 @@ fn test_xrefs (refs: []xref) void = { let ref_id = unparse::identstr(ref.name); defer free(ref_id); - errorf("needle: {}, haystack: {}", ref_id, ref.context)!; + errorfln("needle: {}, haystack: {}", ref_id, ref.context)!; assert(ref.context != "" && s::contains(ref.context, ref_id)); }; }; @@ -51,8 +51,7 @@ fn test_scan_tree (id: str, in: str = "") void = { }; }; - if (in != "") assert(false); - + if (in != "") assert(false, "id not found in file"); }; diff --git a/cmd/harehelper/+test/list_test.ha b/cmd/harehelper/+test/list_test.ha @@ -1,4 +1,6 @@ use dirs; +use fmt; +use hare::ast; use hare::module; use hare::parse; use os; @@ -15,7 +17,12 @@ use os; tags = ["linux"], }; - let res = list_symbols(&ctx, parse::identstr("test")!); + let res = match (list_symbols(&ctx, parse::identstr("test")!)) { + case let e: error => + fmt::fatalf("Error: {}", strerror(e)); + case let l: []ast::ident => + yield l; + }; defer idents_finish(res); assert(len(res) == 3); diff --git a/cmd/harehelper/find_refs.ha b/cmd/harehelper/find_refs.ha @@ -50,16 +50,16 @@ fn print_ref (ref: xref) void = { // Gets whole line specified by [[hare::lex::location]] from [[io::handle]]. // Return value must be freed by caller. -fn get_line (file: io::handle, loc: *lex::location) (str | io::error) = { +fn get_line (file: io::handle, loc: *lex::location) (str | error) = { let off = loc.off; - const o_off = io::tell(file)!; + const o_off = io::tell(file)?; // This is slow. // TODO: figure out a way to get the line from the line/column info. // Problem: the lexer counts '\t' as 8 columns. for (true) { - io::seek(file, off, io::whence::SET)!; + io::seek(file, off, io::whence::SET)?; let r = bufio::read_rune(file); if('\n' == r) break; off -= 1; @@ -67,12 +67,12 @@ fn get_line (file: io::handle, loc: *lex::location) (str | io::error) = { let result = match (bufio::read_line(file)?) { case let b: []u8 => - yield strings::fromutf8(b)!; + yield strings::fromutf8(b)?; case io::EOF => yield ""; }; - io::seek(file, o_off, io::whence::SET)!; + io::seek(file, o_off, io::whence::SET)?; return result; }; @@ -107,7 +107,7 @@ fn find_refs (cmd: getopt::command, ctx: *module::context) void = { fn scan_tree (ctx: *module::context, path: *path::buffer, id: ast::ident) []fileref = { let mods: []module::module = []; -defer module::free_slice(mods); + defer module::free_slice(mods); let err = module::gather(ctx, &mods, path, false); let err = module::gather_submodules(ctx, &mods, path, false); @@ -125,7 +125,10 @@ defer module::free_slice(mods); for (let s &.. mod.srcs.ha) { let file_res = match (scan_file(*s, id_c)) { - case void => continue; + case let e: error => + fmt::errorfln ("Error scanning {}: {}", *s, strerror(e))!; + // Skip file + continue; case let r: []xref => yield r; }; @@ -146,12 +149,12 @@ defer module::free_slice(mods); return res; }; -// Returns [[[xref]]] for next identifier in [[hare::lex::lexer]]. Return value +// Returns [[xref]] for next identifier in [[hare::lex::lexer]]. Return value // must be freed with [[finish_xref]]. -fn next_ref (lex: *lex::lexer) (xref | done | lex::error | parse::error) = { +fn next_ref (lex: *lex::lexer) (xref | done | error) = { for (true) { - + const tok = lex::lex(lex)?; switch (tok.0) { @@ -172,22 +175,17 @@ fn next_ref (lex: *lex::lexer) (xref | done | lex::error | parse::error) = { }; }; -fn scan_file (path: str, id: ast::ident) ([]xref | void)= { +fn scan_file (path: str, id: ast::ident) ([]xref | error) = { - const input = match (os::open(path)) { - case let f: io::file => - yield f; - case let err: fs::error => - fmt::errorf("Error reading {}: {}", path, fs::strerror(err))!; - return void; - }; + const input = os::open(path)?; defer io::close(input)!; + const sc = bufio::newscanner(input); defer bufio::finish(&sc); const lexer = lex::init(&sc, path, lex::flag::COMMENTS); - const imps = parse::imports(&lexer)!; + const imps = parse::imports(&lexer)?; defer ast::imports_finish(imps); const ns = ident_ns(id); @@ -219,9 +217,14 @@ fn scan_file (path: str, id: ast::ident) ([]xref | void)= { }; let res: []xref = []; - for (let ref => next_ref(&lexer)!) { + for (let ref => next_ref(&lexer)?) { if(ast::ident_eq(id_f, ref.name) || ast::ident_eq(id, ref.name)) { - ref.context = get_line(lexer.in.src, &ref)!; + 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)!; } else { finish_xref(&ref); diff --git a/cmd/harehelper/list_modules.ha b/cmd/harehelper/list_modules.ha @@ -32,9 +32,9 @@ fn get_modules (ctx: *module::context) []module::module = { let r = module::gather_submodules(ctx, &res, &mod, true); match (r) { - case let e: hare::module::error => - fmt::errorln(module::strerror(e))!; - case => void; + case let e: module::error => + fmt::errorln(strerror(e))!; + case => void; }; }; diff --git a/cmd/harehelper/list_symbols.ha b/cmd/harehelper/list_symbols.ha @@ -41,7 +41,12 @@ fn list (cmd: getopt::command, ctx: *module::context) void = { }; }; - let list = list_symbols(ctx, id, ls_unexported); + let list = match (list_symbols(ctx, id, ls_unexported)) { + case let e: error => + fmt::fatal(strerror(e)); + case let l: []ast::ident => + yield l; + }; defer idents_finish(list); for (let i .. list) { @@ -53,21 +58,18 @@ fn list_symbols ( ctx: *module::context, id: module::location, unexp: bool = false, -) []ast::ident = { +) ([]ast::ident | error) = { - let (path, src) = match(module::find(ctx, id)){ - case let e: module::error => - fmt::fatal(module::strerror(e)); - case let res: (str, module::srcset) => - yield res; - }; + let (path, src) = module::find(ctx, id)?; let res: []ast::ident = []; for (let s .. src.ha) { let decls = match (scan(s)) { - case let e: parse::error => - fmt::fatal(parse::strerror(e)); + case let e: error => + fmt::errorfln("Error reading {}: {}", s, strerror(e))!; + // skip file. + continue; case let d: []ast::decl => yield d; }; diff --git a/cmd/harehelper/locate.ha b/cmd/harehelper/locate.ha @@ -27,8 +27,8 @@ fn locate (cmd: command, ctx: *module::context) void = { if(len(cmd.args) == 0) fmt::fatal("Expected Argument"); let (id, trailing) = match (identstr_trailing(cmd.args[0])) { - case parse::error => - return void; + case let e: parse::error => + fmt::fatal(parse::strerror(e)); case let r: (ast::ident, bool) => yield r; }; @@ -99,8 +99,10 @@ fn locate_symbol ( for (let s .. src.ha) { let decls = match (scan(s)) { - case let e: parse::error => - fmt::fatal(parse::strerror(e)); + case let e: error => + fmt::errorfln("Error reading {}: {}", s, strerror(e))!; + // skip file. + continue; case let d: []ast::decl => yield d; }; diff --git a/cmd/harehelper/resolve.ha b/cmd/harehelper/resolve.ha @@ -49,6 +49,8 @@ fn resolve(cmd: getopt::command, ctx: *module::context) void = { case let id_exp: ast::ident => printident(id_exp, trailing); ast::ident_free(id_exp); + case let e: error => + fmt::errorfln("Error: {}", strerror(e))!; case => printident(id, trailing); }; @@ -60,7 +62,7 @@ fn resolve_local ( ctx: *module::context, id: ast::ident, file: str, -) (ast::ident | void) = { +) (ast::ident | void | error) = { let hp = strings::split(ctx.harepath, ":")!; defer free(hp); @@ -91,20 +93,15 @@ fn resolve_local ( return void; }; -fn list_imports (path: str) []ast::import = { +fn list_imports (path: str) ([]ast::import | error) = { - const input = match (os::open(path)) { - case let f: io::file => - yield f; - case let err: fs::error => - fmt::fatalf("Error reading {}: {}", path, fs::strerror(err)); - }; + const input = os::open(path)?; defer io::close(input)!; let sc = bufio::newscanner(input); defer bufio::finish(&sc); let lexer = lex::init(&sc, path, lex::flag::NONE); - return parse::imports(&lexer)!; + return parse::imports(&lexer)?; }; // Resolves id based on imports in file at path. Returns void when id is not @@ -113,9 +110,14 @@ fn resolve_uid ( ctx: *module::context, id: ast::ident, path: str -) (void | ast::ident) = { +) (void | ast::ident | error) = { - let imports = list_imports(path); + let imports = match (list_imports(path)) { + case let e: error => + fmt::fatalf("Error: {}", strerror(e)); + case let l: []ast::import => + yield l; + }; defer ast::imports_finish(imports); for (let i .. imports) { @@ -150,7 +152,14 @@ fn resolve_uid ( // if len(id) > 1 -> id is not an interned // symbol. if (len(id) > 1) yield :binding; - let list = list_symbols(ctx, ident); + let list = match (list_symbols(ctx, ident)) { + case let e: error => + fmt::errorfln("Error: {}", strerror(e))!; + // skip module. + yield :binding; + case let l: []ast::ident => + yield l; + }; defer idents_finish(list); for (let s .. list) { diff --git a/cmd/harehelper/util.ha b/cmd/harehelper/util.ha @@ -14,6 +14,7 @@ use ascii; use bufio; +use encoding::utf8; use fmt; use fs; use hare::ast; @@ -21,8 +22,8 @@ use hare::lex; use hare::module; use hare::parse; use hare::unparse; -use memio; use io; +use memio; use os; use strings; @@ -45,14 +46,29 @@ fn ident_ns(id: ast::ident) ast::ident = { if (l > 1) return id[..l - 1] else return id; }; -fn scan(path: str) ([]ast::decl | parse::error) = { - // This function is copied from haredoc (GPL-3.0-only) (c) Hare authors; - const input = match (os::open(path)) { - case let f: io::file => - yield f; - case let err: fs::error => - fmt::fatalf("Error reading {}: {}", path, fs::strerror(err)); +type error = !(parse::error | lex::error | fs::error | module::error | io::error | + utf8::invalid); + +fn strerror (e: error) const str = { + match (e) { + case let e: parse::error => + return parse::strerror(e); + case let e: lex::error => + return lex::strerror(e); + case let e: fs::error => + return fs::strerror(e); + case let e: module::error => + return module::strerror(e); + case let e: io::error => + return io::strerror(e); + case => + return "unknown error"; }; +}; + +fn scan(path: str) ([]ast::decl | error) = { + // This function is copied from haredoc (GPL-3.0-only) (c) Hare authors; + const input = os::open(path)?; defer io::close(input)!; let sc = bufio::newscanner(input); defer bufio::finish(&sc);