Skip to content

Commit

Permalink
feat: enhance evaluator func type_pack_and_check err msg (kcl-lang#1822)
Browse files Browse the repository at this point in the history
* feat: enhance evaluator func type_pack_and_check err msg

Signed-off-by: he1pa <18012015693@163.com>

* insert missing attr msg

Signed-off-by: he1pa <18012015693@163.com>

* fix optional attr

Signed-off-by: he1pa <18012015693@163.com>

* fix ut

Signed-off-by: he1pa <18012015693@163.com>

* fix ut

Signed-off-by: he1pa <18012015693@163.com>

---------

Signed-off-by: he1pa <18012015693@163.com>
  • Loading branch information
He1pa authored Jan 14, 2025
1 parent 829b262 commit 29266c0
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 10 deletions.
23 changes: 23 additions & 0 deletions kclvm/evaluator/src/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,29 @@ impl SchemaEvalContext {
false
}

/// Get all attribute from schema
pub fn get_attrs(s: &Evaluator, ctx: &SchemaEvalContextRef) -> Vec<(String, bool)> {
let mut attrs = vec![];
for stmt in &ctx.borrow().node.body {
if let ast::Stmt::SchemaAttr(attr) = &stmt.node {
attrs.push((attr.name.node.clone(), attr.is_optional));
}
}
if let Some(index) = ctx.borrow().parent {
let frame = {
let frames = s.frames.borrow();
frames
.get(index)
.expect(kcl_error::INTERNAL_ERROR_MSG)
.clone()
};
if let Proxy::Schema(schema) = &frame.proxy {
attrs.extend(SchemaEvalContext::get_attrs(s, &schema.ctx));
}
}
attrs
}

/// Whether the index signature is the schema context.
pub fn has_index_signature(s: &Evaluator, ctx: &SchemaEvalContextRef) -> bool {
if ctx.borrow().node.index_signature.is_some() {
Expand Down
69 changes: 63 additions & 6 deletions kclvm/evaluator/src/ty.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use kclvm_runtime::{
check_type, dereference_type, is_dict_type, is_list_type, is_type_union, schema_config_meta,
schema_runtime_type, separate_kv, split_type_union, val_plan, ConfigEntryOperationKind,
ValueRef, BUILTIN_TYPES, KCL_TYPE_ANY, PKG_PATH_PREFIX,
check_type, dereference_type, is_dict_type, is_list_type, is_schema_type, is_type_union,
schema_config_meta, schema_runtime_type, separate_kv, split_type_union, val_plan,
ConfigEntryOperationKind, ValueRef, BUILTIN_TYPES, KCL_TYPE_ANY, PKG_PATH_PREFIX,
};
use scopeguard::defer;

Expand Down Expand Up @@ -72,7 +72,7 @@ pub fn type_pack_and_check(
let mut checked = false;
let mut converted_value = value.clone();
let expected_type = &expected_types.join(" | ").replace('@', "");
for tpe in expected_types {
for tpe in &expected_types {
if !is_schema {
converted_value = convert_collection_value(s, value, tpe);
}
Expand All @@ -88,9 +88,66 @@ pub fn type_pack_and_check(
}
}
if !checked {
let mut error_msgs = vec![];
for tpe in &expected_types {
if is_schema_type(tpe) {
let schema_type_name = if tpe.contains('.') {
if tpe.starts_with(PKG_PATH_PREFIX) {
tpe.to_string()
} else {
format!("{PKG_PATH_PREFIX}{tpe}")
}
} else {
format!("{}.{}", s.current_pkgpath(), tpe)
};

if let Some(index) = s.schemas.borrow().get(&schema_type_name) {
let frame = {
let frames = s.frames.borrow();
frames
.get(*index)
.expect(kcl_error::INTERNAL_ERROR_MSG)
.clone()
};
if let Proxy::Schema(caller) = &frame.proxy {
if value.is_config() {
let config = value.as_dict_ref();
for (key, _) in &config.values {
let no_such_attr =
!SchemaEvalContext::has_attr(s, &caller.ctx, key)
&& !key.starts_with('_');
let has_index_signature =
SchemaEvalContext::has_index_signature(s, &caller.ctx);
if !has_index_signature && no_such_attr {
error_msgs.push(format!(
"Schema {} does not contain attribute {}",
tpe, key
));
}
}

for (attr, is_optional) in SchemaEvalContext::get_attrs(s, &caller.ctx)
{
if !config.values.contains_key(&attr) && !is_optional {
error_msgs.push(format!(
"Schema {}'s attribute {} is missing",
tpe, attr
));
}
}
}
}
}
}
}
panic!(
"expect {expected_type}, got {}",
val_plan::type_of(value, true)
"expect {expected_type}, got {}{}",
val_plan::type_of(value, true),
if error_msgs.is_empty() {
"".to_string()
} else {
format!(". For details:\n{}", error_msgs.join("\n"))
}
);
}
converted_value
Expand Down
4 changes: 3 additions & 1 deletion test/grammar/types/runtime_ty/runtime_ty_err_0/stderr.golden
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@ error[E3M38]: EvaluationError
--> ${CWD}/main.k:7:1
|
7 | person: Person = json.decode('{"err_name": "Alice", "age": 18}')
| expect Person, got dict
| expect Person, got dict. For details:
Schema Person does not contain attribute err_name
Schema Person's attribute name is missing
|
4 changes: 3 additions & 1 deletion test/grammar/types/type_as/type_as_err_2/stderr.golden
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@ error[E3M38]: EvaluationError
--> ${CWD}/main.k:8:1
|
8 | bar = foo as Bar
| expect Bar, got Foo
| expect Bar, got Foo. For details:
Schema Bar does not contain attribute foo
Schema Bar's attribute bar is missing
|
4 changes: 3 additions & 1 deletion test/grammar/types/type_as/type_as_err_3/stderr.golden
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@ error[E3M38]: EvaluationError
--> ${CWD}/child/child.k:15:1
|
15 | base_a: A = _base_a as A
| expect A, got base.A
| expect A, got base.A. For details:
Schema A does not contain attribute bar
Schema A's attribute foo is missing
|
4 changes: 3 additions & 1 deletion test/grammar/types/type_as/type_as_err_4/stderr.golden
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@ error[E3M38]: EvaluationError
--> ${CWD}/main.k:18:1
|
18 | child_a = _child_a as A
| expect A, got child.A
| expect A, got child.A. For details:
Schema A does not contain attribute foo
Schema A's attribute main is missing
|

0 comments on commit 29266c0

Please sign in to comment.