src/eric7/Plugins/CheckerPlugins/CodeStyleChecker/Unused/UnusedChecker.py

branch
eric7
changeset 11150
73d80859079c
parent 11147
dee6e106b4d3
--- 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):

eric ide

mercurial