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

branch
eric7
changeset 10053
9914b7b4b11c
parent 10052
041d0785dd42
child 10054
d7a47f0cff2b
equal deleted inserted replaced
10052:041d0785dd42 10053:9914b7b4b11c
6 """ 6 """
7 Module implementing a checker for unused arguments, variables, ... . 7 Module implementing a checker for unused arguments, variables, ... .
8 """ 8 """
9 9
10 import ast 10 import ast
11 import collections
11 import copy 12 import copy
12 13
13 import AstUtilities 14 import AstUtilities
14 15
15 16
20 21
21 Codes = [ 22 Codes = [
22 ## Unused Arguments 23 ## Unused Arguments
23 "U100", 24 "U100",
24 "U101", 25 "U101",
26 ## Unused Globals
27 "U200",
25 ] 28 ]
26 29
27 def __init__(self, source, filename, tree, select, ignore, expected, repeat, args): 30 def __init__(self, source, filename, tree, select, ignore, expected, repeat, args):
28 """ 31 """
29 Constructor 32 Constructor
52 self.__filename = filename 55 self.__filename = filename
53 self.__source = source[:] 56 self.__source = source[:]
54 self.__tree = copy.deepcopy(tree) 57 self.__tree = copy.deepcopy(tree)
55 self.__args = args 58 self.__args = args
56 59
57 ### parameters for unused arguments checks
58 ##self.__ignoreAbstract "IgnoreAbstract": False,
59 ##self.__ignoreOverload "IgnoreOverload": False,
60 ##self.__ignoreOverride "IgnoreOverride": False,
61 ##self.__ignoreStubs "IgnoreStubs": False,
62 ##self.__ignoreVariadicNames "IgnoreVariadicNames": False,
63 ##self.__ignoreLambdas "IgnoreLambdas": False,
64 ##self.__ignoreNestedFunctions "IgnoreNestedFunctions": False,
65 ##self.__ignoreDunderMethods "IgnoreDunderMethods": False,
66
67 # statistics counters 60 # statistics counters
68 self.counters = {} 61 self.counters = {}
69 62
70 # collection of detected errors 63 # collection of detected errors
71 self.errors = [] 64 self.errors = []
72 65
73 checkersWithCodes = [ 66 checkersWithCodes = [
74 (self.__checkUnusedArguments, ("U100", "U101")), 67 (self.__checkUnusedArguments, ("U100", "U101")),
68 (self.__checkUnusedGlobals, ("U200", )),
75 ] 69 ]
76 70
77 self.__checkers = [] 71 self.__checkers = []
78 for checker, codes in checkersWithCodes: 72 for checker, codes in checkersWithCodes:
79 if any(not (code and self.__ignoreCode(code)) for code in codes): 73 if any(not (code and self.__ignoreCode(code)) for code in codes):
362 if args.kwarg is not None: 356 if args.kwarg is not None:
363 orderedArguments.append(args.kwarg) 357 orderedArguments.append(args.kwarg)
364 358
365 return orderedArguments 359 return orderedArguments
366 360
361 #######################################################################
362 ## Unused Arguments
363 ##
364 ## adapted from: flake8-unused-arguments v0.0.13
365 #######################################################################
366
367 def __checkUnusedGlobals(self):
368 """
369 Private method to check for unused global variables.
370 """
371 errors = {}
372 loadCounter = GlobalVariableLoadCounter()
373 loadCounter.visit(self.__tree)
374
375 globalVariables = self.__extractGlobalVariables()
376
377 for varId, loads in loadCounter.getLoads():
378 if varId in globalVariables and loads == 0:
379 storeInfo = loadCounter.getStoreInfo(varId)
380 errorInfo = (storeInfo.lineno - 1, storeInfo.offset, "U200", varId)
381 errors[varId] = errorInfo
382
383 for node in self.__tree.body[::-1]:
384 if isinstance(node, ast.Assign):
385 for target in node.targets:
386 if isinstance(target, ast.Name) and target.id in errors:
387 errors.pop(target.id)
388 elif (
389 isinstance(node, ast.AnnAssign)
390 and isinstance(node.target, ast.Name)
391 and node.target.id in errors
392 ):
393 errors.pop(node.target.id)
394 else:
395 break
396
397 if self.__args["IgnoreDunderGlobals"]:
398 # eliminate some special cases
399 for name in list(errors.keys()):
400 if name.startswith("__") and name.endswith("__"):
401 errors.pop(name)
402
403 for varId in errors:
404 self.__error(*errors[varId])
405
406 def __extractGlobalVariables(self):
407 """
408 Private method to get the names of all global variables.
409
410 @return set containing the defined global variable names
411 @rtype set of str
412 """
413 variables = set()
414
415 for assignment in self.__tree.body:
416 if isinstance(assignment, ast.Assign):
417 for target in assignment.targets:
418 if isinstance(target, ast.Name):
419 variables.add(target.id)
420 elif (
421 isinstance(assignment, ast.AnnAssign)
422 and isinstance(assignment.target, ast.Name)
423 ):
424 variables.add(assignment.target.id)
425
426 return variables
427
428 #######################################################################
429 ## Class used by 'Unused Arguments'
430 ##
431 ## adapted from: flake8-unused-arguments v0.0.13
432 #######################################################################
433
367 434
368 class FunctionFinder(ast.NodeVisitor): 435 class FunctionFinder(ast.NodeVisitor):
369 """ 436 """
370 Class to find all defined functions and methods. 437 Class to find all defined functions and methods.
371 """ 438 """
406 else: 473 else:
407 for obj in functionNode.body: 474 for obj in functionNode.body:
408 self.visit(obj) 475 self.visit(obj)
409 476
410 visit_AsyncFunctionDef = visit_FunctionDef = visit_Lambda = __visitFunctionTypes 477 visit_AsyncFunctionDef = visit_FunctionDef = visit_Lambda = __visitFunctionTypes
478
479 #######################################################################
480 ## Class used by 'Unused Globals'
481 ##
482 ## adapted from: flake8-unused-globals v0.1.9
483 #######################################################################
484
485
486 GlobalVariableStoreInfo = collections.namedtuple(
487 "GlobalVariableStoreInfo", ["lineno", "offset"]
488 )
489
490
491 class GlobalVariableLoadCounter(ast.NodeVisitor):
492 """
493 Class to find all defined global variables and count their usages.
494 """
495
496 def __init__(self):
497 """
498 Constructor
499 """
500 super().__init__()
501
502 self.__loads = {}
503 self.__storeInfo = {}
504
505 def visit_Name(self, nameNode):
506 """
507 Public method to record the definition and use of a global variable.
508
509 @param nameNode reference to the name node to be processed
510 @type ast.Name
511 """
512 if isinstance(nameNode.ctx, ast.Load) and nameNode.id in self.__loads:
513 self.__loads[nameNode.id] += 1
514 elif (
515 isinstance(nameNode.ctx, ast.Store)
516 and nameNode.id not in self.__storeInfo
517 ):
518 self.__loads[nameNode.id] = 0
519 self.__storeInfo[nameNode.id] = GlobalVariableStoreInfo(
520 lineno=nameNode.lineno, offset=nameNode.col_offset
521 )
522
523 def getLoads(self):
524 """
525 Public method to get an iterator of the detected variable loads.
526
527 @return DESCRIPTION
528 @rtype TYPE
529 """
530 return self.__loads.items()
531
532 def getStoreInfo(self, variableId):
533 """
534 Public method to get the store info data of a given variable ID.
535
536 @param variableId variable ID to retrieve the store info for
537 @type str
538 @return named tuple containing the line number and column offset
539 @rtype GlobalVariableStoreInfo
540 """
541 try:
542 return self.__storeInfo[variableId]
543 except KeyError:
544 return None

eric ide

mercurial