better errors from rust to lsp for execution errors (#5526)
* better errors start Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * conversions Signed-off-by: Jess Frazelle <github@jessfraz.com> * miette update Signed-off-by: Jess Frazelle <github@jessfraz.com> * related errrors test Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * a bit better Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * regenerate other errors Signed-off-by: Jess Frazelle <github@jessfraz.com> * add diagnostics test Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) --------- Signed-off-by: Jess Frazelle <github@jessfraz.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
@ -29,5 +29,5 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"kcl_version": "0.2.39"
|
||||
"kcl_version": "0.2.40"
|
||||
}
|
||||
6
src/wasm-lib/Cargo.lock
generated
@ -730,7 +730,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "derive-docs"
|
||||
version = "0.1.39"
|
||||
version = "0.1.40"
|
||||
dependencies = [
|
||||
"Inflector",
|
||||
"anyhow",
|
||||
@ -1724,7 +1724,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-lib"
|
||||
version = "0.2.39"
|
||||
version = "0.2.40"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"approx 0.5.1",
|
||||
@ -1791,7 +1791,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-test-server"
|
||||
version = "0.1.39"
|
||||
version = "0.1.40"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"hyper 0.14.32",
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "derive-docs"
|
||||
description = "A tool for generating documentation from Rust derive macros"
|
||||
version = "0.1.39"
|
||||
version = "0.1.40"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/KittyCAD/modeling-app"
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-test-server"
|
||||
description = "A test server for KCL"
|
||||
version = "0.1.39"
|
||||
version = "0.1.40"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-lib"
|
||||
description = "KittyCAD Language implementation and tools"
|
||||
version = "0.2.39"
|
||||
version = "0.2.40"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/KittyCAD/modeling-app"
|
||||
@ -22,7 +22,7 @@ clap = { version = "4.5.27", default-features = false, optional = true, features
|
||||
] }
|
||||
convert_case = "0.6.0"
|
||||
dashmap = "6.1.0"
|
||||
derive-docs = { version = "0.1.38", path = "../derive-docs" }
|
||||
derive-docs = { version = "0.1.40", path = "../derive-docs" }
|
||||
dhat = { version = "0.3", optional = true }
|
||||
fnv = "1.0.7"
|
||||
form_urlencoded = "1.2.1"
|
||||
|
||||
@ -13,14 +13,13 @@ use itertools::Itertools;
|
||||
use serde_json::json;
|
||||
use tokio::task::JoinSet;
|
||||
|
||||
use super::kcl_doc::{ConstData, DocData, FnData};
|
||||
use crate::{
|
||||
docs::{is_primitive, StdLibFn},
|
||||
std::StdLib,
|
||||
ExecutorContext,
|
||||
};
|
||||
|
||||
use super::kcl_doc::{ConstData, DocData, FnData};
|
||||
|
||||
const TYPES_DIR: &str = "../../../docs/kcl/types";
|
||||
const LANG_TOPICS: [&str; 4] = ["Types", "Modules", "Settings", "Known Issues"];
|
||||
|
||||
|
||||
@ -1,13 +1,12 @@
|
||||
use indexmap::IndexMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity};
|
||||
|
||||
use indexmap::IndexMap;
|
||||
|
||||
use crate::{
|
||||
execution::{ArtifactCommand, ArtifactGraph, Operation},
|
||||
lsp::IntoDiagnostic,
|
||||
modules::ModulePath,
|
||||
modules::{ModulePath, ModuleSource},
|
||||
source_range::SourceRange,
|
||||
ModuleId,
|
||||
};
|
||||
@ -120,6 +119,7 @@ pub struct KclErrorWithOutputs {
|
||||
pub artifact_commands: Vec<ArtifactCommand>,
|
||||
pub artifact_graph: ArtifactGraph,
|
||||
pub filenames: IndexMap<ModuleId, ModulePath>,
|
||||
pub source_files: IndexMap<ModuleId, ModuleSource>,
|
||||
}
|
||||
|
||||
impl KclErrorWithOutputs {
|
||||
@ -129,6 +129,7 @@ impl KclErrorWithOutputs {
|
||||
artifact_commands: Vec<ArtifactCommand>,
|
||||
artifact_graph: ArtifactGraph,
|
||||
filenames: IndexMap<ModuleId, ModulePath>,
|
||||
source_files: IndexMap<ModuleId, ModuleSource>,
|
||||
) -> Self {
|
||||
Self {
|
||||
error,
|
||||
@ -136,6 +137,7 @@ impl KclErrorWithOutputs {
|
||||
artifact_commands,
|
||||
artifact_graph,
|
||||
filenames,
|
||||
source_files,
|
||||
}
|
||||
}
|
||||
pub fn no_outputs(error: KclError) -> Self {
|
||||
@ -145,8 +147,169 @@ impl KclErrorWithOutputs {
|
||||
artifact_commands: Default::default(),
|
||||
artifact_graph: Default::default(),
|
||||
filenames: Default::default(),
|
||||
source_files: Default::default(),
|
||||
}
|
||||
}
|
||||
pub fn into_miette_report_with_outputs(self, code: &str) -> anyhow::Result<ReportWithOutputs> {
|
||||
let mut source_ranges = self.error.source_ranges();
|
||||
|
||||
// Pop off the first source range to get the filename.
|
||||
let first_source_range = source_ranges
|
||||
.pop()
|
||||
.ok_or_else(|| anyhow::anyhow!("No source ranges found"))?;
|
||||
|
||||
let source = self
|
||||
.source_files
|
||||
.get(&first_source_range.module_id())
|
||||
.cloned()
|
||||
.unwrap_or(ModuleSource {
|
||||
source: code.to_string(),
|
||||
path: self
|
||||
.filenames
|
||||
.get(&first_source_range.module_id())
|
||||
.ok_or_else(|| {
|
||||
anyhow::anyhow!(
|
||||
"Could not find filename for module id: {:?}",
|
||||
first_source_range.module_id()
|
||||
)
|
||||
})?
|
||||
.clone(),
|
||||
});
|
||||
let filename = source.path.to_string();
|
||||
let kcl_source = source.source.to_string();
|
||||
|
||||
let mut related = Vec::new();
|
||||
for source_range in source_ranges {
|
||||
let module_id = source_range.module_id();
|
||||
let source = self.source_files.get(&module_id).cloned().unwrap_or(ModuleSource {
|
||||
source: code.to_string(),
|
||||
path: self
|
||||
.filenames
|
||||
.get(&module_id)
|
||||
.ok_or_else(|| anyhow::anyhow!("Could not find filename for module id: {:?}", module_id))?
|
||||
.clone(),
|
||||
});
|
||||
let error = self.error.override_source_ranges(vec![source_range]);
|
||||
let report = Report {
|
||||
error,
|
||||
kcl_source: source.source.to_string(),
|
||||
filename: source.path.to_string(),
|
||||
};
|
||||
related.push(report);
|
||||
}
|
||||
|
||||
Ok(ReportWithOutputs {
|
||||
error: self,
|
||||
kcl_source,
|
||||
filename,
|
||||
related,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoDiagnostic for KclErrorWithOutputs {
|
||||
fn to_lsp_diagnostics(&self, code: &str) -> Vec<Diagnostic> {
|
||||
let message = self.error.get_message();
|
||||
let source_ranges = self.error.source_ranges();
|
||||
|
||||
source_ranges
|
||||
.into_iter()
|
||||
.map(|source_range| {
|
||||
let source = self
|
||||
.source_files
|
||||
.get(&source_range.module_id())
|
||||
.cloned()
|
||||
.unwrap_or(ModuleSource {
|
||||
source: code.to_string(),
|
||||
path: self.filenames.get(&source_range.module_id()).unwrap().clone(),
|
||||
});
|
||||
let mut filename = source.path.to_string();
|
||||
if !filename.starts_with("file://") {
|
||||
filename = format!("file:///{}", filename.trim_start_matches("/"));
|
||||
}
|
||||
|
||||
let related_information = if let Ok(uri) = url::Url::parse(&filename) {
|
||||
Some(vec![tower_lsp::lsp_types::DiagnosticRelatedInformation {
|
||||
location: tower_lsp::lsp_types::Location {
|
||||
uri,
|
||||
range: source_range.to_lsp_range(&source.source),
|
||||
},
|
||||
message: message.to_string(),
|
||||
}])
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Diagnostic {
|
||||
range: source_range.to_lsp_range(code),
|
||||
severity: Some(self.severity()),
|
||||
code: None,
|
||||
// TODO: this is neat we can pass a URL to a help page here for this specific error.
|
||||
code_description: None,
|
||||
source: Some("kcl".to_string()),
|
||||
related_information,
|
||||
message: message.clone(),
|
||||
tags: None,
|
||||
data: None,
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn severity(&self) -> DiagnosticSeverity {
|
||||
DiagnosticSeverity::ERROR
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
#[error("{}", self.error.error.get_message())]
|
||||
pub struct ReportWithOutputs {
|
||||
pub error: KclErrorWithOutputs,
|
||||
pub kcl_source: String,
|
||||
pub filename: String,
|
||||
pub related: Vec<Report>,
|
||||
}
|
||||
|
||||
impl miette::Diagnostic for ReportWithOutputs {
|
||||
fn code<'a>(&'a self) -> Option<Box<dyn std::fmt::Display + 'a>> {
|
||||
let family = match self.error.error {
|
||||
KclError::Lexical(_) => "Lexical",
|
||||
KclError::Syntax(_) => "Syntax",
|
||||
KclError::Semantic(_) => "Semantic",
|
||||
KclError::ImportCycle(_) => "ImportCycle",
|
||||
KclError::Type(_) => "Type",
|
||||
KclError::Unimplemented(_) => "Unimplemented",
|
||||
KclError::Unexpected(_) => "Unexpected",
|
||||
KclError::ValueAlreadyDefined(_) => "ValueAlreadyDefined",
|
||||
KclError::UndefinedValue(_) => "UndefinedValue",
|
||||
KclError::InvalidExpression(_) => "InvalidExpression",
|
||||
KclError::Engine(_) => "Engine",
|
||||
KclError::Internal(_) => "Internal",
|
||||
};
|
||||
let error_string = format!("KCL {family} error");
|
||||
Some(Box::new(error_string))
|
||||
}
|
||||
|
||||
fn source_code(&self) -> Option<&dyn miette::SourceCode> {
|
||||
Some(&self.kcl_source)
|
||||
}
|
||||
|
||||
fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
|
||||
let iter = self
|
||||
.error
|
||||
.error
|
||||
.source_ranges()
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(miette::SourceSpan::from)
|
||||
.map(|span| miette::LabeledSpan::new_with_span(Some(self.filename.to_string()), span));
|
||||
Some(Box::new(iter))
|
||||
}
|
||||
|
||||
fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn miette::Diagnostic> + 'a>> {
|
||||
let iter = self.related.iter().map(|r| r as &dyn miette::Diagnostic);
|
||||
Some(Box::new(iter))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
@ -188,7 +351,7 @@ impl miette::Diagnostic for Report {
|
||||
.clone()
|
||||
.into_iter()
|
||||
.map(miette::SourceSpan::from)
|
||||
.map(|span| miette::LabeledSpan::new_with_span(None, span));
|
||||
.map(|span| miette::LabeledSpan::new_with_span(Some(self.filename.to_string()), span));
|
||||
Some(Box::new(iter))
|
||||
}
|
||||
}
|
||||
@ -311,7 +474,7 @@ impl KclError {
|
||||
}
|
||||
|
||||
impl IntoDiagnostic for KclError {
|
||||
fn to_lsp_diagnostic(&self, code: &str) -> Diagnostic {
|
||||
fn to_lsp_diagnostics(&self, code: &str) -> Vec<Diagnostic> {
|
||||
let message = self.get_message();
|
||||
let source_ranges = self.source_ranges();
|
||||
|
||||
@ -322,18 +485,23 @@ impl IntoDiagnostic for KclError {
|
||||
.filter(|r| r.module_id() == module_id)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Diagnostic {
|
||||
range: source_ranges.first().map(|r| r.to_lsp_range(code)).unwrap_or_default(),
|
||||
severity: Some(self.severity()),
|
||||
code: None,
|
||||
// TODO: this is neat we can pass a URL to a help page here for this specific error.
|
||||
code_description: None,
|
||||
source: Some("kcl".to_string()),
|
||||
message,
|
||||
related_information: None,
|
||||
tags: None,
|
||||
data: None,
|
||||
let mut diagnostics = Vec::new();
|
||||
for source_range in &source_ranges {
|
||||
diagnostics.push(Diagnostic {
|
||||
range: source_range.to_lsp_range(code),
|
||||
severity: Some(self.severity()),
|
||||
code: None,
|
||||
// TODO: this is neat we can pass a URL to a help page here for this specific error.
|
||||
code_description: None,
|
||||
source: Some("kcl".to_string()),
|
||||
related_information: None,
|
||||
message: message.clone(),
|
||||
tags: None,
|
||||
data: None,
|
||||
});
|
||||
}
|
||||
|
||||
diagnostics
|
||||
}
|
||||
|
||||
fn severity(&self) -> DiagnosticSeverity {
|
||||
|
||||
@ -342,8 +342,9 @@ impl ExecutorContext {
|
||||
// Add file path string to global state even if it fails to import
|
||||
exec_state.add_path_to_source_id(resolved_path.clone(), id);
|
||||
let source = resolved_path.source(&self.fs, source_range).await?;
|
||||
exec_state.add_id_to_source(id, source.clone());
|
||||
// TODO handle parsing errors properly
|
||||
let parsed = crate::parsing::parse_str(&source, id).parse_errs_as_err()?;
|
||||
let parsed = crate::parsing::parse_str(&source.source, id).parse_errs_as_err()?;
|
||||
exec_state.add_module(id, resolved_path, ModuleRepr::Kcl(parsed, None));
|
||||
|
||||
Ok(id)
|
||||
@ -371,7 +372,10 @@ impl ExecutorContext {
|
||||
// Add file path string to global state even if it fails to import
|
||||
exec_state.add_path_to_source_id(resolved_path.clone(), id);
|
||||
let source = resolved_path.source(&self.fs, source_range).await?;
|
||||
let parsed = crate::parsing::parse_str(&source, id).parse_errs_as_err().unwrap();
|
||||
exec_state.add_id_to_source(id, source.clone());
|
||||
let parsed = crate::parsing::parse_str(&source.source, id)
|
||||
.parse_errs_as_err()
|
||||
.unwrap();
|
||||
exec_state.add_module(id, resolved_path, ModuleRepr::Kcl(parsed, None));
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
@ -138,6 +138,7 @@
|
||||
use std::{collections::HashMap, fmt};
|
||||
|
||||
use anyhow::Result;
|
||||
use env::Environment;
|
||||
use indexmap::IndexMap;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -147,7 +148,6 @@ use crate::{
|
||||
execution::KclValue,
|
||||
source_range::SourceRange,
|
||||
};
|
||||
use env::Environment;
|
||||
|
||||
/// The distinguished name of the return value of a function.
|
||||
pub(crate) const RETURN_NAME: &str = "__return";
|
||||
@ -894,9 +894,8 @@ mod env {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::execution::kcl_value::{FunctionSource, NumericType};
|
||||
|
||||
use super::*;
|
||||
use crate::execution::kcl_value::{FunctionSource, NumericType};
|
||||
|
||||
fn sr() -> SourceRange {
|
||||
SourceRange::default()
|
||||
|
||||
@ -3,8 +3,16 @@
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
|
||||
use anyhow::Result;
|
||||
pub use artifact::{Artifact, ArtifactCommand, ArtifactGraph, ArtifactId};
|
||||
use cache::OldAstState;
|
||||
pub use cache::{bust_cache, clear_mem_cache};
|
||||
pub use cad_op::Operation;
|
||||
pub use geometry::*;
|
||||
pub(crate) use import::{
|
||||
import_foreign, send_to_engine as send_import_to_engine, PreImportedGeometry, ZOO_COORD_SYSTEM,
|
||||
};
|
||||
use indexmap::IndexMap;
|
||||
pub use kcl_value::{KclObjectFields, KclValue, UnitAngle, UnitLen};
|
||||
use kcmc::{
|
||||
each_cmd as mcmd,
|
||||
ok_response::{output::TakeSnapshot, OkModelingCmdResponse},
|
||||
@ -12,8 +20,10 @@ use kcmc::{
|
||||
ImageFormat, ModelingCmd,
|
||||
};
|
||||
use kittycad_modeling_cmds as kcmc;
|
||||
pub use memory::EnvironmentRef;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
pub use state::{ExecState, IdGenerator, MetaSettings};
|
||||
|
||||
use crate::{
|
||||
engine::EngineManager,
|
||||
@ -31,17 +41,6 @@ use crate::{
|
||||
CompilationError, ExecError, ExecutionKind, KclErrorWithOutputs,
|
||||
};
|
||||
|
||||
pub use artifact::{Artifact, ArtifactCommand, ArtifactGraph, ArtifactId};
|
||||
pub use cache::{bust_cache, clear_mem_cache};
|
||||
pub use cad_op::Operation;
|
||||
pub use geometry::*;
|
||||
pub(crate) use import::{
|
||||
import_foreign, send_to_engine as send_import_to_engine, PreImportedGeometry, ZOO_COORD_SYSTEM,
|
||||
};
|
||||
pub use kcl_value::{KclObjectFields, KclValue, UnitAngle, UnitLen};
|
||||
pub use memory::EnvironmentRef;
|
||||
pub use state::{ExecState, IdGenerator, MetaSettings};
|
||||
|
||||
pub(crate) mod annotations;
|
||||
mod artifact;
|
||||
pub(crate) mod cache;
|
||||
@ -728,6 +727,7 @@ impl ExecutorContext {
|
||||
exec_state.global.artifact_commands.clone(),
|
||||
exec_state.global.artifact_graph.clone(),
|
||||
module_id_to_module_path,
|
||||
exec_state.global.id_to_source.clone(),
|
||||
)
|
||||
})?;
|
||||
|
||||
|
||||
@ -5,20 +5,19 @@ use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::EnvironmentRef;
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails, Severity},
|
||||
execution::{
|
||||
annotations, kcl_value, memory::ProgramMemory, Artifact, ArtifactCommand, ArtifactGraph, ArtifactId,
|
||||
ExecOutcome, ExecutorSettings, KclValue, Operation, UnitAngle, UnitLen,
|
||||
},
|
||||
modules::{ModuleId, ModuleInfo, ModuleLoader, ModulePath, ModuleRepr},
|
||||
modules::{ModuleId, ModuleInfo, ModuleLoader, ModulePath, ModuleRepr, ModuleSource},
|
||||
parsing::ast::types::Annotation,
|
||||
source_range::SourceRange,
|
||||
CompilationError,
|
||||
};
|
||||
|
||||
use super::EnvironmentRef;
|
||||
|
||||
/// State for executing a program.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ExecState {
|
||||
@ -34,6 +33,8 @@ pub(super) struct GlobalState {
|
||||
pub id_generator: IdGenerator,
|
||||
/// Map from source file absolute path to module ID.
|
||||
pub path_to_source_id: IndexMap<ModulePath, ModuleId>,
|
||||
/// Map from module ID to source file.
|
||||
pub id_to_source: IndexMap<ModuleId, ModuleSource>,
|
||||
/// Map from module ID to module info.
|
||||
pub module_infos: IndexMap<ModuleId, ModuleInfo>,
|
||||
/// Output map of UUIDs to artifacts.
|
||||
@ -181,6 +182,11 @@ impl ExecState {
|
||||
self.global.path_to_source_id.insert(path.clone(), id);
|
||||
}
|
||||
|
||||
pub(super) fn add_id_to_source(&mut self, id: ModuleId, source: ModuleSource) {
|
||||
debug_assert!(!self.global.id_to_source.contains_key(&id));
|
||||
self.global.id_to_source.insert(id, source.clone());
|
||||
}
|
||||
|
||||
pub(super) fn add_module(&mut self, id: ModuleId, path: ModulePath, repr: ModuleRepr) {
|
||||
debug_assert!(self.global.path_to_source_id.contains_key(&path));
|
||||
let module_info = ModuleInfo { id, repr, path };
|
||||
@ -227,6 +233,7 @@ impl GlobalState {
|
||||
operations: Default::default(),
|
||||
mod_loader: Default::default(),
|
||||
errors: Default::default(),
|
||||
id_to_source: Default::default(),
|
||||
};
|
||||
|
||||
let root_id = ModuleId::default();
|
||||
@ -244,6 +251,8 @@ impl GlobalState {
|
||||
global
|
||||
.path_to_source_id
|
||||
.insert(ModulePath::Local { value: root_path }, root_id);
|
||||
// Ideally we'd have a way to set the root module's source here, but
|
||||
// we don't have a way to get the source from the executor settings.
|
||||
global
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,8 +67,8 @@ impl Discovered {
|
||||
}
|
||||
|
||||
impl IntoDiagnostic for Discovered {
|
||||
fn to_lsp_diagnostic(&self, code: &str) -> Diagnostic {
|
||||
(&self).to_lsp_diagnostic(code)
|
||||
fn to_lsp_diagnostics(&self, code: &str) -> Vec<Diagnostic> {
|
||||
(&self).to_lsp_diagnostics(code)
|
||||
}
|
||||
|
||||
fn severity(&self) -> DiagnosticSeverity {
|
||||
@ -77,11 +77,11 @@ impl IntoDiagnostic for Discovered {
|
||||
}
|
||||
|
||||
impl IntoDiagnostic for &Discovered {
|
||||
fn to_lsp_diagnostic(&self, code: &str) -> Diagnostic {
|
||||
fn to_lsp_diagnostics(&self, code: &str) -> Vec<Diagnostic> {
|
||||
let message = self.finding.title.to_owned();
|
||||
let source_range = self.pos;
|
||||
|
||||
Diagnostic {
|
||||
vec![Diagnostic {
|
||||
range: source_range.to_lsp_range(code),
|
||||
severity: Some(self.severity()),
|
||||
code: None,
|
||||
@ -92,7 +92,7 @@ impl IntoDiagnostic for &Discovered {
|
||||
related_information: None,
|
||||
tags: None,
|
||||
data: None,
|
||||
}
|
||||
}]
|
||||
}
|
||||
|
||||
fn severity(&self) -> DiagnosticSeverity {
|
||||
|
||||
@ -642,10 +642,12 @@ impl Backend {
|
||||
};
|
||||
|
||||
for diagnostic in diagnostics {
|
||||
let d = diagnostic.to_lsp_diagnostic(¶ms.text);
|
||||
let lsp_d = diagnostic.to_lsp_diagnostics(¶ms.text);
|
||||
// Make sure we don't duplicate diagnostics.
|
||||
if !items.iter().any(|x| x == &d) {
|
||||
items.push(d);
|
||||
for d in lsp_d {
|
||||
if !items.iter().any(|x| x == &d) {
|
||||
items.push(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -673,7 +675,7 @@ impl Backend {
|
||||
|
||||
match executor_ctx.run_with_caching(ast.clone()).await {
|
||||
Err(err) => {
|
||||
self.add_to_diagnostics(params, &[err.error], false).await;
|
||||
self.add_to_diagnostics(params, &[err], false).await;
|
||||
|
||||
// Since we already published the diagnostics we don't really care about the error
|
||||
// string.
|
||||
|
||||
@ -18,13 +18,13 @@ use crate::{
|
||||
};
|
||||
|
||||
impl IntoDiagnostic for CompilationError {
|
||||
fn to_lsp_diagnostic(&self, code: &str) -> Diagnostic {
|
||||
fn to_lsp_diagnostics(&self, code: &str) -> Vec<Diagnostic> {
|
||||
let edit = self.suggestion.as_ref().map(|s| {
|
||||
let range = s.source_range.to_lsp_range(code);
|
||||
serde_json::to_value((s, range)).unwrap()
|
||||
});
|
||||
|
||||
Diagnostic {
|
||||
vec![Diagnostic {
|
||||
range: self.source_range.to_lsp_range(code),
|
||||
severity: Some(self.severity()),
|
||||
code: None,
|
||||
@ -34,7 +34,7 @@ impl IntoDiagnostic for CompilationError {
|
||||
related_information: None,
|
||||
tags: self.tag.to_lsp_tags(),
|
||||
data: edit,
|
||||
}
|
||||
}]
|
||||
}
|
||||
|
||||
fn severity(&self) -> DiagnosticSeverity {
|
||||
|
||||
@ -3473,3 +3473,68 @@ async fn kcl_test_kcl_lsp_completions_number_literal() {
|
||||
|
||||
assert_eq!(completions.is_none(), true);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn kcl_test_kcl_lsp_multi_file_error() {
|
||||
let server = kcl_lsp_server(true).await.unwrap();
|
||||
|
||||
let cwd = std::env::current_dir().unwrap();
|
||||
let joined = cwd.join("tests/import_file_parse_error/");
|
||||
|
||||
// Change the current directory.
|
||||
std::env::set_current_dir(joined).unwrap();
|
||||
|
||||
let code = std::fs::read_to_string("input.kcl").unwrap();
|
||||
|
||||
// Send open file.
|
||||
server
|
||||
.did_open(tower_lsp::lsp_types::DidOpenTextDocumentParams {
|
||||
text_document: tower_lsp::lsp_types::TextDocumentItem {
|
||||
uri: "file:///input.kcl".try_into().unwrap(),
|
||||
language_id: "kcl".to_string(),
|
||||
version: 1,
|
||||
text: code.clone(),
|
||||
},
|
||||
})
|
||||
.await;
|
||||
|
||||
// Send diagnostics request.
|
||||
let diagnostics = server
|
||||
.diagnostic(tower_lsp::lsp_types::DocumentDiagnosticParams {
|
||||
text_document: tower_lsp::lsp_types::TextDocumentIdentifier {
|
||||
uri: "file:///input.kcl".try_into().unwrap(),
|
||||
},
|
||||
partial_result_params: Default::default(),
|
||||
work_done_progress_params: Default::default(),
|
||||
identifier: None,
|
||||
previous_result_id: None,
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Check the diagnostics.
|
||||
if let tower_lsp::lsp_types::DocumentDiagnosticReportResult::Report(diagnostics) = diagnostics {
|
||||
if let tower_lsp::lsp_types::DocumentDiagnosticReport::Full(diagnostics) = diagnostics {
|
||||
assert_eq!(diagnostics.full_document_diagnostic_report.items.len(), 1);
|
||||
let item = diagnostics.full_document_diagnostic_report.items.first().unwrap();
|
||||
assert_eq!(item.message, "syntax: Unexpected token: }");
|
||||
assert_eq!(
|
||||
Some(vec![tower_lsp::lsp_types::DiagnosticRelatedInformation {
|
||||
location: tower_lsp::lsp_types::Location {
|
||||
uri: "file:///parse-failure.kcl".try_into().unwrap(),
|
||||
range: tower_lsp::lsp_types::Range {
|
||||
start: tower_lsp::lsp_types::Position { line: 1, character: 9 },
|
||||
end: tower_lsp::lsp_types::Position { line: 2, character: 1 },
|
||||
},
|
||||
},
|
||||
message: "syntax: Unexpected token: }".to_string(),
|
||||
}]),
|
||||
item.related_information
|
||||
);
|
||||
} else {
|
||||
panic!("Expected full diagnostics");
|
||||
}
|
||||
} else {
|
||||
panic!("Expected diagnostics");
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,8 +35,8 @@ pub fn get_line_before(pos: Position, rope: &Rope) -> Option<String> {
|
||||
/// Convert an object into a [lsp_types::Diagnostic] given the
|
||||
/// [TextDocumentItem]'s `.text` field.
|
||||
pub trait IntoDiagnostic {
|
||||
/// Convert the traited object to a [lsp_types::Diagnostic].
|
||||
fn to_lsp_diagnostic(&self, text: &str) -> Diagnostic;
|
||||
/// Convert the traited object to a vector of [lsp_types::Diagnostic].
|
||||
fn to_lsp_diagnostics(&self, text: &str) -> Vec<Diagnostic>;
|
||||
|
||||
/// Get the severity of the diagnostic.
|
||||
fn severity(&self) -> tower_lsp::lsp_types::DiagnosticSeverity;
|
||||
|
||||
@ -143,17 +143,23 @@ impl ModulePath {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn source(&self, fs: &FileManager, source_range: SourceRange) -> Result<String, KclError> {
|
||||
pub(crate) async fn source(&self, fs: &FileManager, source_range: SourceRange) -> Result<ModuleSource, KclError> {
|
||||
match self {
|
||||
ModulePath::Local { value: p } => fs.read_to_string(p, source_range).await,
|
||||
ModulePath::Std { value: name } => read_std(name)
|
||||
.ok_or_else(|| {
|
||||
KclError::Semantic(KclErrorDetails {
|
||||
message: format!("Cannot find standard library module to import: std::{name}."),
|
||||
source_ranges: vec![source_range],
|
||||
ModulePath::Local { value: p } => Ok(ModuleSource {
|
||||
source: fs.read_to_string(p, source_range).await?,
|
||||
path: self.clone(),
|
||||
}),
|
||||
ModulePath::Std { value: name } => Ok(ModuleSource {
|
||||
source: read_std(name)
|
||||
.ok_or_else(|| {
|
||||
KclError::Semantic(KclErrorDetails {
|
||||
message: format!("Cannot find standard library module to import: std::{name}."),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
})
|
||||
})
|
||||
.map(str::to_owned),
|
||||
.map(str::to_owned)?,
|
||||
path: self.clone(),
|
||||
}),
|
||||
ModulePath::Main => unreachable!(),
|
||||
}
|
||||
}
|
||||
@ -188,3 +194,9 @@ impl fmt::Display for ModulePath {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize, ts_rs::TS)]
|
||||
pub struct ModuleSource {
|
||||
pub path: ModulePath,
|
||||
pub source: String,
|
||||
}
|
||||
|
||||
@ -128,11 +128,14 @@ async fn execute(test_name: &str, render_to_png: bool) {
|
||||
// Snapshot the KCL error with a fancy graphical report.
|
||||
// This looks like a Cargo compile error, with arrows pointing
|
||||
// to source code, underlines, etc.
|
||||
let report = crate::errors::Report {
|
||||
error: error.error,
|
||||
filename: format!("{test_name}.kcl"),
|
||||
kcl_source: read("input.kcl", test_name),
|
||||
};
|
||||
miette::set_hook(Box::new(|_| {
|
||||
Box::new(miette::MietteHandlerOpts::new().show_related_errors_as_nested().build())
|
||||
}))
|
||||
.unwrap();
|
||||
let report = error
|
||||
.clone()
|
||||
.into_miette_report_with_outputs(&read("input.kcl", test_name))
|
||||
.unwrap();
|
||||
let report = miette::Report::new(report);
|
||||
if previously_passed {
|
||||
eprintln!("This test case failed, but it previously passed. If this is intended, and the test should actually be failing now, please delete kcl/{ok_path_str} and other associated passing artifacts");
|
||||
@ -2027,6 +2030,28 @@ mod helix_simple {
|
||||
}
|
||||
}
|
||||
|
||||
mod import_file_not_exist_error {
|
||||
const TEST_NAME: &str = "import_file_not_exist_error";
|
||||
|
||||
/// Test parsing KCL.
|
||||
#[test]
|
||||
fn parse() {
|
||||
super::parse(TEST_NAME);
|
||||
}
|
||||
|
||||
/// Test that parsing and unparsing KCL produces the original KCL input.
|
||||
#[test]
|
||||
fn unparse() {
|
||||
super::unparse(TEST_NAME)
|
||||
}
|
||||
|
||||
/// Test that KCL is executed correctly.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn kcl_test_execute() {
|
||||
super::execute(TEST_NAME, true).await
|
||||
}
|
||||
}
|
||||
|
||||
mod import_file_parse_error {
|
||||
const TEST_NAME: &str = "import_file_parse_error";
|
||||
|
||||
|
||||
@ -657,9 +657,8 @@ impl GeometryTrait for Box<Solid> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::execution::kcl_value::NumericType;
|
||||
|
||||
use super::*;
|
||||
use crate::execution::kcl_value::NumericType;
|
||||
|
||||
#[test]
|
||||
fn test_array_to_point3d() {
|
||||
|
||||
@ -4,14 +4,13 @@ use derive_docs::stdlib;
|
||||
use kcmc::{each_cmd as mcmd, length_unit::LengthUnit, shared::Color, ModelingCmd};
|
||||
use kittycad_modeling_cmds as kcmc;
|
||||
|
||||
use super::sketch::PlaneData;
|
||||
use crate::{
|
||||
errors::KclError,
|
||||
execution::{ExecState, KclValue, Plane, PlaneType},
|
||||
std::Args,
|
||||
};
|
||||
|
||||
use super::sketch::PlaneData;
|
||||
|
||||
/// Offset a plane by a distance along its normal.
|
||||
pub async fn offset_plane(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let std_plane = args.get_unlabeled_kw_arg("plane")?;
|
||||
|
||||
@ -8,5 +8,6 @@ KCL Type error
|
||||
╭─[5:5]
|
||||
4 │
|
||||
5 │ map(f, [0, 1])
|
||||
· ─
|
||||
· ┬
|
||||
· ╰── tests/argument_error/input.kcl
|
||||
╰────
|
||||
|
||||
@ -8,5 +8,6 @@ KCL Semantic error
|
||||
╭─[2:8]
|
||||
1 │ arr = []
|
||||
2 │ fail = pop(arr)
|
||||
· ────────
|
||||
· ────┬───
|
||||
· ╰── tests/array_elem_pop_empty_fail/input.kcl
|
||||
╰────
|
||||
|
||||
@ -8,5 +8,6 @@ KCL UndefinedValue error
|
||||
╭─[3:8]
|
||||
2 │ pushedArr = pop(arr)
|
||||
3 │ fail = pushedArr[2]
|
||||
· ────────────
|
||||
· ──────┬─────
|
||||
· ╰── tests/array_elem_pop_fail/input.kcl
|
||||
╰────
|
||||
|
||||
@ -8,5 +8,6 @@ KCL UndefinedValue error
|
||||
╭─[3:8]
|
||||
2 │ pushedArr = push(arr, 4)
|
||||
3 │ fail = arr[3]
|
||||
· ──────
|
||||
· ───┬──
|
||||
· ╰── tests/array_elem_push_fail/input.kcl
|
||||
╰────
|
||||
|
||||
@ -8,5 +8,6 @@ KCL UndefinedValue error
|
||||
╭─[2:5]
|
||||
1 │ arr = []
|
||||
2 │ x = arr[0]
|
||||
· ──────
|
||||
· ───┬──
|
||||
· ╰── tests/array_index_oob/input.kcl
|
||||
╰────
|
||||
|
||||
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
@ -7,5 +7,6 @@ KCL Semantic error
|
||||
× semantic: Expected a number, but found a boolean (true/false value)
|
||||
╭────
|
||||
1 │ assert(3 == 3 == 3, "this should not compile")
|
||||
· ──────
|
||||
· ───┬──
|
||||
· ╰── tests/comparisons_multiple/input.kcl
|
||||
╰────
|
||||
|
||||
@ -8,5 +8,6 @@ KCL UndefinedValue error
|
||||
╭─[22:1]
|
||||
21 │ // Error, after creating meaningful output.
|
||||
22 │ foo
|
||||
· ───
|
||||
· ─┬─
|
||||
· ╰── tests/cube_with_error/input.kcl
|
||||
╰────
|
||||
|
||||
@ -10,6 +10,7 @@ KCL ImportCycle error
|
||||
╭─[2:1]
|
||||
1 │ @settings(defaultLengthUnit = in)
|
||||
2 │ import two from "import_cycle2.kcl"
|
||||
· ───────────────────────────────────
|
||||
· ─────────────────┬─────────────────
|
||||
· ╰── tests/import_cycle1/input.kcl
|
||||
3 │
|
||||
╰────
|
||||
|
||||
@ -0,0 +1,284 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Artifact commands import_file_parse_error.kcl
|
||||
---
|
||||
[
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "make_plane",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"x_axis": {
|
||||
"x": 1.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"y_axis": {
|
||||
"x": 0.0,
|
||||
"y": 1.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"size": 100.0,
|
||||
"clobber": false,
|
||||
"hide": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "plane_set_color",
|
||||
"plane_id": "[uuid]",
|
||||
"color": {
|
||||
"r": 0.7,
|
||||
"g": 0.28,
|
||||
"b": 0.28,
|
||||
"a": 0.4
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "make_plane",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"x_axis": {
|
||||
"x": 0.0,
|
||||
"y": 1.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"y_axis": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 1.0
|
||||
},
|
||||
"size": 100.0,
|
||||
"clobber": false,
|
||||
"hide": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "plane_set_color",
|
||||
"plane_id": "[uuid]",
|
||||
"color": {
|
||||
"r": 0.28,
|
||||
"g": 0.7,
|
||||
"b": 0.28,
|
||||
"a": 0.4
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "make_plane",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"x_axis": {
|
||||
"x": 1.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"y_axis": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 1.0
|
||||
},
|
||||
"size": 100.0,
|
||||
"clobber": false,
|
||||
"hide": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "plane_set_color",
|
||||
"plane_id": "[uuid]",
|
||||
"color": {
|
||||
"r": 0.28,
|
||||
"g": 0.28,
|
||||
"b": 0.7,
|
||||
"a": 0.4
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "make_plane",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"x_axis": {
|
||||
"x": -1.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"y_axis": {
|
||||
"x": 0.0,
|
||||
"y": 1.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"size": 100.0,
|
||||
"clobber": false,
|
||||
"hide": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "make_plane",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"x_axis": {
|
||||
"x": 0.0,
|
||||
"y": -1.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"y_axis": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 1.0
|
||||
},
|
||||
"size": 100.0,
|
||||
"clobber": false,
|
||||
"hide": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "make_plane",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"x_axis": {
|
||||
"x": -1.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"y_axis": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 1.0
|
||||
},
|
||||
"size": 100.0,
|
||||
"clobber": false,
|
||||
"hide": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "edge_lines_visible",
|
||||
"hidden": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "set_scene_units",
|
||||
"unit": "mm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "object_visible",
|
||||
"object_id": "[uuid]",
|
||||
"hidden": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "object_visible",
|
||||
"object_id": "[uuid]",
|
||||
"hidden": true
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -0,0 +1,6 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Artifact graph flowchart import_file_parse_error.kcl
|
||||
extension: md
|
||||
snapshot_kind: binary
|
||||
---
|
||||
@ -0,0 +1,3 @@
|
||||
```mermaid
|
||||
flowchart LR
|
||||
```
|
||||
39
src/wasm-lib/kcl/tests/import_file_not_exist_error/ast.snap
Normal file
@ -0,0 +1,39 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Result of parsing import_file_not_exist_error.kcl
|
||||
---
|
||||
{
|
||||
"Ok": {
|
||||
"body": [
|
||||
{
|
||||
"end": 34,
|
||||
"path": {
|
||||
"type": "Kcl",
|
||||
"filename": "not-exist.kcl"
|
||||
},
|
||||
"selector": {
|
||||
"type": "List",
|
||||
"items": [
|
||||
{
|
||||
"alias": null,
|
||||
"end": 13,
|
||||
"name": {
|
||||
"end": 13,
|
||||
"name": "hotdog",
|
||||
"start": 7,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"start": 7,
|
||||
"type": "ImportItem"
|
||||
}
|
||||
]
|
||||
},
|
||||
"start": 0,
|
||||
"type": "ImportStatement",
|
||||
"type": "ImportStatement"
|
||||
}
|
||||
],
|
||||
"end": 35,
|
||||
"start": 0
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Error from executing import_file_not_exist_error.kcl
|
||||
---
|
||||
KCL Engine error
|
||||
|
||||
× engine: Failed to read file `tests/import_file_not_exist_error/not-
|
||||
│ exist.kcl`: No such file or directory (os error 2)
|
||||
╭────
|
||||
1 │ import hotdog from "not-exist.kcl"
|
||||
· ─────────────────┬────────────────
|
||||
· ╰── tests/import_file_not_exist_error/input.kcl
|
||||
╰────
|
||||
@ -0,0 +1 @@
|
||||
import hotdog from "not-exist.kcl"
|
||||
@ -0,0 +1,5 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Operations executed import_file_parse_error.kcl
|
||||
---
|
||||
[]
|
||||
@ -5,7 +5,9 @@ description: Error from executing import_file_parse_error.kcl
|
||||
KCL Syntax error
|
||||
|
||||
× syntax: Unexpected token: }
|
||||
╭────
|
||||
1 │ import hotdog from "parse-failure.kcl"
|
||||
· ─
|
||||
╭─[3:1]
|
||||
2 │ return
|
||||
3 │ }
|
||||
· ┬
|
||||
· ╰── tests/import_file_parse_error/parse-failure.kcl
|
||||
╰────
|
||||
|
||||
@ -8,5 +8,6 @@ KCL Semantic error
|
||||
╭─[2:5]
|
||||
1 │ arr = [1, 2, 3]
|
||||
2 │ x = arr[1.2]
|
||||
· ────────
|
||||
· ────┬───
|
||||
· ╰── tests/invalid_index_fractional/input.kcl
|
||||
╰────
|
||||
|
||||
@ -8,5 +8,6 @@ KCL Semantic error
|
||||
╭─[3:5]
|
||||
2 │ i = -1
|
||||
3 │ x = arr[i]
|
||||
· ──────
|
||||
· ───┬──
|
||||
· ╰── tests/invalid_index_negative/input.kcl
|
||||
╰────
|
||||
|
||||
@ -9,5 +9,6 @@ KCL Semantic error
|
||||
╭─[2:5]
|
||||
1 │ arr = [1, 2, 3]
|
||||
2 │ x = arr["s"]
|
||||
· ────────
|
||||
· ────┬───
|
||||
· ╰── tests/invalid_index_str/input.kcl
|
||||
╰────
|
||||
|
||||
@ -9,5 +9,6 @@ KCL Semantic error
|
||||
╭─[2:5]
|
||||
1 │ num = 999
|
||||
2 │ x = num[3]
|
||||
· ──────
|
||||
· ───┬──
|
||||
· ╰── tests/invalid_member_object/input.kcl
|
||||
╰────
|
||||
|
||||
@ -9,5 +9,6 @@ KCL Semantic error
|
||||
╭─[2:5]
|
||||
1 │ b = true
|
||||
2 │ x = b["property"]
|
||||
· ─────────────
|
||||
· ──────┬──────
|
||||
· ╰── tests/invalid_member_object_prop/input.kcl
|
||||
╰────
|
||||
|
||||
@ -9,8 +9,21 @@ KCL Semantic error
|
||||
╭─[1:7]
|
||||
1 │ ╭─▶ fn add(x, y) {
|
||||
2 │ │ return x + y
|
||||
3 │ ╰─▶ }
|
||||
3 │ ├─▶ }
|
||||
· ╰──── tests/kw_fn_too_few_args/input.kcl
|
||||
4 │
|
||||
5 │ three = add(x = 1)
|
||||
· ──────────
|
||||
· ─────┬────
|
||||
· ╰── tests/kw_fn_too_few_args/input.kcl
|
||||
╰────
|
||||
╰─▶ KCL Semantic error
|
||||
|
||||
× semantic: This function requires a parameter y, but you haven't
|
||||
│ passed it one.
|
||||
╭─[1:7]
|
||||
1 │ ╭─▶ fn add(x, y) {
|
||||
2 │ │ return x + y
|
||||
3 │ ├─▶ }
|
||||
· ╰──── tests/kw_fn_too_few_args/input.kcl
|
||||
4 │
|
||||
╰────
|
||||
|
||||
@ -9,8 +9,21 @@ KCL Semantic error
|
||||
╭─[1:7]
|
||||
1 │ ╭─▶ fn add(@x) {
|
||||
2 │ │ return x + 1
|
||||
3 │ ╰─▶ }
|
||||
3 │ ├─▶ }
|
||||
· ╰──── tests/kw_fn_unlabeled_but_has_label/input.kcl
|
||||
4 │
|
||||
5 │ two = add(x = 1)
|
||||
· ──────────
|
||||
· ─────┬────
|
||||
· ╰── tests/kw_fn_unlabeled_but_has_label/input.kcl
|
||||
╰────
|
||||
╰─▶ KCL Semantic error
|
||||
|
||||
× semantic: The function does declare a parameter named 'x', but this
|
||||
│ parameter doesn't use a label. Try removing the `x:`
|
||||
╭─[1:7]
|
||||
1 │ ╭─▶ fn add(@x) {
|
||||
2 │ │ return x + 1
|
||||
3 │ ├─▶ }
|
||||
· ╰──── tests/kw_fn_unlabeled_but_has_label/input.kcl
|
||||
4 │
|
||||
╰────
|
||||
|
||||
@ -9,5 +9,6 @@ KCL Semantic error
|
||||
╭─[2:7]
|
||||
1 │ obj = { key = 123 }
|
||||
2 │ num = obj[3]
|
||||
· ──────
|
||||
· ───┬──
|
||||
· ╰── tests/non_string_key_of_object/input.kcl
|
||||
╰────
|
||||
|
||||
@ -8,5 +8,6 @@ KCL UndefinedValue error
|
||||
╭─[2:5]
|
||||
1 │ obj = { }
|
||||
2 │ k = obj["age"]
|
||||
· ──────────
|
||||
· ─────┬────
|
||||
· ╰── tests/object_prop_not_found/input.kcl
|
||||
╰────
|
||||
|
||||
|
Before Width: | Height: | Size: 73 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 75 KiB |
@ -8,6 +8,7 @@ KCL Semantic error
|
||||
╭─[6:10]
|
||||
5 │
|
||||
6 │ answer = %
|
||||
· ─
|
||||
· ┬
|
||||
· ╰── tests/pipe_substitution_inside_function_called_from_pipeline/input.kcl
|
||||
7 │ |> f(%)
|
||||
╰────
|
||||
|
||||
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 147 KiB After Width: | Height: | Size: 148 KiB |