diff --git a/analyzer/src/errors.rs b/analyzer/src/errors.rs index 264ffeaf15..f0f16299c5 100644 --- a/analyzer/src/errors.rs +++ b/analyzer/src/errors.rs @@ -8,6 +8,7 @@ use fe_parser::span::Span; pub enum ErrorKind { BreakWithoutLoop, ContinueWithoutLoop, + KeyWordArgsRequired, MissingReturn, NotSubscriptable, NumericCapacityMismatch, @@ -48,6 +49,14 @@ impl SemanticError { } } + /// Create a new error with kind `KeyWordArgsRequired` + pub fn kw_args_required() -> Self { + SemanticError { + kind: ErrorKind::KeyWordArgsRequired, + context: vec![], + } + } + /// Create a new error with kind `MissingReturn` pub fn missing_return() -> Self { SemanticError { diff --git a/analyzer/src/traversal/expressions.rs b/analyzer/src/traversal/expressions.rs index 47de9e86a0..b389a97e8c 100644 --- a/analyzer/src/traversal/expressions.rs +++ b/analyzer/src/traversal/expressions.rs @@ -530,6 +530,7 @@ fn expr_call_struct_constructor( typ: Struct, args: &Spanned>>, ) -> Result { + validate_are_kw_args(&args.node)?; let argument_attributes = expr_call_args(Rc::clone(&scope), Rc::clone(&context), args)?; if typ.get_field_types() != expression_attributes_to_types(argument_attributes) { @@ -888,6 +889,17 @@ fn expr_attribute_call_type( unreachable!() } +fn validate_are_kw_args(args: &[Spanned]) -> Result<(), SemanticError> { + if args + .iter() + .any(|arg| matches!(arg.node, fe::CallArg::Arg(_))) + { + return Err(SemanticError::kw_args_required()); + } + + Ok(()) +} + fn validate_is_numeric_literal(call_arg: &fe::CallArg) -> Result { if let fe::CallArg::Arg(fe::Expr::UnaryOperation { operand, op: _ }) = call_arg { if let fe::Expr::Num(num) = (*operand).node { diff --git a/compiler/tests/compile_errors.rs b/compiler/tests/compile_errors.rs index 9c5fd899a8..93dfa900a1 100644 --- a/compiler/tests/compile_errors.rs +++ b/compiler/tests/compile_errors.rs @@ -28,6 +28,7 @@ use std::fs; case("type_constructor_from_variable.fe", "NumericLiteralExpected"), case("needs_mem_copy.fe", "CannotMove"), case("string_capacity_mismatch.fe", "StringCapacityMismatch"), + case("struct_call_without_kw_args.fe", "KeyWordArgsRequired"), case("numeric_capacity_mismatch/u8_neg.fe", "NumericCapacityMismatch"), case("numeric_capacity_mismatch/u8_pos.fe", "NumericCapacityMismatch"), case("numeric_capacity_mismatch/u16_neg.fe", "NumericCapacityMismatch"), diff --git a/compiler/tests/fixtures/compile_errors/struct_call_without_kw_args.fe b/compiler/tests/fixtures/compile_errors/struct_call_without_kw_args.fe new file mode 100644 index 0000000000..93ac31dcf7 --- /dev/null +++ b/compiler/tests/fixtures/compile_errors/struct_call_without_kw_args.fe @@ -0,0 +1,8 @@ +struct House: + vacant: bool + price: u256 + +contract Foo: + + pub def bar(): + my_house: House = House(true, price=1000000) \ No newline at end of file diff --git a/compiler/tests/fixtures/structs.fe b/compiler/tests/fixtures/structs.fe index 184faed4c9..bef5ce4d6a 100644 --- a/compiler/tests/fixtures/structs.fe +++ b/compiler/tests/fixtures/structs.fe @@ -7,7 +7,7 @@ contract Foo: my_house: House pub def create_house(): - self.my_house = House(1,2,false) + self.my_house = House(price=1, size=2, vacant=false) assert self.my_house.price == 1 assert self.my_house.size == 2 assert self.my_house.vacant == false @@ -25,7 +25,7 @@ contract Foo: pub def bar() -> u256: - building: House = House(300, 500, true) + building: House = House(price=300, size=500, vacant=true) assert building.size == 500 assert building.price == 300 assert building.vacant diff --git a/newsfragments/260.feature.md b/newsfragments/260.feature.md new file mode 100644 index 0000000000..41f055f3ca --- /dev/null +++ b/newsfragments/260.feature.md @@ -0,0 +1,15 @@ +Require structs to be initialized using keyword arguments. + +Example: + +``` +struct House: + vacant: bool + price: u256 +``` + +Previously, `House` could be instantiated as `House(true, 1000000)`. +With this change it is required to be instantiated like `House(vacant=true, price=1000000)` + +This ensures property assignment is less prone to get mixed up. It also makes struct +initialization visually stand out more from function calls. \ No newline at end of file