--- a/src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Complexity/ComplexityChecker.py Thu Feb 27 09:22:15 2025 +0100 +++ b/src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Complexity/ComplexityChecker.py Thu Feb 27 14:42:39 2025 +0100 @@ -8,12 +8,13 @@ """ import ast -import copy + +from CodeStyleTopicChecker import CodeStyleTopicChecker from .mccabe import PathGraphingAstVisitor -class ComplexityChecker: +class ComplexityChecker(CodeStyleTopicChecker): """ Class implementing a checker for code complexity. """ @@ -23,6 +24,7 @@ "C-111", "C-112", ] + Category = "C" def __init__(self, source, filename, tree, select, ignore, args): """ @@ -41,12 +43,17 @@ @param args dictionary of arguments for the miscellaneous checks @type dict """ - self.__filename = filename - self.__source = source[:] - self.__tree = copy.deepcopy(tree) - self.__select = tuple(select) - self.__ignore = tuple(ignore) - self.__args = args + super().__init__( + ComplexityChecker.Category, + source, + filename, + tree, + select, + ignore, + [], + True, + args, + ) self.__defaultArgs = { "McCabeComplexity": 10, @@ -54,82 +61,11 @@ "LineComplexityScore": 10, } - # statistics counters - self.counters = {} - - # collection of detected errors - self.errors = [] - checkersWithCodes = [ (self.__checkMcCabeComplexity, ("C-101",)), (self.__checkLineComplexity, ("C-111", "C-112")), ] - - self.__checkers = [] - for checker, codes in checkersWithCodes: - if any(not (code and self.__ignoreCode(code)) for code in codes): - self.__checkers.append(checker) - - def __ignoreCode(self, code): - """ - Private method to check if the message code should be ignored. - - @param code message code to check for - @type str - @return flag indicating to ignore the given code - @rtype bool - """ - return code in self.__ignore or ( - code.startswith(self.__ignore) and not code.startswith(self.__select) - ) - - def __error(self, lineNumber, offset, code, *args): - """ - Private method to record an issue. - - @param lineNumber line number of the issue - @type int - @param offset position within line of the issue - @type int - @param code message code - @type str - @param args arguments for the message - @type list - """ - if self.__ignoreCode(code): - return - - if code in self.counters: - self.counters[code] += 1 - else: - self.counters[code] = 1 - - if code: - # record the issue with one based line number - self.errors.append( - { - "file": self.__filename, - "line": lineNumber, - "offset": offset, - "code": code, - "args": args, - } - ) - - def run(self): - """ - Public method to check the given source for code complexity. - """ - if not self.__filename or not self.__source: - # don't do anything, if essential data is missing - return - - if not self.__checkers: - # don't do anything, if no codes were selected - return - - for check in self.__checkers: - check() + self._initializeCheckers(checkersWithCodes) def __checkMcCabeComplexity(self): """ @@ -138,13 +74,13 @@ try: # create the AST again because it is modified by the checker tree = compile( - "".join(self.__source), self.__filename, "exec", ast.PyCF_ONLY_AST + "".join(self.source), self.filename, "exec", ast.PyCF_ONLY_AST ) except (SyntaxError, TypeError): # compile errors are already reported by the run() method return - maxComplexity = self.__args.get( + maxComplexity = self.args.get( "McCabeComplexity", self.__defaultArgs["McCabeComplexity"] ) @@ -152,7 +88,9 @@ visitor.preorder(tree, visitor) for graph in visitor.graphs.values(): if graph.complexity() > maxComplexity: - self.__error(graph.lineno, 0, "C-101", graph.entity, graph.complexity()) + self.addError( + graph.lineno + 1, 0, "C-101", graph.entity, graph.complexity() + ) def __checkLineComplexity(self): """ @@ -162,25 +100,25 @@ Complexity is defined as the number of AST nodes produced by a line of code. """ - maxLineComplexity = self.__args.get( + maxLineComplexity = self.args.get( "LineComplexity", self.__defaultArgs["LineComplexity"] ) - maxLineComplexityScore = self.__args.get( + maxLineComplexityScore = self.args.get( "LineComplexityScore", self.__defaultArgs["LineComplexityScore"] ) visitor = LineComplexityVisitor() - visitor.visit(self.__tree) + visitor.visit(self.tree) sortedItems = visitor.sortedList() score = visitor.score() for line, complexity in sortedItems: if complexity > maxLineComplexity: - self.__error(line, 0, "C-111", complexity) + self.addError(line + 1, 0, "C-111", complexity) if score > maxLineComplexityScore: - self.__error(0, 0, "C-112", score) + self.addError(1, 0, "C-112", score) class LineComplexityVisitor(ast.NodeVisitor): @@ -206,6 +144,7 @@ """ if hasattr(node, "lineno"): self.__count[node.lineno] = self.__count.get(node.lineno, 0) + 1 + self.generic_visit(node) def sortedList(self):