Add analysis to prevent tuple structs with wrong types
This commit is contained in:
parent
83aa53b998
commit
b1337900fb
@ -131,7 +131,7 @@ impl<'a> Analyzer<'a> {
|
||||
if !fields.contains_key(identifier) {
|
||||
return Err(AnalyzerError::UndefinedField {
|
||||
identifier: right.as_ref().clone(),
|
||||
map: left.as_ref().clone(),
|
||||
statement: left.as_ref().clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -143,7 +143,7 @@ impl<'a> Analyzer<'a> {
|
||||
if !fields.contains_key(&Identifier::new(field_name)) {
|
||||
return Err(AnalyzerError::UndefinedField {
|
||||
identifier: right.as_ref().clone(),
|
||||
map: left.as_ref().clone(),
|
||||
statement: left.as_ref().clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -291,16 +291,45 @@ impl<'a> Analyzer<'a> {
|
||||
}
|
||||
Statement::Constant(_) => {}
|
||||
Statement::Invokation {
|
||||
invokee: function,
|
||||
invokee,
|
||||
value_arguments,
|
||||
..
|
||||
} => {
|
||||
self.analyze_statement(function)?;
|
||||
self.analyze_statement(invokee)?;
|
||||
|
||||
let invokee_type = invokee.inner.expected_type(self.context);
|
||||
|
||||
if let Some(arguments) = value_arguments {
|
||||
for argument in arguments {
|
||||
self.analyze_statement(argument)?;
|
||||
}
|
||||
|
||||
if let Some(Type::Struct(struct_type)) = invokee_type {
|
||||
match struct_type {
|
||||
StructType::Unit { name } => todo!(),
|
||||
StructType::Tuple { fields, .. } => {
|
||||
for (expected_type, argument) in fields.iter().zip(arguments.iter())
|
||||
{
|
||||
let actual_type = argument.inner.expected_type(self.context);
|
||||
|
||||
if let Some(actual_type) = actual_type {
|
||||
expected_type.check(&actual_type).map_err(|conflict| {
|
||||
AnalyzerError::TypeConflict {
|
||||
actual_statement: argument.clone(),
|
||||
actual_type: conflict.actual,
|
||||
expected: expected_type.clone(),
|
||||
}
|
||||
})?;
|
||||
} else {
|
||||
return Err(AnalyzerError::ExpectedValue {
|
||||
actual: argument.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
StructType::Fields { name, fields } => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Statement::Identifier(identifier) => {
|
||||
@ -537,7 +566,7 @@ pub enum AnalyzerError {
|
||||
},
|
||||
UndefinedField {
|
||||
identifier: Node<Statement>,
|
||||
map: Node<Statement>,
|
||||
statement: Node<Statement>,
|
||||
},
|
||||
UndefinedType {
|
||||
identifier: Node<Identifier>,
|
||||
@ -625,7 +654,10 @@ impl Display for AnalyzerError {
|
||||
expected, actual_statement, actual_type
|
||||
)
|
||||
}
|
||||
AnalyzerError::UndefinedField { identifier, map } => {
|
||||
AnalyzerError::UndefinedField {
|
||||
identifier,
|
||||
statement: map,
|
||||
} => {
|
||||
write!(f, "Undefined field {} in map {}", identifier, map)
|
||||
}
|
||||
AnalyzerError::UndefinedType { identifier } => {
|
||||
@ -650,6 +682,26 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn tuple_struct_with_wrong_field_types() {
|
||||
let source = "
|
||||
struct Foo(int, float)
|
||||
Foo(1, 2)
|
||||
";
|
||||
|
||||
assert_eq!(
|
||||
analyze(source),
|
||||
Err(DustError::AnalyzerError {
|
||||
analyzer_error: AnalyzerError::TypeConflict {
|
||||
actual_statement: Node::new(Statement::Constant(Value::integer(2)), (55, 56)),
|
||||
actual_type: Type::Integer,
|
||||
expected: Type::Float
|
||||
},
|
||||
source
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn constant_list_index_out_of_bounds() {
|
||||
let source = "[1, 2, 3][3]";
|
||||
@ -684,7 +736,7 @@ mod tests {
|
||||
Err(DustError::AnalyzerError {
|
||||
analyzer_error: AnalyzerError::UndefinedField {
|
||||
identifier: Node::new(Statement::Identifier(Identifier::new("y")), (10, 11)),
|
||||
map: Node::new(
|
||||
statement: Node::new(
|
||||
Statement::Map(vec![(
|
||||
Node::new(Statement::Identifier(Identifier::new("x")), (2, 3)),
|
||||
Node::new(Statement::Constant(Value::integer(1)), (6, 7))
|
||||
@ -706,7 +758,7 @@ mod tests {
|
||||
Err(DustError::AnalyzerError {
|
||||
analyzer_error: AnalyzerError::UndefinedField {
|
||||
identifier: Node::new(Statement::Constant(Value::string("y")), (10, 13)),
|
||||
map: Node::new(
|
||||
statement: Node::new(
|
||||
Statement::Map(vec![(
|
||||
Node::new(Statement::Identifier(Identifier::new("x")), (2, 3)),
|
||||
Node::new(Statement::Constant(Value::integer(1)), (6, 7))
|
||||
|
@ -1027,9 +1027,7 @@ impl Display for ParseError {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
BinaryOperator, Identifier, StructDefinition, StructInstantiation, Type, UnaryOperator,
|
||||
};
|
||||
use crate::{BinaryOperator, Identifier, StructDefinition, Type, UnaryOperator};
|
||||
|
||||
use super::*;
|
||||
|
||||
|
@ -866,6 +866,23 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn assign_tuple_struct_variable() {
|
||||
let input = "
|
||||
struct Foo(int)
|
||||
x = Foo(42)
|
||||
x
|
||||
";
|
||||
|
||||
assert_eq!(
|
||||
run(input),
|
||||
Ok(Some(Value::r#struct(Struct::Tuple {
|
||||
name: Identifier::new("Foo"),
|
||||
fields: vec![Value::integer(42)]
|
||||
})))
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn define_and_instantiate_tuple_struct() {
|
||||
let input = "struct Foo(int) Foo(42)";
|
||||
|
Loading…
Reference in New Issue
Block a user