diff -r fc45672fae42 -r 73d80859079c src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Unused/UnusedChecker.py --- a/src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Unused/UnusedChecker.py Thu Feb 27 09:22:15 2025 +0100 +++ b/src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Unused/UnusedChecker.py Thu Feb 27 14:42:39 2025 +0100 @@ -9,12 +9,13 @@ import ast import collections -import copy import AstUtilities +from CodeStyleTopicChecker import CodeStyleTopicChecker -class UnusedChecker: + +class UnusedChecker(CodeStyleTopicChecker): """ Class implementing a checker for unused arguments, variables, ... . """ @@ -26,6 +27,7 @@ ## Unused Globals "U-200", ] + Category = "U" def __init__(self, source, filename, tree, select, ignore, expected, repeat, args): """ @@ -48,96 +50,23 @@ @param args dictionary of arguments for the various checks @type dict """ - self.__select = tuple(select) - self.__ignore = tuple(ignore) - self.__expected = expected[:] - self.__repeat = repeat - self.__filename = filename - self.__source = source[:] - self.__tree = copy.deepcopy(tree) - self.__args = args - - # statistics counters - self.counters = {} - - # collection of detected errors - self.errors = [] + super().__init__( + UnusedChecker.Category, + source, + filename, + tree, + select, + ignore, + expected, + repeat, + args, + ) checkersWithCodes = [ (self.__checkUnusedArguments, ("U-100", "U-101")), (self.__checkUnusedGlobals, ("U-200",)), ] - - 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 - - # Don't care about expected codes - if code in self.__expected: - return - - if code and (self.counters[code] == 1 or self.__repeat): - # record the issue with one based line number - self.errors.append( - { - "file": self.__filename, - "line": lineNumber + 1, - "offset": offset, - "code": code, - "args": args, - } - ) - - def run(self): - """ - Public method to check the given source against miscellaneous - conditions. - """ - if not self.__filename: - # 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) ####################################################################### ## Unused Arguments @@ -149,52 +78,50 @@ """ Private method to check function and method definitions for unused arguments. """ - finder = FunctionFinder(self.__args["IgnoreNestedFunctions"]) - finder.visit(self.__tree) + finder = FunctionFinder(self.args["IgnoreNestedFunctions"]) + finder.visit(self.tree) for functionNode in finder.functionNodes(): decoratorNames = set(self.__getDecoratorNames(functionNode)) # ignore overload functions, it's not a surprise when they're empty - if self.__args["IgnoreOverload"] and "overload" in decoratorNames: + if self.args["IgnoreOverload"] and "overload" in decoratorNames: continue # ignore overridden functions - if self.__args["IgnoreOverride"] and "override" in decoratorNames: + if self.args["IgnoreOverride"] and "override" in decoratorNames: continue # ignore abstractmethods, it's not a surprise when they're empty - if self.__args["IgnoreAbstract"] and "abstractmethod" in decoratorNames: + if self.args["IgnoreAbstract"] and "abstractmethod" in decoratorNames: continue # ignore Qt slot methods - if self.__args["IgnoreSlotMethods"] and ( + if self.args["IgnoreSlotMethods"] and ( "pyqtSlot" in decoratorNames or "Slot" in decoratorNames ): continue - if self.__args["IgnoreEventHandlerMethods"] and self.__isEventHandlerMethod( + if self.args["IgnoreEventHandlerMethods"] and self.__isEventHandlerMethod( functionNode ): continue # ignore stub functions - if self.__args["IgnoreStubs"] and self.__isStubFunction(functionNode): + if self.args["IgnoreStubs"] and self.__isStubFunction(functionNode): continue # ignore lambdas - if self.__args["IgnoreLambdas"] and isinstance(functionNode, ast.Lambda): + if self.args["IgnoreLambdas"] and isinstance(functionNode, ast.Lambda): continue # ignore __double_underscore_methods__() - if self.__args["IgnoreDunderMethods"] and self.__isDunderMethod( - functionNode - ): + if self.args["IgnoreDunderMethods"] and self.__isDunderMethod(functionNode): continue for i, argument in self.__getUnusedArguments(functionNode): name = argument.arg - if self.__args["IgnoreVariadicNames"]: + if self.args["IgnoreVariadicNames"]: if ( functionNode.args.vararg and functionNode.args.vararg.arg == name @@ -213,7 +140,7 @@ offset = argument.col_offset errorCode = "U-101" if name.startswith("_") else "U-100" - self.__error(lineNumber - 1, offset, errorCode, name) + self.addError(lineNumber, offset, errorCode, name) def __getDecoratorNames(self, functionNode): """ @@ -399,17 +326,17 @@ """ errors = {} loadCounter = GlobalVariableLoadCounter() - loadCounter.visit(self.__tree) + loadCounter.visit(self.tree) globalVariables = self.__extractGlobalVariables() for varId, loads in loadCounter.getLoads(): if varId in globalVariables and loads == 0: storeInfo = loadCounter.getStoreInfo(varId) - errorInfo = (storeInfo.lineno - 1, storeInfo.offset, "U-200", varId) + errorInfo = (storeInfo.lineno, storeInfo.offset, "U-200", varId) errors[varId] = errorInfo - for node in self.__tree.body[::-1]: + for node in self.tree.body[::-1]: if isinstance(node, ast.Assign): for target in node.targets: if isinstance(target, ast.Name) and target.id in errors: @@ -423,14 +350,14 @@ else: break - if self.__args["IgnoreDunderGlobals"]: + if self.args["IgnoreDunderGlobals"]: # eliminate some special cases for name in list(errors): if name.startswith("__") and name.endswith("__"): errors.pop(name) for varId in errors: - self.__error(*errors[varId]) + self.addError(*errors[varId]) def __extractGlobalVariables(self): """ @@ -441,7 +368,7 @@ """ variables = set() - for assignment in self.__tree.body: + for assignment in self.tree.body: if isinstance(assignment, ast.Assign): for target in assignment.targets: if isinstance(target, ast.Name):