From 89315986d4e4fb0977d98c2cfb8003763f5b93cf Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 19 Mar 2024 12:37:11 +0000 Subject: [PATCH 1/9] Add suggestion when mistyping an array backwards e.g. `T[]` --- src/check_type.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/check_type.cpp b/src/check_type.cpp index e71b35809..da4479f6e 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -3344,8 +3344,25 @@ gb_internal Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type) if (!ok) { gbString err_str = expr_to_string(e); + defer (gb_string_free(err_str)); + + ERROR_BLOCK(); error(e, "'%s' is not a type", err_str); - gb_string_free(err_str); + + Ast *node = unparen_expr(e); + if (node && node->kind == Ast_IndexExpr) { + gbString index_str = nullptr; + if (node->IndexExpr.index) { + index_str = expr_to_string(node->IndexExpr.index); + } + defer (gb_string_free(index_str)); + + gbString type_str = expr_to_string(node->IndexExpr.expr); + defer (gb_string_free(type_str)); + + error_line("\tSuggestion: Did you mean '[%s]%s'?", index_str ? index_str : "", type_str); + } + type = t_invalid; } From d5daa9fda59e7b8ad8749f8a7630fb30f1f73a04 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 19 Mar 2024 13:05:28 +0000 Subject: [PATCH 2/9] Minimize error propagation of bad array syntax by treating this like a type --- src/check_type.cpp | 220 ++++++++++++++++++++++++--------------------- 1 file changed, 116 insertions(+), 104 deletions(-) diff --git a/src/check_type.cpp b/src/check_type.cpp index da4479f6e..5889cbcd0 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2834,6 +2834,111 @@ gb_internal Type *make_soa_struct_dynamic_array(CheckerContext *ctx, Ast *array_ return make_soa_struct_internal(ctx, array_typ_expr, elem_expr, elem, -1, nullptr, StructSoa_Dynamic); } +gb_internal void check_array_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_type) { + ast_node(at, ArrayType, e); + if (at->count != nullptr) { + Operand o = {}; + i64 count = check_array_count(ctx, &o, at->count); + Type *generic_type = nullptr; + + Type *elem = check_type_expr(ctx, at->elem, nullptr); + + if (o.mode == Addressing_Type && o.type->kind == Type_Generic) { + generic_type = o.type; + } else if (o.mode == Addressing_Type && is_type_enum(o.type)) { + Type *index = o.type; + Type *bt = base_type(index); + GB_ASSERT(bt->kind == Type_Enum); + + Type *t = alloc_type_enumerated_array(elem, index, bt->Enum.min_value, bt->Enum.max_value, bt->Enum.fields.count, Token_Invalid); + + bool is_sparse = false; + if (at->tag != nullptr) { + GB_ASSERT(at->tag->kind == Ast_BasicDirective); + String name = at->tag->BasicDirective.name.string; + if (name == "sparse") { + is_sparse = true; + } else { + error(at->tag, "Invalid tag applied to an enumerated array, got #%.*s", LIT(name)); + } + } + + if (!is_sparse && t->EnumeratedArray.count > bt->Enum.fields.count) { + error(e, "Non-contiguous enumeration used as an index in an enumerated array"); + long long ea_count = cast(long long)t->EnumeratedArray.count; + long long enum_count = cast(long long)bt->Enum.fields.count; + error_line("\tenumerated array length: %lld\n", ea_count); + error_line("\tenum field count: %lld\n", enum_count); + error_line("\tSuggestion: prepend #sparse to the enumerated array to allow for non-contiguous elements\n"); + if (2*enum_count < ea_count) { + error_line("\tWarning: the number of named elements is much smaller than the length of the array, are you sure this is what you want?\n"); + error_line("\t this warning will be removed if #sparse is applied\n"); + } + } + t->EnumeratedArray.is_sparse = is_sparse; + + *type = t; + + return; + } + + if (count < 0) { + error(at->count, "? can only be used in conjuction with compound literals"); + count = 0; + } + + + if (at->tag != nullptr) { + GB_ASSERT(at->tag->kind == Ast_BasicDirective); + String name = at->tag->BasicDirective.name.string; + if (name == "soa") { + *type = make_soa_struct_fixed(ctx, e, at->elem, elem, count, generic_type); + } else if (name == "simd") { + if (!is_type_valid_vector_elem(elem) && !is_type_polymorphic(elem)) { + gbString str = type_to_string(elem); + error(at->elem, "Invalid element type for #simd, expected an integer, float, or boolean with no specific endianness, got '%s'", str); + gb_string_free(str); + *type = alloc_type_array(elem, count, generic_type); + return; + } + + if (generic_type != nullptr) { + // Ignore + } else if (count < 1 || !is_power_of_two(count)) { + error(at->count, "Invalid length for #simd, expected a power of two length, got '%lld'", cast(long long)count); + *type = alloc_type_array(elem, count, generic_type); + return; + } + + *type = alloc_type_simd_vector(count, elem, generic_type); + + if (count > SIMD_ELEMENT_COUNT_MAX) { + error(at->count, "#simd support a maximum element count of %d, got %lld", SIMD_ELEMENT_COUNT_MAX, cast(long long)count); + } + } else { + error(at->tag, "Invalid tag applied to array, got #%.*s", LIT(name)); + *type = alloc_type_array(elem, count, generic_type); + } + } else { + *type = alloc_type_array(elem, count, generic_type); + } + } else { + Type *elem = check_type(ctx, at->elem); + + if (at->tag != nullptr) { + GB_ASSERT(at->tag->kind == Ast_BasicDirective); + String name = at->tag->BasicDirective.name.string; + if (name == "soa") { + *type = make_soa_struct_slice(ctx, e, at->elem, elem); + } else { + error(at->tag, "Invalid tag applied to array, got #%.*s", LIT(name)); + *type = alloc_type_slice(elem); + } + } else { + *type = alloc_type_slice(elem); + } + } +} gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_type) { GB_ASSERT_NOT_NULL(type); if (e == nullptr) { @@ -3072,109 +3177,7 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T case_end; case_ast_node(at, ArrayType, e); - if (at->count != nullptr) { - Operand o = {}; - i64 count = check_array_count(ctx, &o, at->count); - Type *generic_type = nullptr; - - Type *elem = check_type_expr(ctx, at->elem, nullptr); - - if (o.mode == Addressing_Type && o.type->kind == Type_Generic) { - generic_type = o.type; - } else if (o.mode == Addressing_Type && is_type_enum(o.type)) { - Type *index = o.type; - Type *bt = base_type(index); - GB_ASSERT(bt->kind == Type_Enum); - - Type *t = alloc_type_enumerated_array(elem, index, bt->Enum.min_value, bt->Enum.max_value, bt->Enum.fields.count, Token_Invalid); - - bool is_sparse = false; - if (at->tag != nullptr) { - GB_ASSERT(at->tag->kind == Ast_BasicDirective); - String name = at->tag->BasicDirective.name.string; - if (name == "sparse") { - is_sparse = true; - } else { - error(at->tag, "Invalid tag applied to an enumerated array, got #%.*s", LIT(name)); - } - } - - if (!is_sparse && t->EnumeratedArray.count > bt->Enum.fields.count) { - error(e, "Non-contiguous enumeration used as an index in an enumerated array"); - long long ea_count = cast(long long)t->EnumeratedArray.count; - long long enum_count = cast(long long)bt->Enum.fields.count; - error_line("\tenumerated array length: %lld\n", ea_count); - error_line("\tenum field count: %lld\n", enum_count); - error_line("\tSuggestion: prepend #sparse to the enumerated array to allow for non-contiguous elements\n"); - if (2*enum_count < ea_count) { - error_line("\tWarning: the number of named elements is much smaller than the length of the array, are you sure this is what you want?\n"); - error_line("\t this warning will be removed if #sparse is applied\n"); - } - } - t->EnumeratedArray.is_sparse = is_sparse; - - *type = t; - - goto array_end; - } - - if (count < 0) { - error(at->count, "? can only be used in conjuction with compound literals"); - count = 0; - } - - - if (at->tag != nullptr) { - GB_ASSERT(at->tag->kind == Ast_BasicDirective); - String name = at->tag->BasicDirective.name.string; - if (name == "soa") { - *type = make_soa_struct_fixed(ctx, e, at->elem, elem, count, generic_type); - } else if (name == "simd") { - if (!is_type_valid_vector_elem(elem) && !is_type_polymorphic(elem)) { - gbString str = type_to_string(elem); - error(at->elem, "Invalid element type for #simd, expected an integer, float, or boolean with no specific endianness, got '%s'", str); - gb_string_free(str); - *type = alloc_type_array(elem, count, generic_type); - goto array_end; - } - - if (generic_type != nullptr) { - // Ignore - } else if (count < 1 || !is_power_of_two(count)) { - error(at->count, "Invalid length for #simd, expected a power of two length, got '%lld'", cast(long long)count); - *type = alloc_type_array(elem, count, generic_type); - goto array_end; - } - - *type = alloc_type_simd_vector(count, elem, generic_type); - - if (count > SIMD_ELEMENT_COUNT_MAX) { - error(at->count, "#simd support a maximum element count of %d, got %lld", SIMD_ELEMENT_COUNT_MAX, cast(long long)count); - } - } else { - error(at->tag, "Invalid tag applied to array, got #%.*s", LIT(name)); - *type = alloc_type_array(elem, count, generic_type); - } - } else { - *type = alloc_type_array(elem, count, generic_type); - } - } else { - Type *elem = check_type(ctx, at->elem); - - if (at->tag != nullptr) { - GB_ASSERT(at->tag->kind == Ast_BasicDirective); - String name = at->tag->BasicDirective.name.string; - if (name == "soa") { - *type = make_soa_struct_slice(ctx, e, at->elem, elem); - } else { - error(at->tag, "Invalid tag applied to array, got #%.*s", LIT(name)); - *type = alloc_type_slice(elem); - } - } else { - *type = alloc_type_slice(elem); - } - } - array_end: + check_array_type_internal(ctx, e, type, named_type); set_base_type(named_type, *type); return true; case_end; @@ -3349,6 +3352,10 @@ gb_internal Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type) ERROR_BLOCK(); error(e, "'%s' is not a type", err_str); + type = t_invalid; + + + // NOTE(bill): Check for common mistakes from C programmers e.g. T[] and T[N] Ast *node = unparen_expr(e); if (node && node->kind == Ast_IndexExpr) { gbString index_str = nullptr; @@ -3361,9 +3368,14 @@ gb_internal Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type) defer (gb_string_free(type_str)); error_line("\tSuggestion: Did you mean '[%s]%s'?", index_str ? index_str : "", type_str); + + // NOTE(bill): Minimize error propagation of bad array syntax by treating this like a type + if (node->IndexExpr.expr != nullptr) { + Ast *pseudo_array_expr = ast_array_type(e->file(), ast_token(node->IndexExpr.expr), node->IndexExpr.index, node->IndexExpr.expr); + check_array_type_internal(ctx, pseudo_array_expr, &type, nullptr); + } } - type = t_invalid; } if (type == nullptr) { From 5159f30c9cec63e467e82ed76d54988f58e3bdcd Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 19 Mar 2024 13:10:35 +0000 Subject: [PATCH 3/9] Fix error block handling --- src/check_type.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/check_type.cpp b/src/check_type.cpp index 5889cbcd0..d5cf187a4 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -3349,7 +3349,7 @@ gb_internal Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type) gbString err_str = expr_to_string(e); defer (gb_string_free(err_str)); - ERROR_BLOCK(); + begin_error_block(); error(e, "'%s' is not a type", err_str); type = t_invalid; @@ -3368,14 +3368,16 @@ gb_internal Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type) defer (gb_string_free(type_str)); error_line("\tSuggestion: Did you mean '[%s]%s'?", index_str ? index_str : "", type_str); + end_error_block(); // NOTE(bill): Minimize error propagation of bad array syntax by treating this like a type if (node->IndexExpr.expr != nullptr) { Ast *pseudo_array_expr = ast_array_type(e->file(), ast_token(node->IndexExpr.expr), node->IndexExpr.index, node->IndexExpr.expr); check_array_type_internal(ctx, pseudo_array_expr, &type, nullptr); } + } else { + end_error_block(); } - } if (type == nullptr) { From 80ecf5b68aa02a2445aa4cbf3d1d46ed5a007d75 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 19 Mar 2024 13:32:37 +0000 Subject: [PATCH 4/9] On `x: [?]T = {...}`, minimize errors by using the `[?]T` expression as a kind of hint --- src/check_expr.cpp | 47 +++++++++++++++++++++++++++++++++++++--------- src/check_stmt.cpp | 12 +++++++----- src/checker.hpp | 1 + 3 files changed, 46 insertions(+), 14 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index bb31a1646..ed6182928 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -8672,6 +8672,24 @@ gb_internal void check_compound_literal_field_values(CheckerContext *c, Slicekind == Ast_ArrayType && type_expr->ArrayType.count != nullptr) { + Ast *count = type_expr->ArrayType.count; + if (count->kind == Ast_UnaryExpr && + count->UnaryExpr.op.kind == Token_Question) { + return true; + } + } + return false; +} + gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { ExprKind kind = Expr_Expr; ast_node(cl, CompoundLit, node); @@ -8682,20 +8700,31 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * } bool is_to_be_determined_array_count = false; bool is_constant = true; - if (cl->type != nullptr) { + + Ast *type_expr = cl->type; + + bool used_type_hint_expr = false; + if (type_expr == nullptr && c->type_hint_expr != nullptr) { + if (is_expr_inferred_fixed_array(c->type_hint_expr)) { + type_expr = clone_ast(c->type_hint_expr); + used_type_hint_expr = true; + } + } + + if (type_expr != nullptr) { type = nullptr; // [?]Type - if (cl->type->kind == Ast_ArrayType && cl->type->ArrayType.count != nullptr) { - Ast *count = cl->type->ArrayType.count; + if (type_expr->kind == Ast_ArrayType && type_expr->ArrayType.count != nullptr) { + Ast *count = type_expr->ArrayType.count; if (count->kind == Ast_UnaryExpr && count->UnaryExpr.op.kind == Token_Question) { - type = alloc_type_array(check_type(c, cl->type->ArrayType.elem), -1); + type = alloc_type_array(check_type(c, type_expr->ArrayType.elem), -1); is_to_be_determined_array_count = true; } if (cl->elems.count > 0) { - if (cl->type->ArrayType.tag != nullptr) { - Ast *tag = cl->type->ArrayType.tag; + if (type_expr->ArrayType.tag != nullptr) { + Ast *tag = type_expr->ArrayType.tag; GB_ASSERT(tag->kind == Ast_BasicDirective); String name = tag->BasicDirective.name.string; if (name == "soa") { @@ -8705,9 +8734,9 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * } } } - if (cl->type->kind == Ast_DynamicArrayType && cl->type->DynamicArrayType.tag != nullptr) { + if (type_expr->kind == Ast_DynamicArrayType && type_expr->DynamicArrayType.tag != nullptr) { if (cl->elems.count > 0) { - Ast *tag = cl->type->DynamicArrayType.tag; + Ast *tag = type_expr->DynamicArrayType.tag; GB_ASSERT(tag->kind == Ast_BasicDirective); String name = tag->BasicDirective.name.string; if (name == "soa") { @@ -8718,7 +8747,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast * } if (type == nullptr) { - type = check_type(c, cl->type); + type = check_type(c, type_expr); } } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index a6ca4b9dd..4280e7578 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1919,17 +1919,19 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f e->Variable.thread_local_model = ac.thread_local_model; } - if (is_arch_wasm() && e->Variable.thread_local_model.len != 0) { - // error(e->token, "@(thread_local) is not supported for this target platform"); - } - - if (ac.is_static && ac.thread_local_model != "") { error(e->token, "The 'static' attribute is not needed if 'thread_local' is applied"); } } + // NOTE(bill): This is to improve error handling for things like `x: [?]T = {...}` + Ast *prev_type_hint_expr = ctx->type_hint_expr; + ctx->type_hint_expr = vd->type; + check_init_variables(ctx, entities, entity_count, vd->values, str_lit("variable declaration")); + + ctx->type_hint_expr = prev_type_hint_expr; + check_arity_match(ctx, vd, false); for (isize i = 0; i < entity_count; i++) { diff --git a/src/checker.hpp b/src/checker.hpp index 066d6bb4a..eea99578e 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -451,6 +451,7 @@ struct CheckerContext { u32 state_flags; bool in_defer; Type * type_hint; + Ast * type_hint_expr; String proc_name; DeclInfo * curr_proc_decl; From 8ff788f4fff7504b8b6bca1183a9be98b8765cbc Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 19 Mar 2024 13:38:13 +0000 Subject: [PATCH 5/9] Add better suggestion for [?]T mistake --- src/check_expr.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index ed6182928..236d44a43 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -119,6 +119,8 @@ gb_internal bool is_diverging_expr(Ast *expr); gb_internal isize get_procedure_param_count_excluding_defaults(Type *pt, isize *param_count_); +gb_internal bool is_expr_inferred_fixed_array(Ast *type_expr); + enum LoadDirectiveResult { LoadDirective_Success = 0, LoadDirective_Error = 1, @@ -2242,6 +2244,10 @@ gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o error_line("\tSuggestion: the expression may be casted to %s\n", b); } else if (check_integer_exceed_suggestion(c, o, type, max_bit_size)) { return; + } else if (is_expr_inferred_fixed_array(c->type_hint_expr) && is_type_array_like(type) && is_type_array_like(o->type)) { + gbString s = expr_to_string(c->type_hint_expr); + error_line("\tSuggestion: make sure that `%s` is attached to the compound literal directly\n", s); + gb_string_free(s); } } @@ -8678,7 +8684,6 @@ gb_internal bool is_expr_inferred_fixed_array(Ast *type_expr) { return false; } - // [?]Type if (type_expr->kind == Ast_ArrayType && type_expr->ArrayType.count != nullptr) { Ast *count = type_expr->ArrayType.count; From 9a2fc6cf4c8b4434ae45170953b77b3239120fea Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 19 Mar 2024 15:34:29 +0000 Subject: [PATCH 6/9] Serialize errors to make them sortable, deterministic, and generally more control --- src/array.cpp | 7 ++ src/build_settings.cpp | 4 + src/check_builtin.cpp | 2 +- src/check_expr.cpp | 2 +- src/checker.cpp | 8 +- src/common.cpp | 2 +- src/docs.cpp | 4 +- src/docs_writer.cpp | 2 +- src/error.cpp | 215 +++++++++++++++++++++++++---------------- src/llvm_backend.cpp | 2 +- src/main.cpp | 4 +- src/string.cpp | 1 - 12 files changed, 159 insertions(+), 94 deletions(-) diff --git a/src/array.cpp b/src/array.cpp index 4583a31a9..ec2c97d0e 100644 --- a/src/array.cpp +++ b/src/array.cpp @@ -51,6 +51,13 @@ template gb_internal void array_copy(Array *array, Array cons template gb_internal T *array_end_ptr(Array *array); +template +gb_internal void array_sort(Array &array, gbCompareProc compare_proc) { + gb_sort_array(array.data, array.count, compare_proc); +} + + + template struct Slice { T *data; diff --git a/src/build_settings.cpp b/src/build_settings.cpp index fdaa971f1..c4073f329 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -1272,6 +1272,10 @@ gb_internal String get_fullpath_core_collection(gbAllocator a, String path, bool gb_internal bool show_error_line(void) { return !build_context.hide_error_line; } + +gb_internal bool terse_errors(void) { + return build_context.terse_errors; +} gb_internal bool has_ansi_terminal_colours(void) { return build_context.has_ansi_terminal_colours; } diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index e1b1cd693..6de3b27f2 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1389,7 +1389,7 @@ gb_internal LoadDirectiveResult check_load_directory_directive(CheckerContext *c } } - gb_sort_array(file_caches.data, file_caches.count, file_cache_sort_cmp); + array_sort(file_caches, file_cache_sort_cmp); } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 236d44a43..f359d5a54 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6485,7 +6485,7 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c, } if (valids.count > 1) { - gb_sort_array(valids.data, valids.count, valid_index_and_score_cmp); + array_sort(valids, valid_index_and_score_cmp); i64 best_score = valids[0].score; Entity *best_entity = proc_entities[valids[0].index]; GB_ASSERT(best_entity != nullptr); diff --git a/src/checker.cpp b/src/checker.cpp index fb7d401ab..836f803fc 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -5044,7 +5044,7 @@ gb_internal void check_create_file_scopes(Checker *c) { for_array(i, c->parser->packages) { AstPackage *pkg = c->parser->packages[i]; - gb_sort_array(pkg->files.data, pkg->files.count, sort_file_by_name); + array_sort(pkg->files, sort_file_by_name); isize total_pkg_decl_count = 0; for_array(j, pkg->files) { @@ -5673,7 +5673,7 @@ gb_internal void remove_neighbouring_duplicate_entires_from_sorted_array(Arrayinfo.testing_procedures.data, c->info.testing_procedures.count, init_procedures_cmp); + array_sort(c->info.testing_procedures, init_procedures_cmp); remove_neighbouring_duplicate_entires_from_sorted_array(&c->info.testing_procedures); if (build_context.test_names.entries.count == 0) { @@ -6122,8 +6122,8 @@ gb_internal GB_COMPARE_PROC(fini_procedures_cmp) { } gb_internal void check_sort_init_and_fini_procedures(Checker *c) { - gb_sort_array(c->info.init_procedures.data, c->info.init_procedures.count, init_procedures_cmp); - gb_sort_array(c->info.fini_procedures.data, c->info.fini_procedures.count, fini_procedures_cmp); + array_sort(c->info.init_procedures, init_procedures_cmp); + array_sort(c->info.fini_procedures, fini_procedures_cmp); // NOTE(bill): remove possible duplicates from the init/fini lists // NOTE(bill): because the arrays are sorted, you only need to check the previous element diff --git a/src/common.cpp b/src/common.cpp index 90632def3..aad420325 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -913,7 +913,7 @@ gb_internal void did_you_mean_append(DidYouMeanAnswers *d, String const &target) array_add(&d->distances, dat); } gb_internal Slice did_you_mean_results(DidYouMeanAnswers *d) { - gb_sort_array(d->distances.data, d->distances.count, gb_isize_cmp(gb_offset_of(DistanceAndTarget, distance))); + array_sort(d->distances, gb_isize_cmp(gb_offset_of(DistanceAndTarget, distance))); isize count = 0; for (isize i = 0; i < d->distances.count; i++) { isize distance = d->distances[i].distance; diff --git a/src/docs.cpp b/src/docs.cpp index f00d4e15a..004134a5c 100644 --- a/src/docs.cpp +++ b/src/docs.cpp @@ -237,7 +237,7 @@ gb_internal void print_doc_package(CheckerInfo *info, AstPackage *pkg) { } array_add(&entities, e); } - gb_sort_array(entities.data, entities.count, cmp_entities_for_printing); + array_sort(entities, cmp_entities_for_printing); bool show_docs = (build_context.cmd_doc_flags & CmdDocFlag_Short) == 0; @@ -358,7 +358,7 @@ gb_internal void generate_documentation(Checker *c) { } } - gb_sort_array(pkgs.data, pkgs.count, cmp_ast_package_by_name); + array_sort(pkgs, cmp_ast_package_by_name); for_array(i, pkgs) { print_doc_package(info, pkgs[i]); diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp index 1bc244918..26d8027a9 100644 --- a/src/docs_writer.cpp +++ b/src/docs_writer.cpp @@ -1107,7 +1107,7 @@ gb_internal void odin_doc_write_docs(OdinDocWriter *w) { } debugf("odin_doc_update_entities sort pkgs %s\n", w->state ? "preparing" : "writing"); - gb_sort_array(pkgs.data, pkgs.count, cmp_ast_package_by_name); + array_sort(pkgs, cmp_ast_package_by_name); for_array(i, pkgs) { gbAllocator allocator = heap_allocator(); diff --git a/src/error.cpp b/src/error.cpp index e63682829..e5803e5a2 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -1,3 +1,14 @@ +enum ErrorValueKind : u32 { + ErrorValue_Error, + ErrorValue_Warning, +}; + +struct ErrorValue { + ErrorValueKind kind; + TokenPos pos; + Array msgs; +}; + struct ErrorCollector { TokenPos prev; std::atomic count; @@ -8,21 +19,54 @@ struct ErrorCollector { BlockingMutex string_mutex; RecursiveMutex block_mutex; - RecursiveMutex error_buffer_mutex; - Array error_buffer; - Array errors; + Array error_values; + ErrorValue curr_error_value; + std::atomic curr_error_value_set; }; gb_global ErrorCollector global_error_collector; +gb_internal void push_error_value(TokenPos const &pos, ErrorValueKind kind = ErrorValue_Error) { + GB_ASSERT(global_error_collector.curr_error_value_set.load() == false); + ErrorValue ev = {kind, pos}; + ev.msgs.allocator = heap_allocator(); + + global_error_collector.curr_error_value = ev; + global_error_collector.curr_error_value_set.store(true); +} + +gb_internal void pop_error_value(void) { + if (global_error_collector.curr_error_value_set.load()) { + array_add(&global_error_collector.error_values, global_error_collector.curr_error_value); + + global_error_collector.curr_error_value = {}; + global_error_collector.curr_error_value_set.store(false); + } +} + + +gb_internal void try_pop_error_value(void) { + if (!global_error_collector.in_block.load()) { + pop_error_value(); + } +} + +gb_internal ErrorValue *get_error_value(void) { + GB_ASSERT(global_error_collector.curr_error_value_set.load() == true); + return &global_error_collector.curr_error_value; +} + + + gb_internal bool any_errors(void) { return global_error_collector.count.load() != 0; } + + gb_internal void init_global_error_collector(void) { - array_init(&global_error_collector.errors, heap_allocator()); - array_init(&global_error_collector.error_buffer, heap_allocator()); + array_init(&global_error_collector.error_values, heap_allocator()); array_init(&global_file_path_strings, heap_allocator(), 1, 4096); array_init(&global_files, heap_allocator(), 1, 4096); } @@ -102,6 +146,7 @@ gb_internal AstFile *thread_safe_get_ast_file_from_id(i32 index) { gb_internal bool global_warnings_as_errors(void); gb_internal bool global_ignore_warnings(void); gb_internal bool show_error_line(void); +gb_internal bool terse_errors(void); gb_internal bool has_ansi_terminal_colours(void); gb_internal gbString get_file_line_as_string(TokenPos const &pos, i32 *offset); @@ -113,6 +158,24 @@ gb_internal void syntax_error(Token const &token, char const *fmt, ...); gb_internal void syntax_error(TokenPos pos, char const *fmt, ...); gb_internal void syntax_warning(Token const &token, char const *fmt, ...); gb_internal void compiler_error(char const *fmt, ...); +gb_internal void print_all_errors(void); + + +#define ERROR_OUT_PROC(name) void name(char const *fmt, va_list va) +typedef ERROR_OUT_PROC(ErrorOutProc); + +gb_internal ERROR_OUT_PROC(default_error_out_va) { + char buf[4096] = {}; + isize len = gb_snprintf_va(buf, gb_size_of(buf), fmt, va); + isize n = len-1; + + String msg = {(u8 *)buf, n}; + + ErrorValue *ev = get_error_value(); + array_add(&ev->msgs, copy_string(permanent_allocator(), msg)); +} + +gb_global ErrorOutProc *error_out_va = default_error_out_va; gb_internal void begin_error_block(void) { mutex_lock(&global_error_collector.block_mutex); @@ -120,48 +183,7 @@ gb_internal void begin_error_block(void) { } gb_internal void end_error_block(void) { - mutex_lock(&global_error_collector.error_buffer_mutex); - isize n = global_error_collector.error_buffer.count; - if (n > 0) { - u8 *text = global_error_collector.error_buffer.data; - - bool add_extra_newline = false; - - if (show_error_line()) { - if (n >= 2 && !(text[n-2] == '\n' && text[n-1] == '\n')) { - add_extra_newline = true; - } - } else { - isize newline_count = 0; - for (isize i = 0; i < n; i++) { - if (text[i] == '\n') { - newline_count += 1; - } - } - if (newline_count > 1) { - add_extra_newline = true; - } - } - - if (add_extra_newline) { - // add an extra new line as padding when the error line is being shown - error_line("\n"); - } - - n = global_error_collector.error_buffer.count; - text = gb_alloc_array(permanent_allocator(), u8, n+1); - gb_memmove(text, global_error_collector.error_buffer.data, n); - text[n] = 0; - - - mutex_lock(&global_error_collector.error_out_mutex); - String s = {text, n}; - array_add(&global_error_collector.errors, s); - mutex_unlock(&global_error_collector.error_out_mutex); - - global_error_collector.error_buffer.count = 0; - } - mutex_unlock(&global_error_collector.error_buffer_mutex); + pop_error_value(); global_error_collector.in_block.store(false); mutex_unlock(&global_error_collector.block_mutex); } @@ -169,40 +191,6 @@ gb_internal void end_error_block(void) { #define ERROR_BLOCK() begin_error_block(); defer (end_error_block()) -#define ERROR_OUT_PROC(name) void name(char const *fmt, va_list va) -typedef ERROR_OUT_PROC(ErrorOutProc); - -gb_internal ERROR_OUT_PROC(default_error_out_va) { - gbFile *f = gb_file_get_standard(gbFileStandard_Error); - - char buf[4096] = {}; - isize len = gb_snprintf_va(buf, gb_size_of(buf), fmt, va); - isize n = len-1; - if (global_error_collector.in_block) { - mutex_lock(&global_error_collector.error_buffer_mutex); - - isize cap = global_error_collector.error_buffer.count + n; - array_reserve(&global_error_collector.error_buffer, cap); - u8 *data = global_error_collector.error_buffer.data + global_error_collector.error_buffer.count; - gb_memmove(data, buf, n); - global_error_collector.error_buffer.count += n; - - mutex_unlock(&global_error_collector.error_buffer_mutex); - } else { - mutex_lock(&global_error_collector.error_out_mutex); - { - u8 *text = gb_alloc_array(permanent_allocator(), u8, n+1); - gb_memmove(text, buf, n); - text[n] = 0; - array_add(&global_error_collector.errors, make_string(text, n)); - } - mutex_unlock(&global_error_collector.error_out_mutex); - - } - gb_file_write(f, buf, n); -} - -gb_global ErrorOutProc *error_out_va = default_error_out_va; gb_internal void error_out(char const *fmt, ...) { va_list va; @@ -357,9 +345,12 @@ gb_internal void error_out_coloured(char const *str, TerminalStyle style, Termin gb_internal void error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) { global_error_collector.count.fetch_add(1); if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT()) { + print_all_errors(); gb_exit(1); } mutex_lock(&global_error_collector.mutex); + + push_error_value(pos, ErrorValue_Error); // NOTE(bill): Duplicate error, skip it if (pos.line == 0) { error_out_coloured("Error: ", TerminalStyle_Normal, TerminalColour_Red); @@ -377,6 +368,7 @@ gb_internal void error_va(TokenPos const &pos, TokenPos end, char const *fmt, va } else { global_error_collector.count.fetch_sub(1); } + try_pop_error_value(); mutex_unlock(&global_error_collector.mutex); } @@ -387,6 +379,9 @@ gb_internal void warning_va(TokenPos const &pos, TokenPos end, char const *fmt, } global_error_collector.warning_count.fetch_add(1); mutex_lock(&global_error_collector.mutex); + + push_error_value(pos, ErrorValue_Warning); + if (!global_ignore_warnings()) { // NOTE(bill): Duplicate error, skip it if (pos.line == 0) { @@ -402,6 +397,7 @@ gb_internal void warning_va(TokenPos const &pos, TokenPos end, char const *fmt, show_error_on_line(pos, end); } } + try_pop_error_value(); mutex_unlock(&global_error_collector.mutex); } @@ -413,9 +409,13 @@ gb_internal void error_line_va(char const *fmt, va_list va) { gb_internal void error_no_newline_va(TokenPos const &pos, char const *fmt, va_list va) { global_error_collector.count.fetch_add(1); if (global_error_collector.count.load() > MAX_ERROR_COLLECTOR_COUNT()) { + print_all_errors(); gb_exit(1); } mutex_lock(&global_error_collector.mutex); + + push_error_value(pos, ErrorValue_Error); + // NOTE(bill): Duplicate error, skip it if (pos.line == 0) { error_out_coloured("Error: ", TerminalStyle_Normal, TerminalColour_Red); @@ -428,6 +428,8 @@ gb_internal void error_no_newline_va(TokenPos const &pos, char const *fmt, va_li } error_out_va(fmt, va); } + + try_pop_error_value(); mutex_unlock(&global_error_collector.mutex); } @@ -435,9 +437,13 @@ gb_internal void error_no_newline_va(TokenPos const &pos, char const *fmt, va_li gb_internal void syntax_error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) { global_error_collector.count.fetch_add(1); if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT()) { + print_all_errors(); gb_exit(1); } mutex_lock(&global_error_collector.mutex); + + push_error_value(pos, ErrorValue_Warning); + // NOTE(bill): Duplicate error, skip it if (global_error_collector.prev != pos) { global_error_collector.prev = pos; @@ -451,15 +457,21 @@ gb_internal void syntax_error_va(TokenPos const &pos, TokenPos end, char const * error_out_va(fmt, va); error_out("\n"); } + + try_pop_error_value(); mutex_unlock(&global_error_collector.mutex); } gb_internal void syntax_error_with_verbose_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) { global_error_collector.count.fetch_add(1); if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT()) { + print_all_errors(); gb_exit(1); } mutex_lock(&global_error_collector.mutex); + + push_error_value(pos, ErrorValue_Warning); + // NOTE(bill): Duplicate error, skip it if (pos.line == 0) { error_out_coloured("Syntax_Error: ", TerminalStyle_Normal, TerminalColour_Red); @@ -475,6 +487,8 @@ gb_internal void syntax_error_with_verbose_va(TokenPos const &pos, TokenPos end, error_out("\n"); show_error_on_line(pos, end); } + + try_pop_error_value(); mutex_unlock(&global_error_collector.mutex); } @@ -486,6 +500,10 @@ gb_internal void syntax_warning_va(TokenPos const &pos, TokenPos end, char const } mutex_lock(&global_error_collector.mutex); global_error_collector.warning_count++; + + + push_error_value(pos, ErrorValue_Warning); + if (!global_ignore_warnings()) { // NOTE(bill): Duplicate error, skip it if (global_error_collector.prev != pos) { @@ -501,6 +519,8 @@ gb_internal void syntax_warning_va(TokenPos const &pos, TokenPos end, char const error_out("\n"); } } + + try_pop_error_value(); mutex_unlock(&global_error_collector.mutex); } @@ -568,6 +588,8 @@ gb_internal void syntax_error_with_verbose(TokenPos pos, TokenPos end, char cons gb_internal void compiler_error(char const *fmt, ...) { + print_all_errors(); + va_list va; va_start(va, fmt); @@ -577,3 +599,34 @@ gb_internal void compiler_error(char const *fmt, ...) { GB_DEBUG_TRAP(); gb_exit(1); } + + + + + +gb_internal int error_value_cmp(void const *a, void const *b) { + ErrorValue *x = cast(ErrorValue *)a; + ErrorValue *y = cast(ErrorValue *)b; + return token_pos_cmp(x->pos, y->pos); +} + +gb_internal void print_all_errors(void) { + GB_ASSERT(any_errors()); + gbFile *f = gb_file_get_standard(gbFileStandard_Error); + + array_sort(global_error_collector.error_values, error_value_cmp); + + for_array(i, global_error_collector.error_values) { + ErrorValue ev = global_error_collector.error_values[i]; + for_array(j, ev.msgs) { + String msg = ev.msgs[j]; + gb_file_write(f, msg.text, msg.len); + if (terse_errors()) { + if (string_contains_char(msg, '\n')) { + break; + } + } + } + } + +} \ No newline at end of file diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index ca4341525..b8ee7e7fa 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -3021,7 +3021,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { } } - gb_sort_array(gen->foreign_libraries.data, gen->foreign_libraries.count, foreign_library_cmp); + array_sort(gen->foreign_libraries, foreign_library_cmp); return true; } diff --git a/src/main.cpp b/src/main.cpp index 7951ca2db..0f28e137f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2095,7 +2095,7 @@ gb_internal void print_show_unused(Checker *c) { array_add(&unused, e); } - gb_sort_array(unused.data, unused.count, cmp_entities_for_printing); + array_sort(unused, cmp_entities_for_printing); print_usage_line(0, "Unused Package Declarations"); @@ -2680,6 +2680,7 @@ int main(int arg_count, char const **arg_ptr) { } if (any_errors()) { + print_all_errors(); return 1; } @@ -2691,6 +2692,7 @@ int main(int arg_count, char const **arg_ptr) { check_parsed_files(checker); if (any_errors()) { + print_all_errors(); return 1; } diff --git a/src/string.cpp b/src/string.cpp index 8be40ec3c..7bfa52f33 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -89,7 +89,6 @@ gb_internal char *alloc_cstring(gbAllocator a, String s) { } - gb_internal gb_inline bool str_eq_ignore_case(String const &a, String const &b) { if (a.len == b.len) { for (isize i = 0; i < a.len; i++) { From 17cc7a2c5ee8cbce8aeeff0e0952f54b681e14c7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 19 Mar 2024 15:42:59 +0000 Subject: [PATCH 7/9] General clean-up for error.cpp --- src/error.cpp | 53 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/src/error.cpp b/src/error.cpp index e5803e5a2..d116781fb 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -14,10 +14,9 @@ struct ErrorCollector { std::atomic count; std::atomic warning_count; std::atomic in_block; - BlockingMutex mutex; - BlockingMutex error_out_mutex; - BlockingMutex string_mutex; - RecursiveMutex block_mutex; + + RecursiveMutex mutex; + BlockingMutex path_mutex; Array error_values; ErrorValue curr_error_value; @@ -81,7 +80,7 @@ gb_internal char *token_pos_to_string(TokenPos const &pos); gb_internal bool set_file_path_string(i32 index, String const &path) { bool ok = false; GB_ASSERT(index >= 0); - mutex_lock(&global_error_collector.string_mutex); + mutex_lock(&global_error_collector.path_mutex); if (index >= global_file_path_strings.count) { array_resize(&global_file_path_strings, index+1); @@ -92,14 +91,14 @@ gb_internal bool set_file_path_string(i32 index, String const &path) { ok = true; } - mutex_unlock(&global_error_collector.string_mutex); + mutex_unlock(&global_error_collector.path_mutex); return ok; } gb_internal bool thread_safe_set_ast_file_from_id(i32 index, AstFile *file) { bool ok = false; GB_ASSERT(index >= 0); - mutex_lock(&global_error_collector.string_mutex); + mutex_lock(&global_error_collector.path_mutex); if (index >= global_files.count) { array_resize(&global_files, index+1); @@ -110,33 +109,33 @@ gb_internal bool thread_safe_set_ast_file_from_id(i32 index, AstFile *file) { ok = true; } - mutex_unlock(&global_error_collector.string_mutex); + mutex_unlock(&global_error_collector.path_mutex); return ok; } gb_internal String get_file_path_string(i32 index) { GB_ASSERT(index >= 0); - mutex_lock(&global_error_collector.string_mutex); + mutex_lock(&global_error_collector.path_mutex); String path = {}; if (index < global_file_path_strings.count) { path = global_file_path_strings[index]; } - mutex_unlock(&global_error_collector.string_mutex); + mutex_unlock(&global_error_collector.path_mutex); return path; } gb_internal AstFile *thread_safe_get_ast_file_from_id(i32 index) { GB_ASSERT(index >= 0); - mutex_lock(&global_error_collector.string_mutex); + mutex_lock(&global_error_collector.path_mutex); AstFile *file = nullptr; if (index < global_files.count) { file = global_files[index]; } - mutex_unlock(&global_error_collector.string_mutex); + mutex_unlock(&global_error_collector.path_mutex); return file; } @@ -169,23 +168,26 @@ gb_internal ERROR_OUT_PROC(default_error_out_va) { isize len = gb_snprintf_va(buf, gb_size_of(buf), fmt, va); isize n = len-1; - String msg = {(u8 *)buf, n}; + String msg = {}; + if (n) { + msg = copy_string(permanent_allocator(), {(u8 *)buf, n}); + } ErrorValue *ev = get_error_value(); - array_add(&ev->msgs, copy_string(permanent_allocator(), msg)); + array_add(&ev->msgs, msg); } gb_global ErrorOutProc *error_out_va = default_error_out_va; gb_internal void begin_error_block(void) { - mutex_lock(&global_error_collector.block_mutex); + mutex_lock(&global_error_collector.mutex); global_error_collector.in_block.store(true); } gb_internal void end_error_block(void) { pop_error_value(); global_error_collector.in_block.store(false); - mutex_unlock(&global_error_collector.block_mutex); + mutex_unlock(&global_error_collector.mutex); } #define ERROR_BLOCK() begin_error_block(); defer (end_error_block()) @@ -328,6 +330,9 @@ gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) { return false; } +gb_internal void error_out_empty(void) { + error_out(""); +} gb_internal void error_out_pos(TokenPos pos) { terminal_set_colours(TerminalStyle_Bold, TerminalColour_White); error_out("%s ", token_pos_to_string(pos)); @@ -353,6 +358,7 @@ gb_internal void error_va(TokenPos const &pos, TokenPos end, char const *fmt, va push_error_value(pos, ErrorValue_Error); // NOTE(bill): Duplicate error, skip it if (pos.line == 0) { + error_out_empty(); error_out_coloured("Error: ", TerminalStyle_Normal, TerminalColour_Red); error_out_va(fmt, va); error_out("\n"); @@ -385,6 +391,7 @@ gb_internal void warning_va(TokenPos const &pos, TokenPos end, char const *fmt, if (!global_ignore_warnings()) { // NOTE(bill): Duplicate error, skip it if (pos.line == 0) { + error_out_empty(); error_out_coloured("Warning: ", TerminalStyle_Normal, TerminalColour_Yellow); error_out_va(fmt, va); error_out("\n"); @@ -418,6 +425,7 @@ gb_internal void error_no_newline_va(TokenPos const &pos, char const *fmt, va_li // NOTE(bill): Duplicate error, skip it if (pos.line == 0) { + error_out_empty(); error_out_coloured("Error: ", TerminalStyle_Normal, TerminalColour_Red); error_out_va(fmt, va); } else if (global_error_collector.prev != pos) { @@ -453,6 +461,7 @@ gb_internal void syntax_error_va(TokenPos const &pos, TokenPos end, char const * error_out("\n"); // show_error_on_line(pos, end); } else if (pos.line == 0) { + error_out_empty(); error_out_coloured("Syntax Error: ", TerminalStyle_Normal, TerminalColour_Red); error_out_va(fmt, va); error_out("\n"); @@ -474,6 +483,7 @@ gb_internal void syntax_error_with_verbose_va(TokenPos const &pos, TokenPos end, // NOTE(bill): Duplicate error, skip it if (pos.line == 0) { + error_out_empty(); error_out_coloured("Syntax_Error: ", TerminalStyle_Normal, TerminalColour_Red); error_out_va(fmt, va); error_out("\n"); @@ -514,6 +524,7 @@ gb_internal void syntax_warning_va(TokenPos const &pos, TokenPos end, char const error_out("\n"); // show_error_on_line(pos, end); } else if (pos.line == 0) { + error_out_empty(); error_out_coloured("Syntax Warning: ", TerminalStyle_Normal, TerminalColour_Yellow); error_out_va(fmt, va); error_out("\n"); @@ -618,15 +629,13 @@ gb_internal void print_all_errors(void) { for_array(i, global_error_collector.error_values) { ErrorValue ev = global_error_collector.error_values[i]; - for_array(j, ev.msgs) { + for (isize j = 0; j < ev.msgs.count; j++) { String msg = ev.msgs[j]; gb_file_write(f, msg.text, msg.len); - if (terse_errors()) { - if (string_contains_char(msg, '\n')) { - break; - } + + if (terse_errors() && string_contains_char(msg, '\n')) { + break; } } } - } \ No newline at end of file From ba428fecdb309846b9a6dc8a6a3d738f2034f2ff Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 19 Mar 2024 16:25:09 +0000 Subject: [PATCH 8/9] Add `-json-errors` --- src/build_settings.cpp | 8 ++- src/error.cpp | 107 ++++++++++++++++++++++++++++++++++++----- src/main.cpp | 10 ++++ 3 files changed, 110 insertions(+), 15 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index c4073f329..58b5c9170 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -392,6 +392,7 @@ struct BuildContext { bool warnings_as_errors; bool hide_error_line; bool terse_errors; + bool json_errors; bool has_ansi_terminal_colours; bool ignore_lazy; @@ -1270,14 +1271,17 @@ gb_internal String get_fullpath_core_collection(gbAllocator a, String path, bool } gb_internal bool show_error_line(void) { - return !build_context.hide_error_line; + return !build_context.hide_error_line && !build_context.json_errors; } gb_internal bool terse_errors(void) { return build_context.terse_errors; } +gb_internal bool json_errors(void) { + return build_context.json_errors; +} gb_internal bool has_ansi_terminal_colours(void) { - return build_context.has_ansi_terminal_colours; + return build_context.has_ansi_terminal_colours && !json_errors(); } gb_internal bool has_asm_extension(String const &path) { diff --git a/src/error.cpp b/src/error.cpp index d116781fb..509470602 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -6,6 +6,7 @@ enum ErrorValueKind : u32 { struct ErrorValue { ErrorValueKind kind; TokenPos pos; + TokenPos end; Array msgs; }; @@ -146,6 +147,7 @@ gb_internal bool global_warnings_as_errors(void); gb_internal bool global_ignore_warnings(void); gb_internal bool show_error_line(void); gb_internal bool terse_errors(void); +gb_internal bool json_errors(void); gb_internal bool has_ansi_terminal_colours(void); gb_internal gbString get_file_line_as_string(TokenPos const &pos, i32 *offset); @@ -168,13 +170,11 @@ gb_internal ERROR_OUT_PROC(default_error_out_va) { isize len = gb_snprintf_va(buf, gb_size_of(buf), fmt, va); isize n = len-1; - String msg = {}; - if (n) { - msg = copy_string(permanent_allocator(), {(u8 *)buf, n}); + if (n > 0) { + String msg = copy_string(permanent_allocator(), {(u8 *)buf, n}); + ErrorValue *ev = get_error_value(); + array_add(&ev->msgs, msg); } - - ErrorValue *ev = get_error_value(); - array_add(&ev->msgs, msg); } gb_global ErrorOutProc *error_out_va = default_error_out_va; @@ -246,6 +246,7 @@ gb_internal void terminal_reset_colours(void) { gb_internal bool show_error_on_line(TokenPos const &pos, TokenPos end) { + get_error_value()->end = end; if (!show_error_line()) { return false; } @@ -622,19 +623,99 @@ gb_internal int error_value_cmp(void const *a, void const *b) { } gb_internal void print_all_errors(void) { + auto const &escape_char = [](gbFile *f, u8 c) { + switch (c) { + case '\n': gb_file_write(f, "\\n", 2); break; + case '"': gb_file_write(f, "\\\"", 2); break; + case '\\': gb_file_write(f, "\\\\", 2); break; + case '\b': gb_file_write(f, "\\b", 2); break; + case '\f': gb_file_write(f, "\\f", 2); break; + case '\r': gb_file_write(f, "\\r", 2); break; + case '\t': gb_file_write(f, "\\t", 2); break; + default: + if ('\x00' <= c && c <= '\x1f') { + gb_fprintf(f, "\\u%04x", c); + } else { + gb_file_write(f, &c, 1); + } + break; + } + }; + GB_ASSERT(any_errors()); gbFile *f = gb_file_get_standard(gbFileStandard_Error); array_sort(global_error_collector.error_values, error_value_cmp); - for_array(i, global_error_collector.error_values) { - ErrorValue ev = global_error_collector.error_values[i]; - for (isize j = 0; j < ev.msgs.count; j++) { - String msg = ev.msgs[j]; - gb_file_write(f, msg.text, msg.len); - if (terse_errors() && string_contains_char(msg, '\n')) { - break; + if (json_errors()) { + gb_fprintf(f, "{\n"); + gb_fprintf(f, "\t\"error_count\": %td,\n", global_error_collector.error_values.count); + gb_fprintf(f, "\t\"errors\": [\n"); + for_array(i, global_error_collector.error_values) { + ErrorValue ev = global_error_collector.error_values[i]; + + gb_fprintf(f, "\t\t{\n"); + + gb_fprintf(f, "\t\t\t\"pos\": {\n"); + + if (ev.pos.file_id) { + gb_fprintf(f, "\t\t\t\t\"file\": \""); + String file = get_file_path_string(ev.pos.file_id); + for (isize k = 0; k < file.len; k++) { + escape_char(f, file.text[k]); + } + gb_fprintf(f, "\",\n"); + gb_fprintf(f, "\t\t\t\t\"line\": %d,\n", ev.pos.line); + gb_fprintf(f, "\t\t\t\t\"column\": %d,\n", ev.pos.column); + i32 end_column = gb_max(ev.end.column, ev.pos.column); + gb_fprintf(f, "\t\t\t\t\"end_column\": %d\n", end_column); + gb_fprintf(f, "\t\t\t},\n"); + } + + gb_fprintf(f, "\t\t\t\"msgs\": [\n"); + + if (ev.msgs.count > 1) { + gb_fprintf(f, "\t\t\t\t\""); + + for (isize j = 1; j < ev.msgs.count; j++) { + String msg = ev.msgs[j]; + for (isize k = 0; k < msg.len; k++) { + u8 c = msg.text[k]; + if (c == '\n') { + if (k+1 == msg.len && j+1 == ev.msgs.count) { + // don't do the last one + } else { + gb_fprintf(f, "\",\n"); + gb_fprintf(f, "\t\t\t\t\""); + } + } else { + escape_char(f, c); + } + } + } + gb_fprintf(f, "\"\n"); + } + gb_fprintf(f, "\t\t\t]\n"); + gb_fprintf(f, "\t\t}"); + if (i+1 != global_error_collector.error_values.count) { + gb_fprintf(f, ","); + } + gb_fprintf(f, "\n"); + } + + gb_fprintf(f, "\t]\n"); + gb_fprintf(f, "}\n"); + } else { + for_array(i, global_error_collector.error_values) { + ErrorValue ev = global_error_collector.error_values[i]; + for (isize j = 0; j < ev.msgs.count; j++) { + String msg = ev.msgs[j]; + gb_file_write(f, msg.text, msg.len); + + if (terse_errors() && string_contains_char(msg, '\n')) { + break; + } } } } diff --git a/src/main.cpp b/src/main.cpp index 0f28e137f..672a9318e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -292,6 +292,7 @@ enum BuildFlagKind { BuildFlag_WarningsAsErrors, BuildFlag_TerseErrors, BuildFlag_VerboseErrors, + BuildFlag_JsonErrors, BuildFlag_ErrorPosStyle, BuildFlag_MaxErrorCount, @@ -480,6 +481,7 @@ gb_internal bool parse_build_flags(Array args) { add_flag(&build_flags, BuildFlag_WarningsAsErrors, str_lit("warnings-as-errors"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_TerseErrors, str_lit("terse-errors"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_VerboseErrors, str_lit("verbose-errors"), BuildFlagParam_None, Command_all); + add_flag(&build_flags, BuildFlag_JsonErrors, str_lit("json-errors"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_ErrorPosStyle, str_lit("error-pos-style"), BuildFlagParam_String, Command_all); add_flag(&build_flags, BuildFlag_MaxErrorCount, str_lit("max-error-count"), BuildFlagParam_Integer, Command_all); @@ -1184,6 +1186,10 @@ gb_internal bool parse_build_flags(Array args) { build_context.terse_errors = false; break; + case BuildFlag_JsonErrors: + build_context.json_errors = true; + break; + case BuildFlag_ErrorPosStyle: GB_ASSERT(value.kind == ExactValue_String); @@ -1984,6 +1990,10 @@ gb_internal void print_show_help(String const arg0, String const &command) { print_usage_line(2, "Prints a terse error message without showing the code on that line and the location in that line."); print_usage_line(0, ""); + print_usage_line(1, "-json-errors"); + print_usage_line(2, "Prints the error messages as json to stderr."); + print_usage_line(0, ""); + print_usage_line(1, "-error-pos-style:"); print_usage_line(2, "Available options:"); print_usage_line(3, "-error-pos-style:unix file/path:45:3:"); From 433109ff52d2db76069273cd53b7aebf6aea9be0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 19 Mar 2024 16:29:45 +0000 Subject: [PATCH 9/9] Replace `gb_exit(1)` with `exit_with_errors()` where appropriate --- src/checker.cpp | 4 ++-- src/docs_writer.cpp | 2 +- src/error.cpp | 4 ++++ src/llvm_backend.cpp | 14 +++++++------- src/main.cpp | 2 +- src/parser.cpp | 4 ++-- 6 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/checker.cpp b/src/checker.cpp index 836f803fc..0efe61fba 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1204,7 +1204,7 @@ gb_internal void init_universal(void) { } if (defined_values_double_declaration) { - gb_exit(1); + exit_with_errors(); } @@ -4504,7 +4504,7 @@ gb_internal void add_import_dependency_node(Checker *c, Ast *decl, PtrMapscope != nullptr); diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp index 26d8027a9..824445ed5 100644 --- a/src/docs_writer.cpp +++ b/src/docs_writer.cpp @@ -1170,7 +1170,7 @@ gb_internal void odin_doc_write_to_file(OdinDocWriter *w, char const *filename) gbFileError err = gb_file_open_mode(&f, gbFileMode_Write, filename); if (err != gbFileError_None) { gb_printf_err("Failed to write .odin-doc to: %s\n", filename); - gb_exit(1); + exit_with_errors(); return; } defer (gb_file_close(&f)); diff --git a/src/error.cpp b/src/error.cpp index 509470602..8d550e969 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -613,6 +613,10 @@ gb_internal void compiler_error(char const *fmt, ...) { } +gb_internal void exit_with_errors(void) { + print_all_errors(); + gb_exit(1); +} diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index b8ee7e7fa..cc9b3ac5d 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -1350,7 +1350,7 @@ gb_internal WORKER_TASK_PROC(lb_llvm_emit_worker_proc) { if (LLVMTargetMachineEmitToFile(wd->target_machine, wd->m->mod, cast(char *)wd->filepath_obj.text, wd->code_gen_file_type, &llvm_error)) { gb_printf_err("LLVM Error: %s\n", llvm_error); - gb_exit(1); + exit_with_errors(); } debugf("Generated File: %.*s\n", LIT(wd->filepath_obj)); return 0; @@ -1919,7 +1919,7 @@ verify gb_printf_err("LLVM Error: %s\n", llvm_error); } } - gb_exit(1); + exit_with_errors(); return 1; } #endif @@ -2104,11 +2104,11 @@ gb_internal WORKER_TASK_PROC(lb_llvm_module_verification_worker_proc) { String filepath_ll = lb_filepath_ll_for_module(m); if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) { gb_printf_err("LLVM Error: %s\n", llvm_error); - gb_exit(1); + exit_with_errors(); return false; } } - gb_exit(1); + exit_with_errors(); return 1; } return 0; @@ -2193,7 +2193,7 @@ gb_internal bool lb_llvm_object_generation(lbGenerator *gen, bool do_threading) if (LLVMTargetMachineEmitToFile(m->target_machine, m->mod, cast(char *)filepath_obj.text, code_gen_file_type, &llvm_error)) { gb_printf_err("LLVM Error: %s\n", llvm_error); - gb_exit(1); + exit_with_errors(); return false; } debugf("Generated File: %.*s\n", LIT(filepath_obj)); @@ -2393,7 +2393,7 @@ gb_internal void lb_generate_procedure(lbModule *m, lbProcedure *p) { gb_printf_err("LLVM Error: %s\n", llvm_error); } LLVMVerifyFunction(p->value, LLVMPrintMessageAction); - gb_exit(1); + exit_with_errors(); } } @@ -2962,7 +2962,7 @@ gb_internal bool lb_generate_code(lbGenerator *gen) { String filepath_ll = lb_filepath_ll_for_module(m); if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) { gb_printf_err("LLVM Error: %s\n", llvm_error); - gb_exit(1); + exit_with_errors(); return false; } array_add(&gen->output_temp_paths, filepath_ll); diff --git a/src/main.cpp b/src/main.cpp index 672a9318e..ab721a143 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1404,7 +1404,7 @@ gb_internal void timings_export_all(Timings *t, Checker *c, bool timings_are_fin gbFileError err = gb_file_open_mode(&f, gbFileMode_Write, fileName); if (err != gbFileError_None) { gb_printf_err("Failed to export timings to: %s\n", fileName); - gb_exit(1); + exit_with_errors(); return; } else { gb_printf("\nExporting timings to '%s'... ", fileName); diff --git a/src/parser.cpp b/src/parser.cpp index 14035d6d7..1aa40ccbf 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1484,7 +1484,7 @@ gb_internal Token expect_token(AstFile *f, TokenKind kind) { String p = token_to_string(prev); syntax_error(f->curr_token, "Expected '%.*s', got '%.*s'", LIT(c), LIT(p)); if (prev.kind == Token_EOF) { - gb_exit(1); + exit_with_errors(); } } @@ -6177,7 +6177,7 @@ gb_internal ParseFileError process_imported_file(Parser *p, ImportedFile importe if (err == ParseFile_EmptyFile) { if (fi.fullpath == p->init_fullpath) { syntax_error(pos, "Initial file is empty - %.*s\n", LIT(p->init_fullpath)); - gb_exit(1); + exit_with_errors(); } } else { switch (err) {