src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Miscellaneous/DefaultMatchCaseVisitor.py

branch
eric7
changeset 11150
73d80859079c
equal deleted inserted replaced
11149:fc45672fae42 11150:73d80859079c
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2025 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing a node visitor to check default match cases.
8 """
9
10 import ast
11
12
13 class DefaultMatchCaseVisitor(ast.NodeVisitor):
14 """
15 Class implementing a node visitor to check default match cases.
16
17 Note: This class is modeled after flake8-spm v0.0.1.
18 """
19
20 def __init__(self):
21 """
22 Constructor
23 """
24 super().__init__()
25
26 self.violations = []
27
28 def visit_Match(self, node):
29 """
30 Public method to handle Match nodes.
31
32 @param node reference to the node to be processed
33 @type ast.Match
34 """
35 for badNode, issueCode in self.__badNodes(node):
36 self.violations.append((badNode, issueCode))
37
38 self.generic_visit(node)
39
40 def __badNodes(self, node):
41 """
42 Private method to yield bad match nodes.
43
44 @param node reference to the node to be processed
45 @type ast.Match
46 @yield tuple containing a reference to bad match case node and the corresponding
47 issue code
48 @ytype tyuple of (ast.AST, str)
49 """
50 for case in node.cases:
51 if self.__emptyMatchDefault(case):
52 if self.__lastStatementDoesNotRaise(case):
53 yield self.__findBadNode(case), "M-901"
54 elif self.__returnPrecedesExceptionRaising(case):
55 yield self.__findBadNode(case), "M-902"
56
57 def __emptyMatchDefault(self, case):
58 """
59 Private method to check for an empty default match case.
60
61 @param case reference to the node to be processed
62 @type ast.match_case
63 @return flag indicating an empty default match case
64 @rtype bool
65 """
66 pattern = case.pattern
67 return isinstance(pattern, ast.MatchAs) and (
68 pattern.pattern is None
69 or (
70 isinstance(pattern.pattern, ast.MatchAs)
71 and pattern.pattern.pattern is None
72 )
73 )
74
75 def __lastStatementDoesNotRaise(self, case):
76 """
77 Private method to check that the last case statement does not raise an
78 exception.
79
80 @param case reference to the node to be processed
81 @type ast.match_case
82 @return flag indicating that the last case statement does not raise an
83 exception
84 @rtype bool
85 """
86 return not isinstance(case.body[-1], ast.Raise)
87
88 def __returnPrecedesExceptionRaising(self, case):
89 """
90 Private method to check that no return precedes an exception raising.
91
92 @param case reference to the node to be processed
93 @type ast.match_case
94 @return flag indicating that a return precedes an exception raising
95 @rtype bool
96 """
97 returnIndex = -1
98 raiseIndex = -1
99 for index, body in enumerate(case.body):
100 if isinstance(body, ast.Return):
101 returnIndex = index
102 elif isinstance(body, ast.Raise):
103 raiseIndex = index
104 return returnIndex >= 0 and returnIndex < raiseIndex
105
106 def __findBadNode(self, case) -> ast.AST:
107 """
108 Private method returning a reference to the bad node of a case node.
109
110 @param case reference to the node to be processed
111 @type ast.match_case
112 @return reference to the bad node
113 @rtype ast.AST
114 """
115 for body in case.body:
116 # Handle special case when return precedes exception raising.
117 # In this case the bad node is that with the return statement.
118 if isinstance(body, ast.Return):
119 return body
120
121 return case.body[-1]

eric ide

mercurial