Skip to main content

bop/
error_messages.rs

1//! Shared error-message format helpers.
2//!
3//! Walker, VM, and AOT each previously carried their own copy
4//! of `format!("Variable `{}` not found", name)` and friends.
5//! The strings are usually identical but the differential
6//! tests compare errors across engines by `.message` text, so
7//! any drift would surface as a spurious test failure — or
8//! worse, in one engine and not another if a differential test
9//! didn't happen to exercise the path.
10//!
11//! Collecting the common messages here means:
12//!
13//! - One edit when a message needs rewording.
14//! - The function signature documents the intended arguments
15//!   (you can't accidentally swap `type_name` and `field`).
16//! - AOT-emitted code calls these same helpers, so the
17//!   runtime-generated error text is byte-identical to the
18//!   walker's without the AOT needing to paste format strings.
19//!
20//! Only messages with 2+ copies across engines live here. One-
21//! off per-engine messages (e.g. `"VM: stack underflow"`) stay
22//! with their engine — there's nothing to deduplicate.
23
24#[cfg(feature = "no_std")]
25use alloc::{format, string::String};
26
27/// `Variable `<name>` not found`.
28pub fn variable_not_found(name: &str) -> String {
29    format!("Variable `{}` not found", name)
30}
31
32/// `Function `<name>` not found`.
33pub fn function_not_found(name: &str) -> String {
34    format!("Function `{}` not found", name)
35}
36
37/// `Struct `<type_name>` has no field `<field>``.
38/// Used by the walker's `FieldAccess` path, the VM's
39/// `ConstructStruct` / `FieldGet` / `FieldSet`, and the AOT's
40/// runtime `__bop_field_get` helper.
41pub fn struct_has_no_field(type_name: &str, field: &str) -> String {
42    format!("Struct `{}` has no field `{}`", type_name, field)
43}
44
45/// `Variant `<type_name>::<variant>` has no field `<field>``.
46/// For struct-shaped enum-variant field reads.
47pub fn variant_has_no_field(type_name: &str, variant: &str, field: &str) -> String {
48    format!(
49        "Variant `{}::{}` has no field `{}`",
50        type_name, variant, field
51    )
52}
53
54/// `Struct `<type_name>` is not declared`.
55pub fn struct_not_declared(type_name: &str) -> String {
56    format!("Struct `{}` is not declared", type_name)
57}
58
59/// `Enum `<type_name>` is not declared`.
60pub fn enum_not_declared(type_name: &str) -> String {
61    format!("Enum `{}` is not declared", type_name)
62}
63
64/// `Enum `<type_name>` has no variant `<variant>``.
65pub fn enum_has_no_variant(type_name: &str, variant: &str) -> String {
66    format!("Enum `{}` has no variant `{}`", type_name, variant)
67}
68
69/// `Can't read field `<field>` on <kind>` — `kind` is the
70/// pretty type name (`"array"`, `"int"`, etc.).
71pub fn cant_read_field(field: &str, kind: &str) -> String {
72    format!("Can't read field `{}` on {}", field, kind)
73}
74
75/// `Can't assign to field `<field>` on <kind>`.
76pub fn cant_assign_field(field: &str, kind: &str) -> String {
77    format!("Can't assign to field `{}` on {}", field, kind)
78}
79
80/// `Can't call a <kind>` — when a non-`Value::Fn` value lands
81/// in a call position.
82pub fn cant_call_a(kind: &str) -> String {
83    format!("Can't call a {}", kind)
84}
85
86/// `Can't iterate over <kind>` — when `for x in …` gets a
87/// value that isn't array-like or string-like.
88pub fn cant_iterate_over(kind: &str) -> String {
89    format!("Can't iterate over {}", kind)
90}
91
92/// `<kind> doesn't have a .<method>() method` — the terminal
93/// error from the method dispatcher when nothing matches.
94pub fn no_such_method(kind: &str, method: &str) -> String {
95    format!("{} doesn't have a .{}() method", kind, method)
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101
102    #[test]
103    fn variable_not_found_format() {
104        assert_eq!(variable_not_found("x"), "Variable `x` not found");
105    }
106
107    #[test]
108    fn struct_has_no_field_matches_legacy_format() {
109        // The legacy `format!` spelled this with backticks
110        // around both `type_name` and `field`. Locking it down
111        // here so a rewrite can't drift without updating this
112        // test too.
113        assert_eq!(
114            struct_has_no_field("Point", "z"),
115            "Struct `Point` has no field `z`"
116        );
117    }
118
119    #[test]
120    fn variant_has_no_field_uses_double_colon() {
121        assert_eq!(
122            variant_has_no_field("Shape", "Rect", "r"),
123            "Variant `Shape::Rect` has no field `r`"
124        );
125    }
126
127    #[test]
128    fn cant_read_field_formats_without_backticks_around_kind() {
129        // The kind (type name) is a user-facing noun like
130        // "array" — no backticks around it, matching the
131        // legacy walker phrasing.
132        assert_eq!(
133            cant_read_field("x", "array"),
134            "Can't read field `x` on array"
135        );
136    }
137}