Files
spl/stage1/spl_diag.c
T

368 lines
11 KiB
C

#include "spl_diag.h"
#include "../stage0/include/color.h"
#include <stdarg.h>
static const char *const g_kind_names[] = {
[SPL_DIAG_KIND_NONE] = "none",
[SPL_DIAG_KIND_LEX_UNEXPECTED] = "lex_unexpected",
[SPL_DIAG_KIND_PARSE_EXPECTED] = "parse_expected",
[SPL_DIAG_KIND_PARSE_UNRECOGNIZED] = "parse_unrecognized",
[SPL_DIAG_KIND_UNDEFINED_SYMBOL] = "undefined_symbol",
[SPL_DIAG_KIND_DUPLICATE_SYMBOL] = "duplicate_symbol",
[SPL_DIAG_KIND_TYPE_MISMATCH] = "type_mismatch",
[SPL_DIAG_KIND_ARG_COUNT_MISMATCH] = "arg_count_mismatch",
[SPL_DIAG_KIND_NOT_A_FUNCTION] = "not_a_function",
[SPL_DIAG_KIND_NO_FIELD] = "no_field",
[SPL_DIAG_KIND_NO_METHOD] = "no_method",
[SPL_DIAG_KIND_INDEX_BASE_INVALID] = "index_base_invalid",
[SPL_DIAG_KIND_TUPLE_INDEX_OOB] = "tuple_index_oob",
[SPL_DIAG_KIND_FITS_OVERFLOW] = "fits_overflow",
[SPL_DIAG_KIND_CIRCULAR_DEP] = "circular_dep",
[SPL_DIAG_KIND_BREAK_OUTSIDE_LOOP] = "break_outside_loop",
[SPL_DIAG_KIND_CONTINUE_OUTSIDE_LOOP] = "continue_outside_loop",
[SPL_DIAG_KIND_UNDEFINED_LABEL] = "undefined_label",
[SPL_DIAG_KIND_CAST_INVALID] = "cast_invalid",
[SPL_DIAG_KIND_UNKNOWN_ATTR] = "unknown_attr",
[SPL_DIAG_KIND_UNKNOWN_INTRIN] = "unknown_intrin",
[SPL_DIAG_KIND_INTRIN_ARG_COUNT] = "intrin_arg_count",
[SPL_DIAG_KIND_VAR_UNINITIALIZED] = "var_uninitialized",
[SPL_DIAG_KIND_CANNOT_INFER] = "cannot_infer",
[SPL_DIAG_KIND_MUST_RETURN_VALUE] = "must_return_value",
[SPL_DIAG_KIND_UNHANDLED_NODE] = "unhandled_node",
[SPL_DIAG_KIND_IMPLICIT_CAST_FAIL] = "implicit_cast_fail",
[SPL_DIAG_KIND_NO_LVALUE] = "no_lvalue",
[SPL_DIAG_KIND_BAD_FIELD_INDEX] = "bad_field_index",
[SPL_DIAG_KIND_BAD_BLOCK] = "bad_block",
[SPL_DIAG_KIND_IR_UNKNOWN_KIND] = "ir_unknown_kind",
[SPL_DIAG_KIND_FRAME_OVERFLOW] = "frame_overflow",
[SPL_DIAG_KIND_INTERNAL] = "internal",
};
const char *spl_diag_kind_name(spl_diag_kind_t k) {
if ((usize)k < sizeof(g_kind_names) / sizeof(g_kind_names[0]) && g_kind_names[k])
return g_kind_names[k];
return "unknown";
}
static const char *const g_stage_names[] = {
[SPL_STAGE_NONE] = "none", [SPL_STAGE_LEX] = "lex", [SPL_STAGE_PARSE] = "parse",
[SPL_STAGE_SEMA] = "sema", [SPL_STAGE_AST2IR] = "ast2ir", [SPL_STAGE_IR2VM] = "ir2vm",
[SPL_STAGE_DRIVER] = "driver",
};
const char *spl_diag_stage_name(spl_diag_stage_t s) {
if ((usize)s < sizeof(g_stage_names) / sizeof(g_stage_names[0]) && g_stage_names[s])
return g_stage_names[s];
return "?";
}
const char *spl_diag_level_name(spl_diag_level_t l) {
switch (l) {
case SPL_DIAG_LEVEL_NOTE:
return "note";
case SPL_DIAG_LEVEL_WARN:
return "warn";
case SPL_DIAG_LEVEL_ERROR:
return "error";
case SPL_DIAG_LEVEL_FATAL:
return "fatal";
}
return "?";
}
static const char *level_color(spl_diag_level_t l) {
switch (l) {
case SPL_DIAG_LEVEL_NOTE:
return ANSI_FG_CYAN;
case SPL_DIAG_LEVEL_WARN:
return ANSI_FG_YELLOW;
case SPL_DIAG_LEVEL_ERROR:
return ANSI_FG_RED;
case SPL_DIAG_LEVEL_FATAL:
return ANSI_FG_RED ANSI_UNDERLINED;
}
return ANSI_NONE;
}
static char *diag_strdup(const char *s) {
if (!s)
return NULL;
usize n = strlen(s);
char *r = malloc(n + 1);
if (!r)
return NULL;
memcpy(r, s, n);
r[n] = '\0';
return r;
}
void spl_diag_init(spl_diag_t *d, void *fout) {
memset(d, 0, sizeof *d);
vec_init(d->ctx);
vec_init(d->records);
d->collect = true;
d->fout = fout;
d->color = true;
d->stage = SPL_STAGE_NONE;
}
static void frame_free(spl_diag_frame_t *f) {
free(f->fn_name);
free(f->label);
f->fn_name = NULL;
f->label = NULL;
}
static void record_free(spl_diag_record_t *r) {
free(r->message);
r->message = NULL;
for (usize i = 0; i < vec_size(r->ctx); ++i)
frame_free(&vec_at(r->ctx, i));
vec_free(r->ctx);
for (usize i = 0; i < vec_size(r->notes); ++i)
free(vec_at(r->notes, i).message);
vec_free(r->notes);
}
void spl_diag_reset(spl_diag_t *d) {
for (usize i = 0; i < vec_size(d->ctx); ++i)
frame_free(&vec_at(d->ctx, i));
d->ctx.size = 0;
for (usize i = 0; i < vec_size(d->records); ++i)
record_free(&vec_at(d->records, i));
d->records.size = 0;
d->num_printed = 0;
d->n_errors = 0;
d->n_warns = 0;
d->n_fatals = 0;
}
void spl_diag_drop(spl_diag_t *d) {
spl_diag_reset(d);
vec_free(d->ctx);
vec_free(d->records);
}
void spl_diag_set_stage(spl_diag_t *d, spl_diag_stage_t stage) { d->stage = stage; }
void spl_diag_set_output(spl_diag_t *d, FILE *out) { d->fout = out; }
void spl_diag_set_color(spl_diag_t *d, bool enabled) { d->color = enabled; }
void spl_diag_set_collect(spl_diag_t *d, bool enabled) { d->collect = enabled; }
void spl_diag_push(spl_diag_t *d, const spl_dbg_node_t *loc, const char *fn_name,
const char *label) {
spl_diag_frame_t f = {0};
if (loc)
f.loc = *loc;
f.fn_name = fn_name ? diag_strdup(fn_name) : NULL;
f.label = label ? diag_strdup(label) : NULL;
vec_push(d->ctx, f);
}
void spl_diag_pop(spl_diag_t *d) {
if (vec_size(d->ctx) == 0)
return;
frame_free(&vec_at(d->ctx, vec_size(d->ctx) - 1));
d->ctx.size--;
}
usize spl_diag_depth(const spl_diag_t *d) { return vec_size(d->ctx); }
static spl_diag_frame_vec_t ctx_snapshot(const spl_diag_t *d) {
spl_diag_frame_vec_t snap;
vec_init(snap);
for (usize i = 0; i < vec_size(d->ctx); ++i) {
spl_diag_frame_t *src = &vec_at(d->ctx, i);
spl_diag_frame_t f = {0};
f.loc = src->loc;
f.fn_name = src->fn_name ? diag_strdup(src->fn_name) : NULL;
f.label = src->label ? diag_strdup(src->label) : NULL;
vec_push(snap, f);
}
return snap;
}
static void frame_desc(const spl_diag_frame_t *f, char *buf, usize cap) {
if (f->fn_name && f->label)
snprintf(buf, cap, "%s %s", f->label, f->fn_name);
else if (f->fn_name)
snprintf(buf, cap, "%s", f->fn_name);
else if (f->label)
snprintf(buf, cap, "%s", f->label);
else
snprintf(buf, cap, "?");
}
static void print_loc(FILE *out, const spl_dbg_node_t *loc) {
if (loc && loc->fname)
fprintf(out, "%s:%d:%d", loc->fname, loc->line, loc->col);
else
fprintf(out, "<no-loc>");
}
static void print_record(const spl_diag_t *d, const spl_diag_record_t *r) {
FILE *out = (FILE *)d->fout;
if (!out)
return;
const char *col = d->color ? level_color(r->level) : "";
const char *rst = d->color ? ANSI_NONE : "";
fprintf(out, "%s%s[%s:%s]%s: ", col, spl_diag_level_name(r->level),
spl_diag_stage_name(r->stage), spl_diag_kind_name(r->kind), rst);
print_loc(out, &r->loc);
fputc('\n', out);
fprintf(out, " %s\n", r->message ? r->message : "");
for (usize i = vec_size(r->ctx); i > 0; --i) {
char buf[256];
frame_desc(&vec_at(r->ctx, i - 1), buf, sizeof buf);
fprintf(out, " in %s at ", buf);
print_loc(out, &vec_at(r->ctx, i - 1).loc);
fputc('\n', out);
}
for (usize i = 0; i < vec_size(r->notes); ++i) {
spl_diag_note_t *n = &vec_at(r->notes, i);
const char *ncol = d->color ? ANSI_FG_CYAN : "";
fprintf(out, "%snote%s: ", ncol, rst);
print_loc(out, &n->loc);
fputc('\n', out);
fprintf(out, " %s\n", n->message ? n->message : "");
}
}
static void emit(spl_diag_t *d, spl_diag_level_t level, spl_diag_kind_t kind,
const spl_dbg_node_t *loc, const char *fmt, va_list ap) {
va_list ap2;
va_copy(ap2, ap);
int need = vsnprintf(NULL, 0, fmt, ap);
if (need < 0)
need = 0;
char *msg = malloc((usize)need + 1);
if (msg)
vsnprintf(msg, (usize)need + 1, fmt, ap2);
va_end(ap2);
spl_diag_record_t rec = {0};
rec.level = level;
rec.stage = d->stage;
rec.kind = kind;
if (loc)
rec.loc = *loc;
rec.message = msg;
rec.ctx = ctx_snapshot(d);
vec_init(rec.notes);
if (level == SPL_DIAG_LEVEL_ERROR)
d->n_errors++;
else if (level == SPL_DIAG_LEVEL_FATAL) {
d->n_errors++;
d->n_fatals++;
} else if (level == SPL_DIAG_LEVEL_WARN)
d->n_warns++;
if (d->fout) {
print_record(d, &rec);
d->num_printed++;
}
if (d->collect)
vec_push(d->records, rec);
else
record_free(&rec);
}
void spl_diag_note(spl_diag_t *d, spl_diag_kind_t kind, const spl_dbg_node_t *loc, const char *fmt,
...) {
va_list ap;
va_start(ap, fmt);
emit(d, SPL_DIAG_LEVEL_NOTE, kind, loc, fmt, ap);
va_end(ap);
}
void spl_diag_warn(spl_diag_t *d, spl_diag_kind_t kind, const spl_dbg_node_t *loc, const char *fmt,
...) {
va_list ap;
va_start(ap, fmt);
emit(d, SPL_DIAG_LEVEL_WARN, kind, loc, fmt, ap);
va_end(ap);
}
void spl_diag_error(spl_diag_t *d, spl_diag_kind_t kind, const spl_dbg_node_t *loc, const char *fmt,
...) {
va_list ap;
va_start(ap, fmt);
emit(d, SPL_DIAG_LEVEL_ERROR, kind, loc, fmt, ap);
va_end(ap);
}
void spl_diag_fatal(spl_diag_t *d, spl_diag_kind_t kind, const spl_dbg_node_t *loc, const char *fmt,
...) {
va_list ap;
va_start(ap, fmt);
emit(d, SPL_DIAG_LEVEL_FATAL, kind, loc, fmt, ap);
va_end(ap);
}
void spl_diag_attach_note(spl_diag_t *d, const spl_dbg_node_t *loc, const char *fmt, ...) {
if (vec_size(d->records) == 0)
return;
spl_diag_record_t *last = &vec_at(d->records, vec_size(d->records) - 1);
va_list ap;
va_start(ap, fmt);
int need = vsnprintf(NULL, 0, fmt, ap);
va_end(ap);
if (need < 0)
need = 0;
char *msg = malloc((usize)need + 1);
if (msg) {
va_start(ap, fmt);
vsnprintf(msg, (usize)need + 1, fmt, ap);
va_end(ap);
}
spl_diag_note_t n = {0};
if (loc)
n.loc = *loc;
n.message = msg;
vec_push(last->notes, n);
}
bool spl_diag_has_error(const spl_diag_t *d) { return d->n_errors > 0; }
bool spl_diag_has_fatal(const spl_diag_t *d) { return d->n_fatals > 0; }
usize spl_diag_error_count(const spl_diag_t *d) { return d->n_errors; }
usize spl_diag_warn_count(const spl_diag_t *d) { return d->n_warns; }
void spl_diag_summary(const spl_diag_t *d) {
FILE *out = (FILE *)d->fout;
if (!out)
return;
/* 把还没实时打印过的记录补打一遍。
collect == false 时 records 是空的,num_printed 可能非零,
用 records.size 兜底避免越界。 */
usize printed_limit = d->num_printed;
if (printed_limit > vec_size(d->records))
printed_limit = vec_size(d->records);
for (usize i = printed_limit; i < vec_size(d->records); ++i)
print_record(d, &vec_at(d->records, i));
/* 计数 */
const char *ecol = d->color && d->n_errors > 0 ? ANSI_FG_RED : "";
const char *rst = d->color ? ANSI_NONE : "";
fprintf(out, "%s%zu error%s, %zu warning%s%s\n", ecol, d->n_errors, d->n_errors == 1 ? "" : "s",
d->n_warns, d->n_warns == 1 ? "" : "s", rst);
}