Add analysis to prevent tuple structs with wrong types

This commit is contained in:
Jeff 2024-08-13 16:46:54 -04:00
parent 83aa53b998
commit b1337900fb
3 changed files with 78 additions and 11 deletions

View File

@ -131,7 +131,7 @@ impl<'a> Analyzer<'a> {
if !fields.contains_key(identifier) { if !fields.contains_key(identifier) {
return Err(AnalyzerError::UndefinedField { return Err(AnalyzerError::UndefinedField {
identifier: right.as_ref().clone(), 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)) { if !fields.contains_key(&Identifier::new(field_name)) {
return Err(AnalyzerError::UndefinedField { return Err(AnalyzerError::UndefinedField {
identifier: right.as_ref().clone(), 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::Constant(_) => {}
Statement::Invokation { Statement::Invokation {
invokee: function, invokee,
value_arguments, 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 { if let Some(arguments) = value_arguments {
for argument in arguments { for argument in arguments {
self.analyze_statement(argument)?; 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) => { Statement::Identifier(identifier) => {
@ -537,7 +566,7 @@ pub enum AnalyzerError {
}, },
UndefinedField { UndefinedField {
identifier: Node<Statement>, identifier: Node<Statement>,
map: Node<Statement>, statement: Node<Statement>,
}, },
UndefinedType { UndefinedType {
identifier: Node<Identifier>, identifier: Node<Identifier>,
@ -625,7 +654,10 @@ impl Display for AnalyzerError {
expected, actual_statement, actual_type expected, actual_statement, actual_type
) )
} }
AnalyzerError::UndefinedField { identifier, map } => { AnalyzerError::UndefinedField {
identifier,
statement: map,
} => {
write!(f, "Undefined field {} in map {}", identifier, map) write!(f, "Undefined field {} in map {}", identifier, map)
} }
AnalyzerError::UndefinedType { identifier } => { AnalyzerError::UndefinedType { identifier } => {
@ -650,6 +682,26 @@ mod tests {
use super::*; 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] #[test]
fn constant_list_index_out_of_bounds() { fn constant_list_index_out_of_bounds() {
let source = "[1, 2, 3][3]"; let source = "[1, 2, 3][3]";
@ -684,7 +736,7 @@ mod tests {
Err(DustError::AnalyzerError { Err(DustError::AnalyzerError {
analyzer_error: AnalyzerError::UndefinedField { analyzer_error: AnalyzerError::UndefinedField {
identifier: Node::new(Statement::Identifier(Identifier::new("y")), (10, 11)), identifier: Node::new(Statement::Identifier(Identifier::new("y")), (10, 11)),
map: Node::new( statement: Node::new(
Statement::Map(vec![( Statement::Map(vec![(
Node::new(Statement::Identifier(Identifier::new("x")), (2, 3)), Node::new(Statement::Identifier(Identifier::new("x")), (2, 3)),
Node::new(Statement::Constant(Value::integer(1)), (6, 7)) Node::new(Statement::Constant(Value::integer(1)), (6, 7))
@ -706,7 +758,7 @@ mod tests {
Err(DustError::AnalyzerError { Err(DustError::AnalyzerError {
analyzer_error: AnalyzerError::UndefinedField { analyzer_error: AnalyzerError::UndefinedField {
identifier: Node::new(Statement::Constant(Value::string("y")), (10, 13)), identifier: Node::new(Statement::Constant(Value::string("y")), (10, 13)),
map: Node::new( statement: Node::new(
Statement::Map(vec![( Statement::Map(vec![(
Node::new(Statement::Identifier(Identifier::new("x")), (2, 3)), Node::new(Statement::Identifier(Identifier::new("x")), (2, 3)),
Node::new(Statement::Constant(Value::integer(1)), (6, 7)) Node::new(Statement::Constant(Value::integer(1)), (6, 7))

View File

@ -1027,9 +1027,7 @@ impl Display for ParseError {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{BinaryOperator, Identifier, StructDefinition, Type, UnaryOperator};
BinaryOperator, Identifier, StructDefinition, StructInstantiation, Type, UnaryOperator,
};
use super::*; use super::*;

View File

@ -866,6 +866,23 @@ mod tests {
use super::*; 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] #[test]
fn define_and_instantiate_tuple_struct() { fn define_and_instantiate_tuple_struct() {
let input = "struct Foo(int) Foo(42)"; let input = "struct Foo(int) Foo(42)";