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}