Skip to content

Commit fe59171

Browse files
authored
Add showing KCL variables up to the point of the error (#7762)
* Add showing KCL variables up to the point of the error * Add output of memory when sim tests fail * Update output * Fix so that sim tests can distinguish success from error * Update output after adding success files
1 parent 06a743d commit fe59171

File tree

241 files changed

+15907
-27
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

241 files changed

+15907
-27
lines changed

rust/kcl-lib/src/errors.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity};
88
use crate::execution::{ArtifactCommand, ArtifactGraph, Operation};
99
use crate::{
1010
ModuleId,
11+
exec::KclValue,
1112
execution::DefaultPlanes,
1213
lsp::IntoDiagnostic,
1314
modules::{ModulePath, ModuleSource},
@@ -133,6 +134,9 @@ impl From<KclErrorWithOutputs> for KclError {
133134
pub struct KclErrorWithOutputs {
134135
pub error: KclError,
135136
pub non_fatal: Vec<CompilationError>,
137+
/// Variables in the top-level of the root module. Note that functions will
138+
/// have an invalid env ref.
139+
pub variables: IndexMap<String, KclValue>,
136140
#[cfg(feature = "artifact-graph")]
137141
pub operations: Vec<Operation>,
138142
// TODO: Remove this field. Doing so breaks the ts-rs output for some
@@ -151,6 +155,7 @@ impl KclErrorWithOutputs {
151155
pub fn new(
152156
error: KclError,
153157
non_fatal: Vec<CompilationError>,
158+
variables: IndexMap<String, KclValue>,
154159
#[cfg(feature = "artifact-graph")] operations: Vec<Operation>,
155160
#[cfg(feature = "artifact-graph")] artifact_commands: Vec<ArtifactCommand>,
156161
#[cfg(feature = "artifact-graph")] artifact_graph: ArtifactGraph,
@@ -161,6 +166,7 @@ impl KclErrorWithOutputs {
161166
Self {
162167
error,
163168
non_fatal,
169+
variables,
164170
#[cfg(feature = "artifact-graph")]
165171
operations,
166172
#[cfg(feature = "artifact-graph")]
@@ -176,6 +182,7 @@ impl KclErrorWithOutputs {
176182
Self {
177183
error,
178184
non_fatal: Default::default(),
185+
variables: Default::default(),
179186
#[cfg(feature = "artifact-graph")]
180187
operations: Default::default(),
181188
#[cfg(feature = "artifact-graph")]

rust/kcl-lib/src/execution/exec_ast.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ impl ExecutorContext {
8888
path: &ModulePath,
8989
) -> Result<
9090
(Option<KclValue>, EnvironmentRef, Vec<String>, ModuleArtifactState),
91-
(KclError, Option<ModuleArtifactState>),
91+
(KclError, Option<EnvironmentRef>, Option<ModuleArtifactState>),
9292
> {
9393
crate::log::log(format!("enter module {path} {}", exec_state.stack()));
9494

@@ -100,7 +100,7 @@ impl ExecutorContext {
100100
let no_prelude = self
101101
.handle_annotations(program.inner_attrs.iter(), crate::execution::BodyType::Root, exec_state)
102102
.await
103-
.map_err(|err| (err, None))?;
103+
.map_err(|err| (err, None, None))?;
104104

105105
if !preserve_mem {
106106
exec_state.mut_stack().push_new_root_env(!no_prelude);
@@ -125,7 +125,7 @@ impl ExecutorContext {
125125
crate::log::log(format!("leave {path}"));
126126

127127
result
128-
.map_err(|err| (err, Some(module_artifacts.clone())))
128+
.map_err(|err| (err, Some(env_ref), Some(module_artifacts.clone())))
129129
.map(|result| (result, env_ref, local_state.module_exports, module_artifacts))
130130
}
131131

@@ -644,7 +644,7 @@ impl ExecutorContext {
644644

645645
// TODO: ModuleArtifactState is getting dropped here when there's an
646646
// error. Should we propagate it for non-root modules?
647-
result.map_err(|(err, _)| {
647+
result.map_err(|(err, _, _)| {
648648
if let KclError::ImportCycle { .. } = err {
649649
// It was an import cycle. Keep the original message.
650650
err.override_source_ranges(vec![source_range])

rust/kcl-lib/src/execution/mod.rs

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -842,7 +842,7 @@ impl ExecutorContext {
842842
.map_err(KclErrorWithOutputs::no_outputs)?;
843843

844844
for modules in import_graph::import_graph(&universe, self)
845-
.map_err(|err| exec_state.error_with_outputs(err, default_planes.clone()))?
845+
.map_err(|err| exec_state.error_with_outputs(err, None, default_planes.clone()))?
846846
.into_iter()
847847
{
848848
#[cfg(not(target_arch = "wasm32"))]
@@ -982,7 +982,7 @@ impl ExecutorContext {
982982
exec_state.global.module_infos[&module_id].restore_repr(repr);
983983
}
984984
Err(e) => {
985-
return Err(exec_state.error_with_outputs(e, default_planes));
985+
return Err(exec_state.error_with_outputs(e, None, default_planes));
986986
}
987987
}
988988
}
@@ -1020,7 +1020,7 @@ impl ExecutorContext {
10201020
exec_state,
10211021
)
10221022
.await
1023-
.map_err(|err| exec_state.error_with_outputs(err, default_planes))?;
1023+
.map_err(|err| exec_state.error_with_outputs(err, None, default_planes))?;
10241024

10251025
Ok((universe, root_imports))
10261026
}
@@ -1130,7 +1130,7 @@ impl ExecutorContext {
11301130
));
11311131
crate::log::log(format!("Engine stats: {:?}", self.engine.stats()));
11321132

1133-
let env_ref = result.map_err(|e| exec_state.error_with_outputs(e, default_planes))?;
1133+
let env_ref = result.map_err(|(err, env_ref)| exec_state.error_with_outputs(err, env_ref, default_planes))?;
11341134

11351135
if !self.is_mock() {
11361136
let mut mem = exec_state.stack().deep_clone();
@@ -1149,7 +1149,7 @@ impl ExecutorContext {
11491149
program: NodeRef<'_, crate::parsing::ast::types::Program>,
11501150
exec_state: &mut ExecState,
11511151
preserve_mem: bool,
1152-
) -> Result<EnvironmentRef, KclError> {
1152+
) -> Result<EnvironmentRef, (KclError, Option<EnvironmentRef>)> {
11531153
// Don't early return! We need to build other outputs regardless of
11541154
// whether execution failed.
11551155

@@ -1159,7 +1159,8 @@ impl ExecutorContext {
11591159
let start_op = exec_state.global.root_module_artifacts.operations.len();
11601160

11611161
self.eval_prelude(exec_state, SourceRange::from(program).start_as_range())
1162-
.await?;
1162+
.await
1163+
.map_err(|e| (e, None))?;
11631164

11641165
let exec_result = self
11651166
.exec_module_body(
@@ -1176,13 +1177,13 @@ impl ExecutorContext {
11761177
exec_state.global.root_module_artifacts.extend(module_artifacts);
11771178
env_ref
11781179
})
1179-
.map_err(|(err, module_artifacts)| {
1180+
.map_err(|(err, env_ref, module_artifacts)| {
11801181
if let Some(module_artifacts) = module_artifacts {
11811182
// We need to extend because it may already have operations
11821183
// from imports.
11831184
exec_state.global.root_module_artifacts.extend(module_artifacts);
11841185
}
1185-
err
1186+
(err, env_ref)
11861187
});
11871188

11881189
#[cfg(feature = "artifact-graph")]
@@ -1208,15 +1209,21 @@ impl ExecutorContext {
12081209
}
12091210

12101211
// Ensure all the async commands completed.
1211-
self.engine.ensure_async_commands_completed().await?;
1212+
self.engine.ensure_async_commands_completed().await.map_err(|e| {
1213+
match &exec_result {
1214+
Ok(env_ref) => (e, Some(*env_ref)),
1215+
// Prefer the execution error.
1216+
Err((exec_err, env_ref)) => (exec_err.clone(), *env_ref),
1217+
}
1218+
})?;
12121219

12131220
// If we errored out and early-returned, there might be commands which haven't been executed
12141221
// and should be dropped.
12151222
self.engine.clear_queues().await;
12161223

12171224
match exec_state.build_artifact_graph(&self.engine, program).await {
12181225
Ok(_) => exec_result,
1219-
Err(err) => exec_result.and(Err(err)),
1226+
Err(err) => exec_result.and_then(|env_ref| Err((err, Some(env_ref)))),
12201227
}
12211228
}
12221229

rust/kcl-lib/src/execution/state.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ impl ExecState {
302302
pub(crate) fn error_with_outputs(
303303
&self,
304304
error: KclError,
305+
main_ref: Option<EnvironmentRef>,
305306
default_planes: Option<DefaultPlanes>,
306307
) -> KclErrorWithOutputs {
307308
let module_id_to_module_path: IndexMap<ModuleId, ModulePath> = self
@@ -314,6 +315,9 @@ impl ExecState {
314315
KclErrorWithOutputs::new(
315316
error,
316317
self.errors().to_vec(),
318+
main_ref
319+
.map(|main_ref| self.mod_local.variables(main_ref))
320+
.unwrap_or_default(),
317321
#[cfg(feature = "artifact-graph")]
318322
self.global.root_module_artifacts.operations.clone(),
319323
#[cfg(feature = "artifact-graph")]

rust/kcl-lib/src/simulation_tests.rs

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use indexmap::IndexMap;
88
use crate::{
99
ExecOutcome, ExecState, ExecutorContext, ModuleId,
1010
errors::KclError,
11+
exec::KclValue,
1112
execution::{EnvironmentRef, ModuleArtifactState},
1213
};
1314
#[cfg(feature = "artifact-graph")]
@@ -259,24 +260,25 @@ async fn execute_test(test: &Test, render_to_png: bool, export_step: bool) {
259260
panic!("Step data was empty");
260261
}
261262
}
262-
let (outcome, module_state) = exec_state.into_test_exec_outcome(env_ref, &ctx, &test.input_dir).await;
263-
264-
let mem_result = catch_unwind(AssertUnwindSafe(|| {
265-
assert_snapshot(test, "Variables in memory after executing", || {
266-
insta::assert_json_snapshot!("program_memory", outcome.variables, {
267-
".**.sourceRange" => Vec::new(),
268-
})
263+
let ok_snap = catch_unwind(AssertUnwindSafe(|| {
264+
assert_snapshot(test, "Execution success", || {
265+
insta::assert_json_snapshot!("execution_success", ())
269266
})
270267
}));
271268

269+
let (outcome, module_state) = exec_state.into_test_exec_outcome(env_ref, &ctx, &test.input_dir).await;
270+
271+
assert_common_snapshots(test, outcome.variables);
272+
272273
#[cfg(not(feature = "artifact-graph"))]
273274
drop(module_state);
274275
#[cfg(feature = "artifact-graph")]
275276
assert_artifact_snapshots(test, module_state, outcome.artifact_graph);
276-
mem_result.unwrap();
277+
278+
ok_snap.unwrap();
277279
}
278280
Err(e) => {
279-
let ok_path = test.output_dir.join("program_memory.snap");
281+
let ok_path = test.output_dir.join("execution_success.snap");
280282
let previously_passed = std::fs::exists(&ok_path).unwrap();
281283
match e.error {
282284
crate::errors::ExecError::Kcl(error) => {
@@ -304,6 +306,8 @@ async fn execute_test(test: &Test, render_to_png: bool, export_step: bool) {
304306
})
305307
}));
306308

309+
assert_common_snapshots(test, error.variables);
310+
307311
#[cfg(feature = "artifact-graph")]
308312
{
309313
let module_state = e
@@ -325,6 +329,19 @@ async fn execute_test(test: &Test, render_to_png: bool, export_step: bool) {
325329
}
326330
}
327331

332+
/// Assert snapshots that should happen both when KCL execution succeeds and
333+
/// when it results in an error.
334+
fn assert_common_snapshots(test: &Test, variables: IndexMap<String, KclValue>) {
335+
let mem_result = catch_unwind(AssertUnwindSafe(|| {
336+
assert_snapshot(test, "Variables in memory after executing", || {
337+
insta::assert_json_snapshot!("program_memory", variables, {
338+
".**.sourceRange" => Vec::new(),
339+
})
340+
})
341+
}));
342+
mem_result.unwrap();
343+
}
344+
328345
/// Assert snapshots for artifacts that should happen both when KCL execution
329346
/// succeeds and when it results in an error.
330347
#[cfg(feature = "artifact-graph")]
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
source: kcl-lib/src/simulation_tests.rs
3+
description: Variables in memory after executing add_arrays.kcl
4+
---
5+
{}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
source: kcl-lib/src/simulation_tests.rs
3+
description: Execution success add_lots.kcl
4+
---
5+
null
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
source: kcl-lib/src/simulation_tests.rs
3+
description: Execution success angled_line.kcl
4+
---
5+
null
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
source: kcl-lib/src/simulation_tests.rs
3+
description: Execution success any_type.kcl
4+
---
5+
null
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
source: kcl-lib/src/simulation_tests.rs
3+
description: Variables in memory after executing argument_error.kcl
4+
---
5+
{
6+
"f": {
7+
"type": "Function",
8+
"value": null
9+
}
10+
}

0 commit comments

Comments
 (0)