KCL: Fix 'cryptic' error when referencing a variable in its own declaration (#7325)

Previously, `x = cos(x)` would just say "`x` is undefined". Now it says that `x` cannot be referenced in its own definition, try using a different variable instead.

To do this, I've added a new `Option<String>` field to the mod-local executor context, tracking the current variable declaration. This means cloning some strings, implying a small performance hit. I think it's fine, for the better diagnostics.

In the future we could refactor this to use a &str or store variable labels in stack-allocated strings like docs.rs/compact_str or something.

Closes https://github.com/KittyCAD/modeling-app/issues/6072
This commit is contained in:
Adam Chalmers
2025-06-02 17:25:55 -05:00
committed by GitHub
parent 2bb6c74f42
commit 7680605085
15 changed files with 230 additions and 39 deletions

View File

@ -108,7 +108,10 @@ pub enum KclError {
#[error("value already defined: {details:?}")]
ValueAlreadyDefined { details: KclErrorDetails },
#[error("undefined value: {details:?}")]
UndefinedValue { details: KclErrorDetails },
UndefinedValue {
details: KclErrorDetails,
name: Option<String>,
},
#[error("invalid expression: {details:?}")]
InvalidExpression { details: KclErrorDetails },
#[error("engine: {details:?}")]
@ -451,8 +454,8 @@ impl KclError {
KclError::Lexical { details }
}
pub fn new_undefined_value(details: KclErrorDetails) -> KclError {
KclError::UndefinedValue { details }
pub fn new_undefined_value(details: KclErrorDetails, name: Option<String>) -> KclError {
KclError::UndefinedValue { details, name }
}
pub fn new_type(details: KclErrorDetails) -> KclError {
@ -491,7 +494,7 @@ impl KclError {
KclError::Io { details: e } => e.source_ranges.clone(),
KclError::Unexpected { details: e } => e.source_ranges.clone(),
KclError::ValueAlreadyDefined { details: e } => e.source_ranges.clone(),
KclError::UndefinedValue { details: e } => e.source_ranges.clone(),
KclError::UndefinedValue { details: e, .. } => e.source_ranges.clone(),
KclError::InvalidExpression { details: e } => e.source_ranges.clone(),
KclError::Engine { details: e } => e.source_ranges.clone(),
KclError::Internal { details: e } => e.source_ranges.clone(),
@ -509,7 +512,7 @@ impl KclError {
KclError::Io { details: e } => &e.message,
KclError::Unexpected { details: e } => &e.message,
KclError::ValueAlreadyDefined { details: e } => &e.message,
KclError::UndefinedValue { details: e } => &e.message,
KclError::UndefinedValue { details: e, .. } => &e.message,
KclError::InvalidExpression { details: e } => &e.message,
KclError::Engine { details: e } => &e.message,
KclError::Internal { details: e } => &e.message,
@ -526,7 +529,7 @@ impl KclError {
| KclError::Io { details: e }
| KclError::Unexpected { details: e }
| KclError::ValueAlreadyDefined { details: e }
| KclError::UndefinedValue { details: e }
| KclError::UndefinedValue { details: e, .. }
| KclError::InvalidExpression { details: e }
| KclError::Engine { details: e }
| KclError::Internal { details: e } => e.backtrace.clone(),
@ -544,7 +547,7 @@ impl KclError {
| KclError::Io { details: e }
| KclError::Unexpected { details: e }
| KclError::ValueAlreadyDefined { details: e }
| KclError::UndefinedValue { details: e }
| KclError::UndefinedValue { details: e, .. }
| KclError::InvalidExpression { details: e }
| KclError::Engine { details: e }
| KclError::Internal { details: e } => {
@ -573,7 +576,7 @@ impl KclError {
| KclError::Io { details: e }
| KclError::Unexpected { details: e }
| KclError::ValueAlreadyDefined { details: e }
| KclError::UndefinedValue { details: e }
| KclError::UndefinedValue { details: e, .. }
| KclError::InvalidExpression { details: e }
| KclError::Engine { details: e }
| KclError::Internal { details: e } => {
@ -597,7 +600,7 @@ impl KclError {
| KclError::Io { details: e }
| KclError::Unexpected { details: e }
| KclError::ValueAlreadyDefined { details: e }
| KclError::UndefinedValue { details: e }
| KclError::UndefinedValue { details: e, .. }
| KclError::InvalidExpression { details: e }
| KclError::Engine { details: e }
| KclError::Internal { details: e } => {