FIX: Better error messages for func calls.

Also better more consistency for some type fails in other expressions.
This commit is contained in:
Jeremy Wall 2019-02-08 20:57:13 -06:00
parent a1bc81ee89
commit 214045f1a6
4 changed files with 71 additions and 106 deletions

View File

@ -270,7 +270,7 @@ fn test_binary_sum_operator_wrong_type_on_rhs_compile_failure() {
assert_build_failure( assert_build_failure(
"1 + \"foo\";", "1 + \"foo\";",
vec![ vec![
Regex::new(r"Expected Integer but got .foo.").unwrap(), Regex::new(r"Expected Integer but got \(.foo.\)").unwrap(),
Regex::new(r"at <eval> line: 1, column: 5").unwrap(), Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
], ],
) )
@ -281,7 +281,7 @@ fn test_binary_minus_operator_wrong_type_on_rhs_compile_failure() {
assert_build_failure( assert_build_failure(
"1 - \"foo\";", "1 - \"foo\";",
vec![ vec![
Regex::new(r"Expected Integer but got .foo.").unwrap(), Regex::new(r"Expected Integer but got \(.foo.\)").unwrap(),
Regex::new(r"at <eval> line: 1, column: 5").unwrap(), Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
], ],
) )
@ -292,7 +292,7 @@ fn test_binary_mul_operator_wrong_type_on_rhs_compile_failure() {
assert_build_failure( assert_build_failure(
"1 * \"foo\";", "1 * \"foo\";",
vec![ vec![
Regex::new(r"Expected Integer but got .foo.").unwrap(), Regex::new(r"Expected Integer but got \(.foo.\)").unwrap(),
Regex::new(r"at <eval> line: 1, column: 5").unwrap(), Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
], ],
) )
@ -303,7 +303,7 @@ fn test_binary_div_operator_wrong_type_on_rhs_compile_failure() {
assert_build_failure( assert_build_failure(
"1 / \"foo\";", "1 / \"foo\";",
vec![ vec![
Regex::new(r"Expected Integer but got .foo.").unwrap(), Regex::new(r"Expected Integer but got \(.foo.\)").unwrap(),
Regex::new(r"at <eval> line: 1, column: 5").unwrap(), Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
], ],
) )
@ -314,7 +314,7 @@ fn test_binary_gt_operator_wrong_type_on_rhs_compile_failure() {
assert_build_failure( assert_build_failure(
"1 > \"foo\";", "1 > \"foo\";",
vec![ vec![
Regex::new(r"Expected Integer but got .foo.").unwrap(), Regex::new(r"Expected Integer but got \(.foo.\)").unwrap(),
Regex::new(r"at <eval> line: 1, column: 5").unwrap(), Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
], ],
) )
@ -325,7 +325,7 @@ fn test_binary_lt_operator_wrong_type_on_rhs_compile_failure() {
assert_build_failure( assert_build_failure(
"1 < \"foo\";", "1 < \"foo\";",
vec![ vec![
Regex::new(r"Expected Integer but got .foo.").unwrap(), Regex::new(r"Expected Integer but got \(.foo.\)").unwrap(),
Regex::new(r"at <eval> line: 1, column: 5").unwrap(), Regex::new(r"at <eval> line: 1, column: 5").unwrap(),
], ],
) )
@ -336,7 +336,7 @@ fn test_binary_lteq_operator_wrong_type_on_rhs_compile_failure() {
assert_build_failure( assert_build_failure(
"1 <= \"foo\";", "1 <= \"foo\";",
vec![ vec![
Regex::new(r"Expected Integer but got .foo.").unwrap(), Regex::new(r"Expected Integer but got \(.foo.\)").unwrap(),
Regex::new(r"at <eval> line: 1, column: 6").unwrap(), Regex::new(r"at <eval> line: 1, column: 6").unwrap(),
], ],
) )
@ -347,7 +347,7 @@ fn test_binary_gteq_operator_wrong_type_on_rhs_compile_failure() {
assert_build_failure( assert_build_failure(
"1 >= \"foo\";", "1 >= \"foo\";",
vec![ vec![
Regex::new(r"Expected Integer but got .foo.").unwrap(), Regex::new(r"Expected Integer but got \(.foo.\)").unwrap(),
Regex::new(r"at <eval> line: 1, column: 6").unwrap(), Regex::new(r"at <eval> line: 1, column: 6").unwrap(),
], ],
) )
@ -358,7 +358,7 @@ fn test_binary_eqeq_operator_wrong_type_on_rhs_compile_failure() {
assert_build_failure( assert_build_failure(
"1 == \"foo\";", "1 == \"foo\";",
vec![ vec![
Regex::new(r"Expected Integer but got .foo.").unwrap(), Regex::new(r"Expected Integer but got \(.foo.\)").unwrap(),
Regex::new(r"at <eval> line: 1, column: 6").unwrap(), Regex::new(r"at <eval> line: 1, column: 6").unwrap(),
], ],
) )
@ -529,3 +529,29 @@ fn test_copy_expression_wrong_field_type_compile_failure() {
], ],
) )
} }
// TODO(jwall): Function call errors
#[test]
fn test_func_call_wrong_argument_length_compile_failure() {
assert_build_failure(
"let foo = func() => 1;\nfoo(1);",
vec![
Regex::new(r"Func called with too many args").unwrap(),
Regex::new(r"at <eval> line: 2, column: 1").unwrap(),
],
)
}
#[test]
fn test_func_call_wrong_argument_type_compile_failure() {
assert_build_failure(
"let foo = func(i) => 1 + i;\nfoo(\"bar\");",
vec![
Regex::new(r"Func evaluation failed").unwrap(),
Regex::new(r"at <eval> line: 1, column: 26").unwrap(),
Regex::new(r"Expected Integer but got \(.bar.\)").unwrap(),
Regex::new(r"at <eval> line: 2, column: 1").unwrap(),
],
)
}

View File

@ -113,7 +113,7 @@ impl Val {
(&Val::Empty, _) => Ok(false), (&Val::Empty, _) => Ok(false),
(_, &Val::Empty) => Ok(false), (_, &Val::Empty) => Ok(false),
(me, tgt) => Err(error::BuildError::new( (me, tgt) => Err(error::BuildError::new(
format!("Expected {} but got {}", me.type_name(), tgt), format!("Expected {} but got ({})", me.type_name(), tgt),
error::ErrorType::TypeFail, error::ErrorType::TypeFail,
pos, pos,
)), )),

View File

@ -55,8 +55,6 @@ impl FuncDef {
/// Expands a ucg function using the given arguments into a new Tuple. /// Expands a ucg function using the given arguments into a new Tuple.
pub fn eval( pub fn eval(
&self, &self,
// TODO(jwall): This should come from the FuncDef instead.
root: PathBuf,
parent_builder: &FileBuilder, parent_builder: &FileBuilder,
mut args: Vec<Rc<Val>>, mut args: Vec<Rc<Val>>,
) -> Result<Rc<Val>, Box<dyn Error>> { ) -> Result<Rc<Val>, Box<dyn Error>> {
@ -64,10 +62,7 @@ impl FuncDef {
// func call error. // func call error.
if args.len() > self.argdefs.len() { if args.len() > self.argdefs.len() {
return Err(Box::new(error::BuildError::new( return Err(Box::new(error::BuildError::new(
format!( "Func called with too many args",
"Func called with too many args in file: {}",
root.to_string_lossy()
),
error::ErrorType::BadArgLen, error::ErrorType::BadArgLen,
self.pos.clone(), self.pos.clone(),
))); )));
@ -135,7 +130,7 @@ macro_rules! eval_binary_expr {
} }
val => { val => {
return Err(Box::new(error::BuildError::new( return Err(Box::new(error::BuildError::new(
format!("Expected {} but got {}", $msg, val), format!("Expected {} but got ({})", $msg, val),
error::ErrorType::TypeFail, error::ErrorType::TypeFail,
$pos.clone(), $pos.clone(),
))); )));
@ -682,7 +677,7 @@ impl<'a> FileBuilder<'a> {
} }
} }
Err(Box::new(error::BuildError::new( Err(Box::new(error::BuildError::new(
format!("Expected {} but got {}", left.type_name(), right,), format!("Expected {} but got ({})", left.type_name(), right,),
error::ErrorType::TypeFail, error::ErrorType::TypeFail,
pos.clone(), pos.clone(),
))) )))
@ -706,7 +701,7 @@ impl<'a> FileBuilder<'a> {
} }
} }
Err(Box::new(error::BuildError::new( Err(Box::new(error::BuildError::new(
format!("Expected {} but got {}", left.type_name(), right,), format!("Expected {} but got ({})", left.type_name(), right,),
error::ErrorType::TypeFail, error::ErrorType::TypeFail,
pos.clone(), pos.clone(),
))) )))
@ -729,7 +724,7 @@ impl<'a> FileBuilder<'a> {
} }
} }
Err(Box::new(error::BuildError::new( Err(Box::new(error::BuildError::new(
format!("Expected {} but got {}", left.type_name(), right), format!("Expected {} but got ({})", left.type_name(), right),
error::ErrorType::TypeFail, error::ErrorType::TypeFail,
pos.clone(), pos.clone(),
))) )))
@ -752,7 +747,7 @@ impl<'a> FileBuilder<'a> {
} }
} }
Err(Box::new(error::BuildError::new( Err(Box::new(error::BuildError::new(
format!("Expected {} but got {}", left.type_name(), right,), format!("Expected {} but got ({})", left.type_name(), right,),
error::ErrorType::TypeFail, error::ErrorType::TypeFail,
pos.clone(), pos.clone(),
))) )))
@ -839,7 +834,7 @@ impl<'a> FileBuilder<'a> {
} }
return Err(Box::new(error::BuildError::new( return Err(Box::new(error::BuildError::new(
format!( format!(
"Expected boolean value for operator but got {}", "Expected boolean value for operator but got ({})",
left.type_name() left.type_name()
), ),
error::ErrorType::TypeFail, error::ErrorType::TypeFail,
@ -848,7 +843,7 @@ impl<'a> FileBuilder<'a> {
} else { } else {
return Err(Box::new(error::BuildError::new( return Err(Box::new(error::BuildError::new(
format!( format!(
"Expected boolean value for operator but got {}", "Expected boolean value for operator but got ({})",
left.type_name() left.type_name()
), ),
error::ErrorType::TypeFail, error::ErrorType::TypeFail,
@ -914,7 +909,7 @@ impl<'a> FileBuilder<'a> {
regex::Regex::new(s.as_ref())? regex::Regex::new(s.as_ref())?
} else { } else {
return Err(Box::new(error::BuildError::new( return Err(Box::new(error::BuildError::new(
format!("Expected string for regex but got {}", right.type_name()), format!("Expected string for regex but got ({})", right.type_name()),
error::ErrorType::TypeFail, error::ErrorType::TypeFail,
right_pos.clone(), right_pos.clone(),
))); )));
@ -923,7 +918,7 @@ impl<'a> FileBuilder<'a> {
s.as_ref() s.as_ref()
} else { } else {
return Err(Box::new(error::BuildError::new( return Err(Box::new(error::BuildError::new(
format!("Expected string but got {}", left.type_name()), format!("Expected string but got ({})", left.type_name()),
error::ErrorType::TypeFail, error::ErrorType::TypeFail,
left_pos.clone(), left_pos.clone(),
))); )));
@ -1164,13 +1159,21 @@ impl<'a> FileBuilder<'a> {
fn eval_call(&self, def: &CallDef, scope: &Scope) -> Result<Rc<Val>, Box<dyn Error>> { fn eval_call(&self, def: &CallDef, scope: &Scope) -> Result<Rc<Val>, Box<dyn Error>> {
let args = &def.arglist; let args = &def.arglist;
let v = self.eval_value(&def.funcref, scope)?; let v = self.eval_value(&def.funcref, scope)?;
let call_pos = def.pos.clone();
if let &Val::Func(ref def) = v.deref() { if let &Val::Func(ref def) = v.deref() {
// Congratulations this is actually a function. // Congratulations this is actually a function.
let mut argvals: Vec<Rc<Val>> = Vec::new(); let mut argvals: Vec<Rc<Val>> = Vec::new();
for arg in args.iter() { for arg in args.iter() {
argvals.push(self.eval_expr(arg, scope)?); argvals.push(self.eval_expr(arg, scope)?);
} }
return Ok(def.eval(self.working_dir.clone(), self, argvals)?); return match def.eval(self, argvals) {
Ok(v) => Ok(v),
Err(e) => Err(Box::new(error::BuildError::new(
format!("Func evaluation failed\nCaused by:\n\t{}", e),
error::ErrorType::TypeFail,
call_pos,
))),
};
} }
Err(Box::new(error::BuildError::new( Err(Box::new(error::BuildError::new(
// We should pretty print the selectors here. // We should pretty print the selectors here.
@ -1259,7 +1262,7 @@ impl<'a> FileBuilder<'a> {
let mut out = Vec::new(); let mut out = Vec::new();
for item in elems.iter() { for item in elems.iter() {
let argvals = vec![item.clone()]; let argvals = vec![item.clone()];
let val = def.eval(self.working_dir.clone(), self, argvals)?; let val = def.eval(self, argvals)?;
match typ { match typ {
ProcessingOpType::Map => { ProcessingOpType::Map => {
out.push(val.clone()); out.push(val.clone());
@ -1288,7 +1291,7 @@ impl<'a> FileBuilder<'a> {
let mut out = Vec::new(); let mut out = Vec::new();
for &(ref name, ref val) in fs { for &(ref name, ref val) in fs {
let argvals = vec![Rc::new(Val::Str(name.val.clone())), val.clone()]; let argvals = vec![Rc::new(Val::Str(name.val.clone())), val.clone()];
let result = def.eval(self.working_dir.clone(), self, argvals)?; let result = def.eval(self, argvals)?;
match typ { match typ {
ProcessingOpType::Map => { ProcessingOpType::Map => {
if let &Val::List(ref fs) = result.as_ref() { if let &Val::List(ref fs) = result.as_ref() {
@ -1324,7 +1327,7 @@ impl<'a> FileBuilder<'a> {
} else { } else {
return Err(Box::new(error::BuildError::new( return Err(Box::new(error::BuildError::new(
format!( format!(
"map on a tuple field expects a list as output but got {:?}", "map on a tuple field expects a list as output but got ({})",
result.type_name() result.type_name()
), ),
error::ErrorType::TypeFail, error::ErrorType::TypeFail,
@ -1365,7 +1368,7 @@ impl<'a> FileBuilder<'a> {
&Val::List(ref elems) => { &Val::List(ref elems) => {
for item in elems.iter() { for item in elems.iter() {
let argvals = vec![acc.clone(), item.clone()]; let argvals = vec![acc.clone(), item.clone()];
let result = funcdef.eval(self.working_dir.clone(), self, argvals)?; let result = funcdef.eval(self, argvals)?;
acc = result; acc = result;
} }
} }
@ -1376,14 +1379,14 @@ impl<'a> FileBuilder<'a> {
Rc::new(Val::Str(name.val.clone())), Rc::new(Val::Str(name.val.clone())),
val.clone(), val.clone(),
]; ];
let result = funcdef.eval(self.working_dir.clone(), self, argvals)?; let result = funcdef.eval(self, argvals)?;
acc = result; acc = result;
} }
} }
&Val::Str(ref s) => { &Val::Str(ref s) => {
for gc in s.graphemes(true) { for gc in s.graphemes(true) {
let argvals = vec![acc.clone(), Rc::new(Val::Str(gc.to_string()))]; let argvals = vec![acc.clone(), Rc::new(Val::Str(gc.to_string()))];
let result = funcdef.eval(self.working_dir.clone(), self, argvals)?; let result = funcdef.eval(self, argvals)?;
acc = result; acc = result;
} }
} }
@ -1410,7 +1413,7 @@ impl<'a> FileBuilder<'a> {
let mut result = String::new(); let mut result = String::new();
for gc in s.graphemes(true) { for gc in s.graphemes(true) {
let arg = Rc::new(Val::Str(gc.to_string())); let arg = Rc::new(Val::Str(gc.to_string()));
let out = def.eval(self.working_dir.clone(), self, vec![arg])?; let out = def.eval(self, vec![arg])?;
match typ { match typ {
ProcessingOpType::Filter => { ProcessingOpType::Filter => {
match out.as_ref() { match out.as_ref() {
@ -1425,7 +1428,7 @@ impl<'a> FileBuilder<'a> {
_ => { _ => {
return Err(Box::new(error::BuildError::new( return Err(Box::new(error::BuildError::new(
format!( format!(
"Expected boolean or NULL for filter return but got {}", "Expected boolean or NULL for filter return but got ({})",
out.type_name() out.type_name()
), ),
error::ErrorType::TypeFail, error::ErrorType::TypeFail,
@ -1440,7 +1443,7 @@ impl<'a> FileBuilder<'a> {
} }
_ => { _ => {
return Err(Box::new(error::BuildError::new( return Err(Box::new(error::BuildError::new(
format!("Expected string map return but got {}", out.type_name()), format!("Expected string map return but got ({})", out.type_name()),
error::ErrorType::TypeFail, error::ErrorType::TypeFail,
def.pos.clone(), def.pos.clone(),
))); )));
@ -1648,7 +1651,7 @@ impl<'a> FileBuilder<'a> {
_ => { _ => {
return Err(Box::new(error::BuildError::new( return Err(Box::new(error::BuildError::new(
format!( format!(
"Expected an integer for range start but got {}", "Expected an integer for range start but got ({})",
start.type_name() start.type_name()
), ),
error::ErrorType::TypeFail, error::ErrorType::TypeFail,
@ -1665,7 +1668,7 @@ impl<'a> FileBuilder<'a> {
_ => { _ => {
return Err(Box::new(error::BuildError::new( return Err(Box::new(error::BuildError::new(
format!( format!(
"Expected an integer for range step but got {}", "Expected an integer for range step but got ({})",
step.type_name() step.type_name()
), ),
error::ErrorType::TypeFail, error::ErrorType::TypeFail,
@ -1684,7 +1687,7 @@ impl<'a> FileBuilder<'a> {
_ => { _ => {
return Err(Box::new(error::BuildError::new( return Err(Box::new(error::BuildError::new(
format!( format!(
"Expected an integer for range start but got {}", "Expected an integer for range start but got ({})",
end.type_name() end.type_name()
), ),
error::ErrorType::TypeFail, error::ErrorType::TypeFail,
@ -1710,7 +1713,7 @@ impl<'a> FileBuilder<'a> {
Val::Str(ref s) => s.clone(), Val::Str(ref s) => s.clone(),
_ => { _ => {
return Err(Box::new(error::BuildError::new( return Err(Box::new(error::BuildError::new(
format!("Expected string expression but got {}", tval), format!("Expected string expression but got ({})", tval),
error::ErrorType::TypeFail, error::ErrorType::TypeFail,
def.right.pos().clone(), def.right.pos().clone(),
))); )));
@ -1729,7 +1732,7 @@ impl<'a> FileBuilder<'a> {
"module" => val.is_module(), "module" => val.is_module(),
other => { other => {
return Err(Box::new(error::BuildError::new( return Err(Box::new(error::BuildError::new(
format!("Expected valid type name but got {}", other), format!("Expected valid type name but got ({})", other),
error::ErrorType::TypeFail, error::ErrorType::TypeFail,
def.right.pos().clone(), def.right.pos().clone(),
))); )));
@ -1772,7 +1775,7 @@ impl<'a> FileBuilder<'a> {
} else { } else {
Err(Box::new(error::BuildError::new( Err(Box::new(error::BuildError::new(
format!( format!(
"Expected string for message but got {}", "Expected string for message but got ({})",
def.message.as_ref() def.message.as_ref()
), ),
error::ErrorType::TypeFail, error::ErrorType::TypeFail,
@ -1787,7 +1790,7 @@ impl<'a> FileBuilder<'a> {
} else { } else {
Err(Box::new(error::BuildError::new( Err(Box::new(error::BuildError::new(
format!( format!(
"Expected boolean for expression but got {}", "Expected boolean for expression but got ({})",
def.expr.as_ref() def.expr.as_ref()
), ),
error::ErrorType::TypeFail, error::ErrorType::TypeFail,

View File

@ -170,70 +170,6 @@ fn test_expr_copy_no_such_tuple() {
); );
} }
#[test]
#[should_panic(expected = "Expected Tuple or Module got 1")]
fn test_expr_copy_not_a_tuple() {
let i_paths = Vec::new();
let cache = Rc::new(RefCell::new(MemoryCache::new()));
let mut b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, cache);
b.scope
.build_output
.entry(value_node!("tpl1".to_string(), Position::new(1, 0, 0)))
.or_insert(Rc::new(Val::Int(1)));
test_expr_to_val(
vec![(
Expression::Copy(CopyDef {
selector: Value::Symbol(PositionedItem::new(
"tpl1".to_string(),
Position::new(1, 1, 1),
)),
fields: Vec::new(),
pos: Position::new(1, 0, 0),
}),
Val::Tuple(Vec::new()),
)],
b,
);
}
#[test]
#[should_panic(expected = "Expected type Integer for field fld1 but got String")]
fn test_expr_copy_field_type_error() {
let i_paths = Vec::new();
let cache = Rc::new(RefCell::new(MemoryCache::new()));
let mut b = FileBuilder::new(std::env::current_dir().unwrap(), &i_paths, cache);
b.scope
.build_output
.entry(value_node!("tpl1".to_string(), Position::new(1, 0, 0)))
.or_insert(Rc::new(Val::Tuple(vec![(
value_node!("fld1".to_string(), Position::new(1, 0, 0)),
Rc::new(Val::Int(1)),
)])));
test_expr_to_val(
vec![(
Expression::Copy(CopyDef {
selector: Value::Symbol(PositionedItem::new(
"tpl1".to_string(),
Position::new(1, 1, 1),
)),
fields: vec![(
make_tok!("fld1", Position::new(1, 1, 1)),
Expression::Simple(Value::Str(value_node!(
"2".to_string(),
Position::new(1, 1, 1)
))),
)],
pos: Position::new(1, 0, 0),
}),
Val::Tuple(vec![(
value_node!("fld1".to_string(), Position::new(1, 1, 1)),
Rc::new(Val::Str("2".to_string())),
)]),
)],
b,
);
}
#[test] #[test]
#[should_panic(expected = "Expected String but got Integer in Select expression")] #[should_panic(expected = "Expected String but got Integer in Select expression")]
fn test_select_expr_not_a_string() { fn test_select_expr_not_a_string() {