Coverage for src/castep_linter/error_logging/error_types.py: 63%

56 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-11-23 18:07 +0000

1"""Module to handle errors, warnings and info messages""" 

2from typing import ClassVar 

3 

4from castep_linter.fortran.fortran_node import FortranNode 

5 

6 

7class FortranMsgBase: 

8 """Base class for other static analysis problems to inherit from""" 

9 

10 ERROR_TYPE: ClassVar[str] = "NONE" 

11 ERROR_STYLE: ClassVar[str] = "grey" 

12 LINE_NUMBER_OFFSET = 8 

13 ERROR_SEVERITY: ClassVar[int] = 100 

14 

15 def __init__(self, node: FortranNode, message: str) -> None: 

16 self.message = message 

17 self.start_point = node.node.start_point # TODO FIX 

18 self.end_point = node.node.end_point 

19 

20 def print_err(self, filename: str, console) -> None: 

21 """Print the error to the supplied console""" 

22 console.print(self, style=self.ERROR_STYLE) 

23 context = self.context(filename, underline=True) 

24 if context: 

25 console.print(context) 

26 

27 def context(self, filename, *, underline=False): 

28 """Print a line of context for the current error""" 

29 context = "" 

30 

31 with open(filename, "rb") as fd: 

32 start_line, start_char = self.start_point 

33 end_line, end_char = self.end_point 

34 num_lines = end_line - start_line + 1 

35 num_chars = end_char - start_char 

36 

37 file_str = str(filename) 

38 

39 if num_lines == 1: 

40 line = fd.read().splitlines()[start_line].decode(errors="replace") 

41 context = f"{file_str}:{start_line+1:{self.LINE_NUMBER_OFFSET}}>{line}" 

42 if underline: 

43 context += ( 

44 "\n" 

45 + " " * (len(file_str) + 1) 

46 + " " * (self.LINE_NUMBER_OFFSET + 1) 

47 + " " * start_char 

48 + "^" * num_chars 

49 ) 

50 return context 

51 

52 def __repr__(self): 

53 return f"{self.ERROR_TYPE}: {self.message}" 

54 

55 

56class FortranError(FortranMsgBase): 

57 """Fatal static analysis problem in code""" 

58 

59 ERROR_TYPE: ClassVar[str] = "Error" 

60 ERROR_STYLE: ClassVar[str] = "red" 

61 ERROR_SEVERITY: ClassVar[int] = 2 

62 

63 

64class FortranWarning(FortranMsgBase): 

65 """Warning message from static analysis""" 

66 

67 ERROR_TYPE: ClassVar[str] = "Warning" 

68 ERROR_STYLE: ClassVar[str] = "yellow" 

69 ERROR_SEVERITY: ClassVar[int] = 1 

70 

71 

72class FortranInfo(FortranMsgBase): 

73 """Warning message from static analysis""" 

74 

75 ERROR_TYPE: ClassVar[str] = "Info" 

76 ERROR_STYLE: ClassVar[str] = "Blue" 

77 ERROR_SEVERITY: ClassVar[int] = 0 

78 

79 

80def new_fortran_error(level: str, node: FortranNode, message: str) -> FortranMsgBase: 

81 """Generate a new fortran diagnostic message""" 

82 cls = FortranMsgBase 

83 if level == "Error": 

84 cls = FortranError 

85 elif level == "Warning": 

86 cls = FortranWarning 

87 elif level == "Info": 87 ↛ 90line 87 didn't jump to line 90, because the condition on line 87 was never false

88 cls = FortranInfo 

89 else: 

90 raise ValueError("Unknown fortran diagnostic message type: " + level) 

91 return cls(node, message) 

92 

93 

94FORTRAN_ERRORS = { 

95 "Error": FortranError, 

96 "Warn": FortranWarning, 

97 "Info": FortranInfo, 

98} 

99 

100ERROR_SEVERITY = {k: v.ERROR_SEVERITY for k, v in FORTRAN_ERRORS.items()}