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
« 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
4from castep_linter.fortran.fortran_node import FortranNode
7class FortranMsgBase:
8 """Base class for other static analysis problems to inherit from"""
10 ERROR_TYPE: ClassVar[str] = "NONE"
11 ERROR_STYLE: ClassVar[str] = "grey"
12 LINE_NUMBER_OFFSET = 8
13 ERROR_SEVERITY: ClassVar[int] = 100
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
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)
27 def context(self, filename, *, underline=False):
28 """Print a line of context for the current error"""
29 context = ""
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
37 file_str = str(filename)
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
52 def __repr__(self):
53 return f"{self.ERROR_TYPE}: {self.message}"
56class FortranError(FortranMsgBase):
57 """Fatal static analysis problem in code"""
59 ERROR_TYPE: ClassVar[str] = "Error"
60 ERROR_STYLE: ClassVar[str] = "red"
61 ERROR_SEVERITY: ClassVar[int] = 2
64class FortranWarning(FortranMsgBase):
65 """Warning message from static analysis"""
67 ERROR_TYPE: ClassVar[str] = "Warning"
68 ERROR_STYLE: ClassVar[str] = "yellow"
69 ERROR_SEVERITY: ClassVar[int] = 1
72class FortranInfo(FortranMsgBase):
73 """Warning message from static analysis"""
75 ERROR_TYPE: ClassVar[str] = "Info"
76 ERROR_STYLE: ClassVar[str] = "Blue"
77 ERROR_SEVERITY: ClassVar[int] = 0
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)
94FORTRAN_ERRORS = {
95 "Error": FortranError,
96 "Warn": FortranWarning,
97 "Info": FortranInfo,
98}
100ERROR_SEVERITY = {k: v.ERROR_SEVERITY for k, v in FORTRAN_ERRORS.items()}