commit 860bc8ef6b90263db70cbacbbdecc423dcca95dc
parent 7c19e7b72c5b33b641deaf68ae19266c308b6a73
Author: Sebastian <sebastian@sebsite.pw>
Date: Sat, 30 Sep 2023 23:36:52 -0400
haredoc: distinguish between modules and decls better
- If an identifier could refer to either a declaration or a module, the
declaration is shown, with a notice for tty that the identifier is
ambiguous.
- The identifier may have a trailing ::, in which case declarations are
never shown, only modules.
- In addition, parseident has been improved a bit, and also some memory
leaks were also fixed if they were nearby.
Signed-off-by: Sebastian <sebastian@sebsite.pw>
Diffstat:
3 files changed, 131 insertions(+), 74 deletions(-)
diff --git a/cmd/haredoc/doc/tty.ha b/cmd/haredoc/doc/tty.ha
@@ -24,6 +24,23 @@ export fn emit_tty(ctx: *context) (void | error) = {
};
const summary = ctx.summary;
+ if (ctx.ambiguous) {
+ const id = unparse::identstr(ctx.ident);
+ defer free(id);
+
+ if (!no_color) fmt::fprintf(ctx.out, "\x1b[{}m",
+ color(unparse::synkind::COMMENT))?;
+ fmt::fprint(ctx.out, "// ")?;
+ if (!no_color) fmt::fprint(ctx.out, "\x1b[93m")?;
+ fmt::fprint(ctx.out, "NOTE")?;
+ if (!no_color) fmt::fprintf(ctx.out, "\x1b[m" "\x1b[{}m",
+ color(unparse::synkind::COMMENT))?;
+ fmt::fprintf(ctx.out, ": {} also refers to module [[{}::]]",
+ id, id)?;
+ if (!no_color) fmt::fprint(ctx.out, "\x1b[m")?;
+ fmt::fprintln(ctx.out, "\n")?;
+ };
+
match (ctx.readme) {
case let readme: io::file =>
let rbuf: [os::BUFSZ]u8 = [0...];
diff --git a/cmd/haredoc/doc/types.ha b/cmd/haredoc/doc/types.ha
@@ -51,7 +51,7 @@ export type context = struct {
mctx: *module::context,
ident: ast::ident,
tags: []str,
- modpath: str,
+ ambiguous: bool,
srcs: module::srcset,
submods: []str,
summary: summary,
diff --git a/cmd/haredoc/main.ha b/cmd/haredoc/main.ha
@@ -93,92 +93,130 @@ fn doc(name: str, cmd: *getopt::command) (void | error) = {
fmt::fatal("Option -a must be used only with -Fhare or -Ftty");
};
- let decls: []ast::decl = [];
- defer free(decls);
-
let ctx = module::context {
harepath = harepath(),
harecache = harecache(),
tags = tags,
};
- let decl = "";
- let (modpath, srcs, id) = if (len(cmd.args) == 0) {
- let (modpath, srcs) = module::find(&ctx, []: ast::ident)?;
- yield (modpath, srcs, []: ast::ident);
+ let declpath = "";
+ defer free(declpath);
+ let declsrcs = module::srcset { ... };
+ defer module::finish_srcset(&declsrcs);
+ let modpath = "";
+ defer free(modpath);
+ let modsrcs = module::srcset { ... };
+ defer module::finish_srcset(&modsrcs);
+ let id: ast::ident = [];
+ defer free(id);
+
+ if (len(cmd.args) == 0) {
+ let (p, s) = module::find(&ctx, []: ast::ident)?;
+ modpath = strings::dup(p);
+ modsrcs = s;
} else match (parseident(cmd.args[0])) {
- case let id: ast::ident =>
- // first assume it's a module
- yield match (module::find(&ctx, id)) {
- case let r: (str, module::srcset) =>
- yield (r.0, r.1, id);
+ case let ident: (ast::ident, bool) =>
+ id = ident.0;
+ const trailing = ident.1;
+ if (!trailing) {
+ // check if it's an ident inside a module
+ match (module::find(&ctx, id[..len(id)-1])) {
+ case let s: (str, module::srcset) =>
+ declpath = strings::dup(s.0);
+ declsrcs = s.1;
+ case let e: module::error =>
+ module::finish_error(e);
+ };
+ };
+ // check if it's a module
+ match (module::find(&ctx, id)) {
+ case let s: (str, module::srcset) =>
+ modpath = strings::dup(s.0);
+ modsrcs = s.1;
case let e: module::error =>
module::finish_error(e);
- // then assume it's an ident inside a module
- decl = id[len(id)-1];
- id = id[..len(id)-1];
- let (modpath, srcs) = module::find(&ctx, id)?;
- yield (modpath, srcs, id);
+ if (declpath == "") {
+ const id = unparse::identstr(id);
+ fmt::fatalf("Could not find {}{}", id,
+ if (trailing) "::" else "");
+ };
};
- case =>
- let buf = path::buffer { ... };
- path::set(&buf, cmd.args[0])?;
- let (modpath, srcs) = module::find(&ctx, &buf)?;
- yield (modpath, srcs, []: ast::ident);
- };
-
- for (let i = 0z; i < len(srcs.ha); i += 1) {
- let u = doc::scan(srcs.ha[i])?;
- ast::imports_finish(u.imports);
- append(decls, u.decls...);
+ case void =>
+ let buf = path::init(cmd.args[0])?;
+ let (p, s) = module::find(&ctx, &buf)?;
+ modpath = strings::dup(p);
+ modsrcs = s;
};
- const rpath = path::init(modpath, "README")!;
- const readme: (io::file | void) = if (decl == "") {
- yield match (os::open(path::string(&rpath))) {
- case let err: fs::error =>
- yield void;
- case let f: io::file =>
- yield f;
+ let decls: []ast::decl = [];
+ defer {
+ for (let i = 0z; i < len(decls); i += 1) {
+ ast::decl_finish(decls[i]);
};
- } else void;
-
- defer match (readme) {
- case void => void;
- case let f: io::file =>
- io::close(f)!;
+ free(decls);
};
- if (decl != "") {
+ if (declpath != "") {
+ for (let i = 0z; i < len(declsrcs.ha); i += 1) {
+ let u = doc::scan(declsrcs.ha[i])?;
+ ast::imports_finish(u.imports);
+ append(decls, u.decls...);
+ };
let new: []ast::decl = [];
for (let i = 0z; i < len(decls); i += 1) {
- if (has_decl(decls[i], decl)) {
+ if (has_decl(decls[i], id[len(id) - 1])) {
append(new, decls[i]);
} else {
ast::decl_finish(decls[i]);
};
};
- if (len(new) == 0) {
- fmt::fatalf("Could not find {}::{}",
- unparse::identstr(id), decl);
- };
free(decls);
decls = new;
+ if (len(new) == 0) {
+ if (modpath == "") {
+ const id = unparse::identstr(id);
+ fmt::fatalf("Could not find {}", id);
+ };
+ } else {
+ show_undocumented = true;
+ };
+ };
- show_undocumented = true;
+ let readme: (io::file | void) = void;
+ defer match (readme) {
+ case void => void;
+ case let f: io::file =>
+ io::close(f)!;
};
- defer for (let i = 0z; i < len(decls); i += 1) {
- ast::decl_finish(decls[i]);
+ const ambiguous = modpath != "" && len(decls) > 0;
+
+ if (len(decls) == 0) {
+ for (let i = 0z; i < len(modsrcs.ha); i += 1) {
+ let u = doc::scan(modsrcs.ha[i])?;
+ ast::imports_finish(u.imports);
+ append(decls, u.decls...);
+ };
+
+ const rpath = path::init(modpath, "README")!;
+ match (os::open(path::string(&rpath))) {
+ case let f: io::file =>
+ readme = f;
+ case fs::error => void;
+ };
};
+ const submods: []str = if (!ambiguous && modpath != "") {
+ yield doc::submodules(modpath)?;
+ } else [];
+ const srcs = if (!ambiguous && modpath != "") modsrcs else declsrcs;
const ctx = doc::context {
mctx = &ctx,
ident = id,
tags = tags,
- modpath = modpath,
+ ambiguous = ambiguous,
srcs = srcs,
- submods = if (decl == "") doc::submodules(modpath)? else [],
+ submods = submods,
summary = doc::sort_decls(decls),
format = fmt,
template = template,
@@ -208,30 +246,31 @@ fn doc(name: str, cmd: *getopt::command) (void | error) = {
// converted to strings and there must be no trailing tokens that don't belong
// to the ident in the string. For example, this function will parse `rt::abort`
// as a valid identifier.
-fn parseident(in: str) (ast::ident | parse::error) = {
+fn parseident(in: str) ((ast::ident, bool) | void) = {
const buf = memio::fixed(strings::toutf8(in));
const lexer = lex::init(&buf, "<string>");
defer lex::finish(&lexer);
- // XXX: errdefer
let success = false;
let ident: ast::ident = [];
defer if (!success) ast::ident_free(ident);
+ let trailing = false;
let z = 0z;
for (true) {
- const tok = lex::lex(&lexer)?;
+ const tok = lex::lex(&lexer)!;
const name = if (tok.0 == lex::ltok::NAME) {
yield tok.1 as str;
} else if (tok.0 < lex::ltok::LAST_KEYWORD) {
yield strings::dup(lex::tokstr(tok));
+ } else if (tok.0 == lex::ltok::EOF && len(ident) > 0) {
+ trailing = true;
+ break;
} else {
lex::unlex(&lexer, tok);
- const loc = lex::mkloc(&lexer);
- const why = "Unexpected trailing :: in ident";
- return (loc, why): lex::syntax: parse::error;
+ return;
};
append(ident, name);
z += len(name);
- const tok = lex::lex(&lexer)?;
+ const tok = lex::lex(&lexer)!;
switch (tok.0) {
case lex::ltok::EOF =>
break;
@@ -239,19 +278,14 @@ fn parseident(in: str) (ast::ident | parse::error) = {
z += 1;
case =>
lex::unlex(&lexer, tok);
- const loc = lex::mkloc(&lexer);
- const why = fmt::asprintf("Unexpected '{}' in ident",
- lex::tokstr(tok));
- return (loc, why): lex::syntax: parse::error;
+ return;
};
};
if (z > ast::IDENT_MAX) {
- const loc = lex::mkloc(&lexer);
- const why = "Identifier exceeds maximum length";
- return (loc, why): lex::syntax: parse::error;
+ return;
};
success = true;
- return ident;
+ return (ident, trailing);
};
fn init_tty(ctx: *doc::context) io::handle = {
@@ -333,10 +367,16 @@ fn emit(ctx: *doc::context) (void | error) = {
};
@test fn parseident() void = {
- assert(ast::ident_eq(parseident("hare::lex") as ast::ident,
- ["hare", "lex"]));
- assert(ast::ident_eq(parseident("rt::abort") as ast::ident,
- ["rt", "abort"]));
- assert(parseident("strings::dup*{}&@") is parse::error);
- assert(parseident("foo::bar::") is parse::error);
+ let (ident, trailing) = parseident("hare::lex") as (ast::ident, bool);
+ assert(ast::ident_eq(ident, ["hare", "lex"]));
+ assert(!trailing);
+ let (ident, trailing) = parseident("rt::abort") as (ast::ident, bool);
+ assert(ast::ident_eq(ident, ["rt", "abort"]));
+ assert(!trailing);
+ let (ident, trailing) = parseident("foo::bar::") as (ast::ident, bool);
+ assert(ast::ident_eq(ident, ["foo", "bar"]));
+ assert(trailing);
+ assert(parseident("strings::dup*{}&@") is void);
+ assert(parseident("") is void);
+ assert(parseident("::") is void);
};