Coverage for src/castep_linter/tests/has_trace_entry_exit.py: 93%

1"""Test that a subroutine or function has a trace_entry and trace_exit with the correct name"""

2 from castep_linter.error_logging import ErrorLogger

3 from castep_linter.fortran import CallExpression, FType, VariableDeclaration

4 from castep_linter.fortran.fortran_node import Fortran, FortranNode, WrongNodeError

5 from castep_linter.fortran.identifier import Identifier

6 from castep_linter.tests import castep_identifiers

7

8

9 def test_trace_entry_exit(node: FortranNode, error_log: ErrorLogger) -> None:

10 """Test that a subroutine or function has a trace_entry and trace_exit with the correct name"""

11

12 if node.is_type(Fortran.SUBROUTINE):

13 subroutine_name = Identifier.from_node(node.get(Fortran.SUBROUTINE_STMT).get(Fortran.NAME))

14 elif node.is_type(Fortran.FUNCTION): 14 ↛ 17line 14 didn't jump to line 17, because the condition on line 14 was never false

15 subroutine_name = Identifier.from_node(node.get(Fortran.FUNCTION_STMT).get(Fortran.NAME))

16 else:

17 err = "Wrong node type passed"

18 raise WrongNodeError(err)

19

20 has_trace_entry = False

21 has_trace_exit = False

22

23 const_string_vars = {}

24

25 for var_node in node.get_children_by_name(Fortran.VARIABLE_DECLARATION):

26 var_decl = VariableDeclaration(var_node)

27

28 if var_decl.type != FType.CHARACTER:

29 continue

30

31 for var_name, initial_value in var_decl.vars.items():

32 if initial_value:

33 const_string_vars[var_name] = initial_value.lower()

34

35 for statement in node.get_children_by_name(Fortran.SUBROUTINE_CALL):

36 routine = CallExpression(statement)

37

38 if routine.name == castep_identifiers.TRACE_ENTRY:

39 has_trace_entry = True

40 elif routine.name == castep_identifiers.TRACE_EXIT:

41 has_trace_exit = True

42

43 if routine.name in [castep_identifiers.TRACE_ENTRY, castep_identifiers.TRACE_EXIT]:

44 try:

45 _, trace_node = routine.get_arg(position=1, keyword=castep_identifiers.TRACE_STRING)

46 except KeyError:

47 err = f"Unparsable name passed to trace in {subroutine_name}"

48 error_log.add_msg("Error", statement, err)

49 continue

50

51 if trace_node.is_type(Fortran.STRING_LITERAL):

52 trace_string = trace_node.parse_string_literal().lower()

53 if trace_string != subroutine_name:

54 err = f"Incorrect name passed to trace in {subroutine_name}"

55 error_log.add_msg("Error", trace_node, err)

56

57 elif trace_node.is_type(Fortran.IDENTIFIER): 57 ↛ 74line 57 didn't jump to line 74, because the condition on line 57 was never false

58 trace_sub_text = Identifier.from_node(trace_node)

59

60 if trace_sub_text in const_string_vars:

61 trace_string = const_string_vars[trace_sub_text]

62

63 if trace_string.lower() != subroutine_name:

64 err = (

65 f"Incorrect name passed to trace in {subroutine_name} "

66 f'by variable {trace_sub_text}="{trace_string}"'

67 )

68 error_log.add_msg("Error", trace_node, err)

69 else:

70 err = f"Unidentified variable {trace_sub_text} passed to trace in {subroutine_name}"

71 error_log.add_msg("Error", trace_node, err)

72

73 else:

74 err = f"Unrecognisable {statement.raw} {trace_node.type=} {statement}"

75 raise ValueError(err)

76

77 if not has_trace_entry:

78 error_log.add_msg("Warning", node, f"Missing trace_entry in {subroutine_name}")

79 if not has_trace_exit:

80 error_log.add_msg("Warning", node, f"Missing trace_exit in {subroutine_name}")