From 5d6ae376f02d0883ebb5f8a4ca1840bf4cbfe97e Mon Sep 17 00:00:00 2001 From: Marcos Henrich Date: Tue, 23 Apr 2024 11:57:54 +0100 Subject: [PATCH] Fixes argument errors not displayed. type_check_method_application does the parsing of arguments in 2 passes, but when the resolved_method_name failed the argument error would not be displayed. We now store the arg_handlers and append their errors in case resolve_method_name fails. Fixes #5660 --- .../typed_expression/method_application.rs | 31 ++++++++++++++----- .../variable_does_not_exist/Forc.lock | 8 +++++ .../variable_does_not_exist/Forc.toml | 9 ++++++ .../variable_does_not_exist/src/main.sw | 23 ++++++++++++++ .../variable_does_not_exist/test.toml | 31 +++++++++++++++++++ 5 files changed, 94 insertions(+), 8 deletions(-) create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/variable_does_not_exist/Forc.lock create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/variable_does_not_exist/Forc.toml create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/variable_does_not_exist/src/main.sw create mode 100644 test/src/e2e_vm_tests/test_programs/should_fail/variable_does_not_exist/test.toml diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs index bd4dd0f4572..53b21cd0e90 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs @@ -47,40 +47,55 @@ pub(crate) fn type_check_method_application( .with_help_text("") .with_type_annotation(type_engine.insert(engines, TypeInfo::Unknown, None)); if index == 0 { - args_opt_buf.push_back(ty::TyExpression::type_check(handler, ctx, arg.clone()).ok()); + args_opt_buf.push_back(( + ty::TyExpression::type_check(handler, ctx, arg.clone()).ok(), + None, + )); } else { // Ignore errors in method parameters // On the second pass we will throw the errors if they persist. let arg_handler = Handler::default(); let arg_opt = ty::TyExpression::type_check(&arg_handler, ctx, arg.clone()).ok(); if arg_handler.has_errors() { - args_opt_buf.push_back(None); + args_opt_buf.push_back((None, Some(arg_handler))); } else { - args_opt_buf.push_back(arg_opt); + args_opt_buf.push_back((arg_opt, None)); } }; } // resolve the method name to a typed function declaration and type_check - let (original_decl_ref, call_path_typeid) = resolve_method_name( + let method_result = resolve_method_name( handler, ctx.by_ref(), &method_name_binding, args_opt_buf .iter() .map(|arg| match arg { - Some(arg) => arg.return_type, - None => type_engine.insert(engines, TypeInfo::Unknown, None), + (Some(arg), _) => arg.return_type, + (None, _) => type_engine.insert(engines, TypeInfo::Unknown, None), }) .collect(), - )?; + ); + + // In case resolve_method_name fails throw argument errors. + let (original_decl_ref, call_path_typeid) = if let Err(e) = method_result { + for (_, arg_handler) in args_opt_buf.iter() { + if let Some(arg_handler) = arg_handler.clone() { + handler.append(arg_handler); + } + } + return Err(e); + } else { + method_result.unwrap() + }; let method = decl_engine.get_function(&original_decl_ref); // type check the function arguments (2nd pass) let mut args_buf = VecDeque::new(); for (arg, index, arg_opt) in izip!(arguments.iter(), 0.., args_opt_buf.iter().cloned()) { - if let Some(arg) = arg_opt { + if let (Some(arg), _) = arg_opt { args_buf.push_back(arg); } else { let param_index = if method.is_contract_call { diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/variable_does_not_exist/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/variable_does_not_exist/Forc.lock new file mode 100644 index 00000000000..478b462e84b --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/variable_does_not_exist/Forc.lock @@ -0,0 +1,8 @@ +[[package]] +name = "core" +source = "path+from-root-1C5801B8398D8ED4" + +[[package]] +name = "variable_does_not_exist" +source = "member" +dependencies = ["core"] diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/variable_does_not_exist/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/variable_does_not_exist/Forc.toml new file mode 100644 index 00000000000..31713fc97e7 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/variable_does_not_exist/Forc.toml @@ -0,0 +1,9 @@ +[project] +name = "variable_does_not_exist" +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +implicit-std = false + +[dependencies] +core = { path = "../../../../../../sway-lib-core" } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/variable_does_not_exist/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/variable_does_not_exist/src/main.sw new file mode 100644 index 00000000000..73cc041ae7b --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/variable_does_not_exist/src/main.sw @@ -0,0 +1,23 @@ +script; + +struct S {} + +impl S { + fn associated(a: u64, b: u64, c: u64) -> u64 { + a + b + c + } +} + +fn function(a: u64, b: u64, c: u64) -> u64 { + a + b + c +} + +fn main() { + let _ = S::associated(x, y, z); + + + let _ = function(x, y, z); + + + let _ = x + y + z; +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/variable_does_not_exist/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/variable_does_not_exist/test.toml new file mode 100644 index 00000000000..e832fd4ea2c --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/variable_does_not_exist/test.toml @@ -0,0 +1,31 @@ +category = "fail" + +# check: $()let _ = S::associated(x, y, z); +# nextln:$()Variable "x" does not exist in this scope. + +# check: $()let _ = S::associated(x, y, z); +# nextln:$()Variable "y" does not exist in this scope. + +# check: $()let _ = S::associated(x, y, z); +# nextln:$()Variable "z" does not exist in this scope. + +# check: $()let _ = function(x, y, z); +# nextln:$()Variable "x" does not exist in this scope. + +# check: $()let _ = function(x, y, z); +# nextln:$()Variable "y" does not exist in this scope. + +# check: $()let _ = function(x, y, z); +# nextln:$()Variable "z" does not exist in this scope. + +# check: $()let _ = x + y + z; +# nextln: $()Variable "x" does not exist in this scope. + +# check: $()let _ = x + y + z; +# nextln: $()Variable "y" does not exist in this scope. + +# check: $()let _ = x + y + z; +# nextln: $()No method named "add" found for type "{unknown}". + +# check: $()let _ = x + y + z; +# nextln: $()Variable "z" does not exist in this scope.