diff --git a/src/psyclone/psyir/nodes/call.py b/src/psyclone/psyir/nodes/call.py index 499f3eb7a1..ff41a0dfc5 100644 --- a/src/psyclone/psyir/nodes/call.py +++ b/src/psyclone/psyir/nodes/call.py @@ -51,6 +51,12 @@ from typing import List +class CallMatchingArgumentsNotFound(BaseException): + """Excepction to signal that matching arguments have not been found + for this routine + """ + + class Call(Statement, DataNode): ''' Node representing a Call. This can be found as a standalone statement or an expression. @@ -258,7 +264,8 @@ def replace_named_arg(self, existing_name, arg): raise ValueError( f"The value of the existing_name argument ({existing_name}) " f"in 'replace_named_arg' in the 'Call' node was not found " - f"in the existing arguments.") + f"in the existing arguments." + ) # The n'th argument is placed at the n'th+1 children position # because the 1st child is the routine reference self.children[index + 1] = arg @@ -594,11 +601,6 @@ def _location_txt(node): f"{_location_txt(root_node)}. This is normally because the routine" f" is within a CodeBlock.") - class MatchingArgumentsNotFound(BaseException): - """Excepction to signal that matching arguments have not been found - for this routine - """ - def get_argument_routine_match(self, routine: Routine): """Return a list of integers giving for each argument of the call the index of the argument in argument_list (typically of a routine) @@ -615,12 +617,8 @@ def get_argument_routine_match(self, routine: Routine): routine_argument_list: List[DataNode] = \ routine.symbol_table.argument_list[:] - # Find matching argument list - # if len(self.arguments) != len(routine_argument_list): - # return None - if len(self.arguments) > len(routine.symbol_table.argument_list): - raise self.MatchingArgumentsNotFound( + raise CallMatchingArgumentsNotFound( f"More arguments in callee (call '{self.routine.name}')" f" than caller (routine '{routine.name}')" ) @@ -648,7 +646,7 @@ def get_argument_routine_match(self, routine: Routine): routine_arg.datatype, UnsupportedFortranType): if call_arg.datatype != routine_arg.datatype: - raise self.MatchingArgumentsNotFound( + raise CallMatchingArgumentsNotFound( f"Argument type mismatch of call argument " f"'{call_arg}' and routine argument " f"'{routine_arg}'" @@ -680,7 +678,7 @@ def get_argument_routine_match(self, routine: Routine): routine_arg.datatype, UnsupportedFortranType): if call_arg.datatype != routine_arg.datatype: - raise self.MatchingArgumentsNotFound( + raise CallMatchingArgumentsNotFound( f"Argument type mismatch of call argument " f"'{call_arg}' and routine argument " f"'{routine_arg}'" @@ -692,10 +690,9 @@ def get_argument_routine_match(self, routine: Routine): if not named_arg_found: # It doesn't match => Raise exception - raise self.MatchingArgumentsNotFound - - if routine_arg_idx is None: - raise IndexError("Internal error") + raise CallMatchingArgumentsNotFound( + f"Named argument '{arg_name}' not found" + ) routine_argument_list[routine_arg_idx] = None @@ -713,8 +710,8 @@ def get_argument_routine_match(self, routine: Routine): if ", OPTIONAL" in str(routine_arg.datatype): continue - raise self.MatchingArgumentsNotFound( - f"Argument '{routine_arg}' in subroutine" + raise CallMatchingArgumentsNotFound( + f"Argument '{routine_arg.name}' in subroutine" f" '{routine.name}' not handled" ) @@ -740,15 +737,20 @@ def get_callee( routine_list = self.get_callees() + error: Exception = None + # Search for the routine matching the right arguments for routine in routine_list: routine: Routine try: arg_match_list = self.get_argument_routine_match(routine) - except self.MatchingArgumentsNotFound: + except CallMatchingArgumentsNotFound as err: + error = err continue + error = None + if ret_arg_match_list is not None: ret_arg_match_list[:] = arg_match_list @@ -761,5 +763,9 @@ def get_callee( if not check_matching_arguments: return routine_list[0] - raise NotImplementedError(f"No matching routine for call " - f"'{self.routine.name}' found") + if error is not None: + raise error + else: + raise NotImplementedError( + f"No matching routine for call " f"'{self.routine.name}' found" + ) diff --git a/src/psyclone/tests/psyir/nodes/call_test.py b/src/psyclone/tests/psyir/nodes/call_test.py index bbc68984d6..cb1d4d4440 100644 --- a/src/psyclone/tests/psyir/nodes/call_test.py +++ b/src/psyclone/tests/psyir/nodes/call_test.py @@ -48,6 +48,7 @@ from psyclone.psyir.symbols import ( ArrayType, INTEGER_TYPE, DataSymbol, NoType, RoutineSymbol, REAL_TYPE, SymbolError, UnsupportedFortranType) +from psyclone.psyir.nodes.call import CallMatchingArgumentsNotFound from psyclone.errors import GenerationError @@ -741,7 +742,9 @@ def test_call_get_callee_3_trigger_error(fortran_reader): try: call_foo.get_callee(ret_arg_match_list=arg_idx_list) - except Exception: + + except CallMatchingArgumentsNotFound as err: + assert "More arguments in callee" in str(err) print("Success! Exception triggered (as expected)") return @@ -1073,6 +1076,92 @@ def test_call_get_callee_6_interfaces(fortran_reader): print(" - Passed subtest foo_c[2]") +def test_call_get_callee_7_matching_arguments_not_found(fortran_reader): + """ + Trigger error that matching arguments were not found + """ + code = """ +module some_mod + implicit none +contains + + subroutine main() + integer :: e, f, g + ! Use name 'd' which doesn't exist + call foo(e, f, d=g) + end subroutine + + ! Matching routine + subroutine foo(a, b, c) + integer :: a, b, c + end subroutine + +end module some_mod""" + + psyir = fortran_reader.psyir_from_source(code) + + routine_main: Routine = psyir.walk(Routine)[0] + assert routine_main.name == "main" + + call_foo: Call = routine_main.walk(Call)[0] + + try: + call_foo.get_callee() + + except CallMatchingArgumentsNotFound as err: + assert "Named argument 'd' not found" in str(err) + print("Success! Exception triggered (as expected)") + return + + assert False, ( + "This should have triggered an error since there" + "are more arguments in the call than in the routine" + ) + + +def test_call_get_callee_8_arguments_not_handled(fortran_reader): + """ + Trigger error that matching arguments were not found + """ + code = """ +module some_mod + implicit none +contains + + subroutine main() + integer :: e, f + ! Use name 'd' which doesn't exist + call foo(e, f) + end subroutine + + ! Matching routine + subroutine foo(a, b, c) + integer :: a, b, c + end subroutine + +end module some_mod""" + + psyir = fortran_reader.psyir_from_source(code) + + routine_main: Routine = psyir.walk(Routine)[0] + assert routine_main.name == "main" + + call_foo: Call = routine_main.walk(Call)[0] + + try: + call_foo.get_callee() + + except CallMatchingArgumentsNotFound as err: + assert "Argument 'c' in subroutine 'foo' not handled" in str(err) + print("Success! Exception triggered (as expected)") + return + + assert False, ( + "This should have triggered an error since there" + "are more arguments in the call than in the routine" + ) + + @pytest.mark.usefixtures("clear_module_manager_instance") def test_call_get_callees_unresolved(fortran_reader, tmpdir, monkeypatch): '''