From 107f7ba3137bc53aff25475e07d565f1f5c20b92 Mon Sep 17 00:00:00 2001 From: Karl Zylinski Date: Thu, 20 Aug 2026 16:13:08 +0200 Subject: [PATCH] Give better hints for what to write instead of `0` when you do `x = 0` or `x == 0` where `0` is not valid. Here are the rules for how it hints: string -> "" bool -> false bit_set -> {} anything with a nil -> nil everything else -> {} This adds a hint for structs, arrays, matrices, bit_fields, `#no_nil` unions, `rawptr`, `typeid`, the four string types and `bool`. A `bit_set` now suggests `{}` rather than `nil`. Co-Authored-By: Claude Opus 5 --- src/check_expr.cpp | 57 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 39409777b..993117177 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -100,6 +100,7 @@ gb_internal Type * check_init_variable (CheckerContext *c, Entity * gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type, i64 max_bit_size=0); +gb_internal char const *zero_value_suggestion(Operand *o, Type *type); gb_internal bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *type); gb_internal void add_map_key_type_dependencies(CheckerContext *ctx, Type *key); @@ -2697,6 +2698,35 @@ gb_internal bool check_integer_exceed_suggestion(CheckerContext *c, Operand *o, } return false; } + +// Returns how the empty value of `type` should be spelled when a numeric zero was written, +// or nullptr if there is nothing worth suggesting. +gb_internal char const *zero_value_suggestion(Operand *o, Type *type) { + if (o->value.kind != ExactValue_Integer && o->value.kind != ExactValue_Float) { + return nullptr; + } + if (!is_exact_value_zero(o->value)) { + return nullptr; + } + + char const *suggestion = nullptr; + if (is_type_string(type)) { + suggestion = "\"\""; + } else if (is_type_boolean(type)) { + suggestion = "false"; + } else if (is_type_bit_set(type)) { + // A bit_set accepts both `nil` and `{}`. `{}` is a bit more idiomatic + // because `{.Something}` becomes `{}` when no bits are set. + suggestion = "{}"; + } else if (type_has_nil(type)) { + suggestion = "nil"; + } else { + suggestion = "{}"; + } + + return suggestion; +} + gb_internal void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type, i64 max_bit_size) { gbString a = expr_to_string(o->expr); gbString b = type_to_string(type); @@ -2832,7 +2862,14 @@ gb_internal bool check_is_expressible(CheckerContext *ctx, Operand *o, Type *typ check_assignment_error_suggestion(ctx, o, type, max_bit_size); } } else { - error(o->expr, "Cannot convert '%s' to '%s' from '%s', got %s", a, b, c, s); + char suggestion_buf[64] = {}; + char const *extra_text = ""; + if (char const *suggestion = zero_value_suggestion(o, type)) { + gb_snprintf(suggestion_buf, gb_size_of(suggestion_buf), " - Did you want '%s'?", suggestion); + extra_text = suggestion_buf; + } + + error(o->expr, "Cannot convert '%s' to '%s' from '%s', got %s%s", a, b, c, s, extra_text); check_assignment_error_suggestion(ctx, o, type); } return false; @@ -5058,20 +5095,14 @@ gb_internal void convert_untyped_error(CheckerContext *c, Operand *operand, Type gbString expr_str = expr_to_string(operand->expr); gbString type_str = type_to_string(target_type); gbString from_type_str = type_to_string(operand->type); + + char suggestion_buf[64] = {}; char const *extra_text = ""; - - if (operand->mode == Addressing_Constant && type_has_nil(target_type)) { - bool is_zero_int_or_float = - (operand->value.kind == ExactValue_Integer || operand->value.kind == ExactValue_Float) && - is_exact_value_zero(operand->value); - - if (is_zero_int_or_float) { - if (make_string_c(expr_str) != "nil") { // HACK NOTE(bill): Just in case - // NOTE(bill): Doesn't matter what the type is as it's still zero in the union - extra_text = " - Did you want 'nil'?"; - } - } + if (char const *suggestion = zero_value_suggestion(operand, target_type)) { + gb_snprintf(suggestion_buf, gb_size_of(suggestion_buf), " - Did you want '%s'?", suggestion); + extra_text = suggestion_buf; } + if (!ignore_error_block) { begin_error_block(); }