From a226869d170c66a3d63b2f38965df17eb1501831 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 5 Sep 2026 12:49:07 +0000 Subject: [PATCH] fix: arity checking of call expression arguments Zipping the arguments with the parameters silently truncated to the shorter of the two. Calling with too few arguments left the unbound parameters resolving to Null, whilst extra arguments were discarded without ever being evaluated, so their side effects never ran. Compare the counts before binding and error with "Wrong number of arguments, want=2, got=1." instead. The check runs before the environment is pushed, so a rejected call leaves no scope behind. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01WVqA23HZyr13UowhjHjNEt --- src/evaluator/expression/call/mod.rs | 8 +++ src/tests/evaluation_error/mod.rs | 1 + .../wrong_number_of_arguments/mod.rs | 30 +++++++++++ ..._of_arguments_case_1_evaluation_error.snap | 7 +++ ..._of_arguments_case_1_lexical_analysis.snap | 31 ++++++++++++ ...r_of_arguments_case_1_syntax_analysis.snap | 36 +++++++++++++ ..._of_arguments_case_2_evaluation_error.snap | 7 +++ ..._of_arguments_case_2_lexical_analysis.snap | 48 ++++++++++++++++++ ...r_of_arguments_case_2_syntax_analysis.snap | 50 +++++++++++++++++++ ..._of_arguments_case_3_evaluation_error.snap | 7 +++ ..._of_arguments_case_3_lexical_analysis.snap | 20 ++++++++ ...r_of_arguments_case_3_syntax_analysis.snap | 25 ++++++++++ ..._of_arguments_case_4_evaluation_error.snap | 7 +++ ..._of_arguments_case_4_lexical_analysis.snap | 20 ++++++++ ...r_of_arguments_case_4_syntax_analysis.snap | 27 ++++++++++ 15 files changed, 324 insertions(+) create mode 100644 src/tests/evaluation_error/wrong_number_of_arguments/mod.rs create mode 100644 src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_1_evaluation_error.snap create mode 100644 src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_1_lexical_analysis.snap create mode 100644 src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_1_syntax_analysis.snap create mode 100644 src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_2_evaluation_error.snap create mode 100644 src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_2_lexical_analysis.snap create mode 100644 src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_2_syntax_analysis.snap create mode 100644 src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_3_evaluation_error.snap create mode 100644 src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_3_lexical_analysis.snap create mode 100644 src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_3_syntax_analysis.snap create mode 100644 src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_4_evaluation_error.snap create mode 100644 src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_4_lexical_analysis.snap create mode 100644 src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_4_syntax_analysis.snap diff --git a/src/evaluator/expression/call/mod.rs b/src/evaluator/expression/call/mod.rs index 14369b8..8ee675f 100644 --- a/src/evaluator/expression/call/mod.rs +++ b/src/evaluator/expression/call/mod.rs @@ -10,6 +10,14 @@ impl Evaluator { ) -> anyhow::Result { match self.evaluate_expression(function)? { Object::Function { parameters, block } => { + if arguments.len() != parameters.len() { + anyhow::bail!( + "Wrong number of arguments, want={}, got={}.", + parameters.len(), + arguments.len() + ); + } + self.environment.push(); for (argument, parameter_identifier) in arguments.into_iter().zip(parameters) { diff --git a/src/tests/evaluation_error/mod.rs b/src/tests/evaluation_error/mod.rs index 6749ce0..355085a 100644 --- a/src/tests/evaluation_error/mod.rs +++ b/src/tests/evaluation_error/mod.rs @@ -10,3 +10,4 @@ mod type_mismatch_integer_infix_expression; mod type_mismatch_integer_prefix_expression; mod uncallable_object; mod unknown_operator_integer_infix_expression; +mod wrong_number_of_arguments; diff --git a/src/tests/evaluation_error/wrong_number_of_arguments/mod.rs b/src/tests/evaluation_error/wrong_number_of_arguments/mod.rs new file mode 100644 index 0000000..90b113a --- /dev/null +++ b/src/tests/evaluation_error/wrong_number_of_arguments/mod.rs @@ -0,0 +1,30 @@ +use super::*; + +#[template] +#[rstest( + code, + snapshot_name, + case("fn(x, y) { x + y }(1);", "wrong_number_of_arguments_case_1"), + case( + "let add = fn(x, y) { x + y }; add(1, 2, 3);", + "wrong_number_of_arguments_case_2" + ), + case("fn(x) { x }();", "wrong_number_of_arguments_case_3"), + case("fn() { 5 }(1);", "wrong_number_of_arguments_case_4") +)] +fn wrong_number_of_arguments_cases(code: &str, snapshot_name: &str) {} + +#[apply(wrong_number_of_arguments_cases)] +fn test_wrong_number_of_arguments_lexical_analysis(code: &str, snapshot_name: &str) { + assert_lexical_analysis!(code, snapshot_name); +} + +#[apply(wrong_number_of_arguments_cases)] +fn test_wrong_number_of_arguments_syntax_analysis(code: &str, snapshot_name: &str) { + assert_syntax_analysis!(code, snapshot_name); +} + +#[apply(wrong_number_of_arguments_cases)] +fn test_wrong_number_of_arguments_evaluation_error(code: &str, snapshot_name: &str) { + assert_evaluation_error!(code, snapshot_name); +} diff --git a/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_1_evaluation_error.snap b/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_1_evaluation_error.snap new file mode 100644 index 0000000..082943b --- /dev/null +++ b/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_1_evaluation_error.snap @@ -0,0 +1,7 @@ +--- +source: src/tests/evaluation_error/wrong_number_of_arguments/mod.rs +expression: error +--- +Err( + "Wrong number of arguments, want=2, got=1.", +) diff --git a/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_1_lexical_analysis.snap b/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_1_lexical_analysis.snap new file mode 100644 index 0000000..4dee2e1 --- /dev/null +++ b/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_1_lexical_analysis.snap @@ -0,0 +1,31 @@ +--- +source: src/tests/evaluation_error/wrong_number_of_arguments/mod.rs +expression: tokens +--- +[ + Function, + OpeningRoundBracket, + Identifier { + literal: "x", + }, + Comma, + Identifier { + literal: "y", + }, + ClosingRoundBracket, + OpeningCurlyBracket, + Identifier { + literal: "x", + }, + Plus, + Identifier { + literal: "y", + }, + ClosingCurlyBracket, + OpeningRoundBracket, + Integer { + literal: 1, + }, + ClosingRoundBracket, + SemiColon, +] diff --git a/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_1_syntax_analysis.snap b/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_1_syntax_analysis.snap new file mode 100644 index 0000000..c0b6d35 --- /dev/null +++ b/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_1_syntax_analysis.snap @@ -0,0 +1,36 @@ +--- +source: src/tests/evaluation_error/wrong_number_of_arguments/mod.rs +expression: abstract_syntax_tree +--- +[ + Expression { + expression: Call { + function: Function { + parameters: [ + "x", + "y", + ], + block: Block { + nodes: [ + Expression { + expression: Infix { + left_hand: Identifier { + identifier: "x", + }, + operator: Plus, + right_hand: Identifier { + identifier: "y", + }, + }, + }, + ], + }, + }, + arguments: [ + Integer { + literal: 1, + }, + ], + }, + }, +] diff --git a/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_2_evaluation_error.snap b/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_2_evaluation_error.snap new file mode 100644 index 0000000..e048d30 --- /dev/null +++ b/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_2_evaluation_error.snap @@ -0,0 +1,7 @@ +--- +source: src/tests/evaluation_error/wrong_number_of_arguments/mod.rs +expression: error +--- +Err( + "Wrong number of arguments, want=2, got=3.", +) diff --git a/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_2_lexical_analysis.snap b/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_2_lexical_analysis.snap new file mode 100644 index 0000000..0ca7a2a --- /dev/null +++ b/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_2_lexical_analysis.snap @@ -0,0 +1,48 @@ +--- +source: src/tests/evaluation_error/wrong_number_of_arguments/mod.rs +expression: tokens +--- +[ + Let, + Identifier { + literal: "add", + }, + Assign, + Function, + OpeningRoundBracket, + Identifier { + literal: "x", + }, + Comma, + Identifier { + literal: "y", + }, + ClosingRoundBracket, + OpeningCurlyBracket, + Identifier { + literal: "x", + }, + Plus, + Identifier { + literal: "y", + }, + ClosingCurlyBracket, + SemiColon, + Identifier { + literal: "add", + }, + OpeningRoundBracket, + Integer { + literal: 1, + }, + Comma, + Integer { + literal: 2, + }, + Comma, + Integer { + literal: 3, + }, + ClosingRoundBracket, + SemiColon, +] diff --git a/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_2_syntax_analysis.snap b/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_2_syntax_analysis.snap new file mode 100644 index 0000000..ce28285 --- /dev/null +++ b/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_2_syntax_analysis.snap @@ -0,0 +1,50 @@ +--- +source: src/tests/evaluation_error/wrong_number_of_arguments/mod.rs +expression: abstract_syntax_tree +--- +[ + Statement { + statement: Let { + identifier: "add", + expression: Function { + parameters: [ + "x", + "y", + ], + block: Block { + nodes: [ + Expression { + expression: Infix { + left_hand: Identifier { + identifier: "x", + }, + operator: Plus, + right_hand: Identifier { + identifier: "y", + }, + }, + }, + ], + }, + }, + }, + }, + Expression { + expression: Call { + function: Identifier { + identifier: "add", + }, + arguments: [ + Integer { + literal: 1, + }, + Integer { + literal: 2, + }, + Integer { + literal: 3, + }, + ], + }, + }, +] diff --git a/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_3_evaluation_error.snap b/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_3_evaluation_error.snap new file mode 100644 index 0000000..0bb0830 --- /dev/null +++ b/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_3_evaluation_error.snap @@ -0,0 +1,7 @@ +--- +source: src/tests/evaluation_error/wrong_number_of_arguments/mod.rs +expression: error +--- +Err( + "Wrong number of arguments, want=1, got=0.", +) diff --git a/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_3_lexical_analysis.snap b/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_3_lexical_analysis.snap new file mode 100644 index 0000000..3757305 --- /dev/null +++ b/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_3_lexical_analysis.snap @@ -0,0 +1,20 @@ +--- +source: src/tests/evaluation_error/wrong_number_of_arguments/mod.rs +expression: tokens +--- +[ + Function, + OpeningRoundBracket, + Identifier { + literal: "x", + }, + ClosingRoundBracket, + OpeningCurlyBracket, + Identifier { + literal: "x", + }, + ClosingCurlyBracket, + OpeningRoundBracket, + ClosingRoundBracket, + SemiColon, +] diff --git a/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_3_syntax_analysis.snap b/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_3_syntax_analysis.snap new file mode 100644 index 0000000..af3488c --- /dev/null +++ b/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_3_syntax_analysis.snap @@ -0,0 +1,25 @@ +--- +source: src/tests/evaluation_error/wrong_number_of_arguments/mod.rs +expression: abstract_syntax_tree +--- +[ + Expression { + expression: Call { + function: Function { + parameters: [ + "x", + ], + block: Block { + nodes: [ + Expression { + expression: Identifier { + identifier: "x", + }, + }, + ], + }, + }, + arguments: [], + }, + }, +] diff --git a/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_4_evaluation_error.snap b/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_4_evaluation_error.snap new file mode 100644 index 0000000..5dbd7c0 --- /dev/null +++ b/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_4_evaluation_error.snap @@ -0,0 +1,7 @@ +--- +source: src/tests/evaluation_error/wrong_number_of_arguments/mod.rs +expression: error +--- +Err( + "Wrong number of arguments, want=0, got=1.", +) diff --git a/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_4_lexical_analysis.snap b/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_4_lexical_analysis.snap new file mode 100644 index 0000000..99c37fc --- /dev/null +++ b/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_4_lexical_analysis.snap @@ -0,0 +1,20 @@ +--- +source: src/tests/evaluation_error/wrong_number_of_arguments/mod.rs +expression: tokens +--- +[ + Function, + OpeningRoundBracket, + ClosingRoundBracket, + OpeningCurlyBracket, + Integer { + literal: 5, + }, + ClosingCurlyBracket, + OpeningRoundBracket, + Integer { + literal: 1, + }, + ClosingRoundBracket, + SemiColon, +] diff --git a/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_4_syntax_analysis.snap b/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_4_syntax_analysis.snap new file mode 100644 index 0000000..2b3e33a --- /dev/null +++ b/src/tests/evaluation_error/wrong_number_of_arguments/snapshots/monkey_interpreter__tests__evaluation_error__wrong_number_of_arguments__test_wrong_number_of_arguments_case_4_syntax_analysis.snap @@ -0,0 +1,27 @@ +--- +source: src/tests/evaluation_error/wrong_number_of_arguments/mod.rs +expression: abstract_syntax_tree +--- +[ + Expression { + expression: Call { + function: Function { + parameters: [], + block: Block { + nodes: [ + Expression { + expression: Integer { + literal: 5, + }, + }, + ], + }, + }, + arguments: [ + Integer { + literal: 1, + }, + ], + }, + }, +]