Plugins/CheckerPlugins/SyntaxChecker/pyflakes/checker.py

branch
BgService
changeset 3159
02cb2adb4868
parent 2571
e6bb19eb87ea
child 3177
5af61402d74d
equal deleted inserted replaced
3146:14721e0f3b5b 3159:02cb2adb4868
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2010 - 2013 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5 # Original (c) 2005-2010 Divmod, Inc.
6 #
7 # This module is based on pyflakes for Python2 but was modified to
8 # work with eric5
9
10 import os.path
11 try:
12 import builtins
13 PY2 = False
14 except ImportError:
15 import __builtin__ as builtins #__IGNORE_WARNING__
16 PY2 = True
17
18 try:
19 import ast
20 iter_child_nodes = ast.iter_child_nodes
21 except (ImportError, AttributeError): # Python 2.5
22 import _ast as ast
23
24 def iter_child_nodes(node, astcls=ast.AST):
25 """
26 Yield all direct child nodes of *node*, that is, all fields that are nodes
27 and all items of fields that are lists of nodes.
28 """
29 for name in node._fields:
30 field = getattr(node, name, None)
31 if isinstance(field, astcls):
32 yield field
33 elif isinstance(field, list):
34 for item in field:
35 yield item
36 # Python >= 3.3 uses ast.Try instead of (ast.TryExcept + ast.TryFinally)
37 if hasattr(ast, 'Try'):
38 ast_TryExcept = ast.Try
39 ast_TryFinally = ()
40 else:
41 ast_TryExcept = ast.TryExcept
42 ast_TryFinally = ast.TryFinally
43
44 from . import messages
45
46
47 class Binding(object):
48 """
49 Represents the binding of a value to a name.
50
51 The checker uses this to keep track of which names have been bound and
52 which names have not. See L{Assignment} for a special type of binding that
53 is checked with stricter rules.
54 """
55
56 def __init__(self, name, source):
57 self.name = name
58 self.source = source
59 self.used = False
60
61 def __str__(self):
62 return self.name
63
64 def __repr__(self):
65 return '<%s object %r from line %r at 0x%x>' % (self.__class__.__name__,
66 self.name,
67 self.source.lineno,
68 id(self))
69
70
71 class UnBinding(Binding):
72 """Created by the 'del' operator."""
73
74
75 class Importation(Binding):
76 """
77 A binding created by an import statement.
78 """
79 def __init__(self, name, source):
80 self.fullName = name
81 name = name.split('.')[0]
82 super(Importation, self).__init__(name, source)
83
84
85 class Argument(Binding):
86 """
87 Represents binding a name as an argument.
88 """
89
90
91 class Definition(Binding):
92 """
93 A binding that defines a function or a class.
94 """
95
96
97 class Assignment(Binding):
98 """
99 Represents binding a name with an explicit assignment.
100
101 The checker will raise warnings for any Assignment that isn't used. Also,
102 the checker does not consider assignments in tuple/list unpacking to be
103 Assignments, rather it treats them as simple Bindings.
104 """
105
106
107 class FunctionDefinition(Definition):
108 pass
109
110
111 class ClassDefinition(Definition):
112 pass
113
114
115 class ExportBinding(Binding):
116 """
117 A binding created by an C{__all__} assignment. If the names in the list
118 can be determined statically, they will be treated as names for export and
119 additional checking applied to them.
120
121 The only C{__all__} assignment that can be recognized is one which takes
122 the value of a literal list containing literal strings. For example::
123
124 __all__ = ["foo", "bar"]
125
126 Names which are imported and not otherwise used but appear in the value of
127 C{__all__} will not have an unused import warning reported for them.
128 """
129 def names(self):
130 """
131 Return a list of the names referenced by this binding.
132 """
133 names = []
134 if isinstance(self.source, ast.List):
135 for node in self.source.elts:
136 if isinstance(node, ast.Str):
137 names.append(node.s)
138 return names
139
140
141 class Scope(dict):
142 importStarred = False # set to True when import * is found
143 usesLocals = False
144
145 def __repr__(self):
146 return '<%s at 0x%x %s>' % (self.__class__.__name__, id(self), dict.__repr__(self))
147
148
149 class ClassScope(Scope):
150 pass
151
152
153 class FunctionScope(Scope):
154 """
155 I represent a name scope for a function.
156 """
157 def __init__(self):
158 super(FunctionScope, self).__init__()
159 self.globals = {}
160
161
162 class ModuleScope(Scope):
163 pass
164
165
166 # Globally defined names which are not attributes of the builtins module, or
167 # are only present on some platforms.
168 _MAGIC_GLOBALS = ['__file__', '__builtins__', 'WindowsError']
169
170
171 def getNodeName(node):
172 # Returns node.id, or node.name, or None
173 if hasattr(node, 'id'): # One of the many nodes with an id
174 return node.id
175 if hasattr(node, 'name'): # a ExceptHandler node
176 return node.name
177
178
179 class Checker(object):
180 """
181 I check the cleanliness and sanity of Python code.
182 """
183
184 nodeDepth = 0
185 traceTree = False
186 builtIns = set(dir(builtins)) | set(_MAGIC_GLOBALS)
187
188 def __init__(self, tree, filename='(none)', builtins=None):
189 self._deferredFunctions = []
190 self._deferredAssignments = []
191 self.deadScopes = []
192 self.messages = []
193 self.filename = filename
194 if builtins:
195 self.builtIns = self.builtIns.union(builtins)
196 self.scopeStack = [ModuleScope()]
197 self.futuresAllowed = True
198 self.root = tree
199 self.handleChildren(tree)
200 self.runDeferred(self._deferredFunctions)
201 # Set _deferredFunctions to None so that deferFunction will fail
202 # noisily if called after we've run through the deferred functions.
203 self._deferredFunctions = None
204 self.runDeferred(self._deferredAssignments)
205 # Set _deferredAssignments to None so that deferAssignment will fail
206 # noisily if called after we've run through the deferred assignments.
207 self._deferredAssignments = None
208 del self.scopeStack[1:]
209 self.popScope()
210 self.checkDeadScopes()
211
212 def deferFunction(self, callable):
213 """
214 Schedule a function handler to be called just before completion.
215
216 This is used for handling function bodies, which must be deferred
217 because code later in the file might modify the global scope. When
218 `callable` is called, the scope at the time this is called will be
219 restored, however it will contain any new bindings added to it.
220 """
221 self._deferredFunctions.append((callable, self.scopeStack[:]))
222
223 def deferAssignment(self, callable):
224 """
225 Schedule an assignment handler to be called just after deferred
226 function handlers.
227 """
228 self._deferredAssignments.append((callable, self.scopeStack[:]))
229
230 def runDeferred(self, deferred):
231 """
232 Run the callables in C{deferred} using their associated scope stack.
233 """
234 for handler, scope in deferred:
235 self.scopeStack = scope
236 handler()
237
238 @property
239 def scope(self):
240 return self.scopeStack[-1]
241
242 def popScope(self):
243 self.deadScopes.append(self.scopeStack.pop())
244
245 def checkDeadScopes(self):
246 """
247 Look at scopes which have been fully examined and report names in them
248 which were imported but unused.
249 """
250 for scope in self.deadScopes:
251 export = isinstance(scope.get('__all__'), ExportBinding)
252 if export:
253 all = scope['__all__'].names()
254 if not scope.importStarred and os.path.basename(self.filename) != '__init__.py':
255 # Look for possible mistakes in the export list
256 undefined = set(all) - set(scope)
257 for name in undefined:
258 self.report(messages.UndefinedExport,
259 scope['__all__'].source.lineno, name)
260 else:
261 all = []
262
263 # Look for imported names that aren't used.
264 for importation in scope.values():
265 if isinstance(importation, Importation):
266 if not importation.used and importation.name not in all:
267 self.report(messages.UnusedImport,
268 importation.source.lineno, importation.name)
269
270 def pushFunctionScope(self):
271 self.scopeStack.append(FunctionScope())
272
273 def pushClassScope(self):
274 self.scopeStack.append(ClassScope())
275
276 def report(self, messageClass, *args, **kwargs):
277 self.messages.append(messageClass(self.filename, *args, **kwargs))
278
279 def hasParent(self, node, kind):
280 while hasattr(node, 'parent'):
281 node = node.parent
282 if isinstance(node, kind):
283 return True
284
285 def getCommonAncestor(self, lnode, rnode, stop=None):
286 if not stop:
287 stop = self.root
288 if lnode is rnode:
289 return lnode
290 if stop in (lnode, rnode):
291 return stop
292
293 if not hasattr(lnode, 'parent') or not hasattr(rnode, 'parent'):
294 return
295 if (lnode.level > rnode.level):
296 return self.getCommonAncestor(lnode.parent, rnode, stop)
297 if (rnode.level > lnode.level):
298 return self.getCommonAncestor(lnode, rnode.parent, stop)
299 return self.getCommonAncestor(lnode.parent, rnode.parent, stop)
300
301 def descendantOf(self, node, ancestors, stop=None):
302 for a in ancestors:
303 if self.getCommonAncestor(node, a, stop) not in (stop, None):
304 return True
305 return False
306
307 def onFork(self, parent, lnode, rnode, items):
308 return (self.descendantOf(lnode, items, parent) ^
309 self.descendantOf(rnode, items, parent))
310
311 def differentForks(self, lnode, rnode):
312 """True, if lnode and rnode are located on different forks of IF/TRY"""
313 ancestor = self.getCommonAncestor(lnode, rnode)
314 if isinstance(ancestor, ast.If):
315 for fork in (ancestor.body, ancestor.orelse):
316 if self.onFork(ancestor, lnode, rnode, fork):
317 return True
318 elif isinstance(ancestor, ast_TryExcept):
319 body = ancestor.body + ancestor.orelse
320 for fork in [body] + [[hdl] for hdl in ancestor.handlers]:
321 if self.onFork(ancestor, lnode, rnode, fork):
322 return True
323 elif isinstance(ancestor, ast_TryFinally):
324 if self.onFork(ancestor, lnode, rnode, ancestor.body):
325 return True
326 return False
327
328 def addBinding(self, node, value, reportRedef=True):
329 """
330 Called when a binding is altered.
331
332 - `node` is the statement responsible for the change
333 - `value` is the optional new value, a Binding instance, associated
334 with the binding; if None, the binding is deleted if it exists.
335 - if `reportRedef` is True (default), rebinding while unused will be
336 reported.
337 """
338 redefinedWhileUnused = False
339 if not isinstance(self.scope, ClassScope):
340 for scope in self.scopeStack[::-1]:
341 existing = scope.get(value.name)
342 if (isinstance(existing, Importation)
343 and not existing.used
344 and (not isinstance(value, Importation) or value.fullName == existing.fullName)
345 and reportRedef
346 and not self.differentForks(node, existing.source)):
347 redefinedWhileUnused = True
348 self.report(messages.RedefinedWhileUnused,
349 node.lineno, value.name, existing.source.lineno)
350
351 existing = self.scope.get(value.name)
352 if not redefinedWhileUnused and self.hasParent(value.source, ast.ListComp):
353 if (existing and reportRedef
354 and not self.hasParent(existing.source, (ast.For, ast.ListComp))):
355 self.report(messages.RedefinedInListComp,
356 node.lineno, value.name, existing.source.lineno)
357
358 if isinstance(value, UnBinding):
359 try:
360 del self.scope[value.name]
361 except KeyError:
362 self.report(messages.UndefinedName, node.lineno, value.name)
363 elif (isinstance(existing, Definition)
364 and not existing.used
365 and not self.differentForks(node, existing.source)):
366 self.report(messages.RedefinedWhileUnused,
367 node.lineno, value.name, existing.source.lineno)
368 else:
369 self.scope[value.name] = value
370
371 def handleNodeLoad(self, node):
372 name = getNodeName(node)
373 if not name:
374 return
375 # try local scope
376 importStarred = self.scope.importStarred
377 try:
378 self.scope[name].used = (self.scope, node.lineno)
379 except KeyError:
380 pass
381 else:
382 return
383
384 # try enclosing function scopes
385 for scope in self.scopeStack[-2:0:-1]:
386 importStarred = importStarred or scope.importStarred
387 if not isinstance(scope, FunctionScope):
388 continue
389 try:
390 scope[name].used = (self.scope, node.lineno)
391 except KeyError:
392 pass
393 else:
394 return
395
396 # try global scope
397 importStarred = importStarred or self.scopeStack[0].importStarred
398 try:
399 self.scopeStack[0][name].used = (self.scope, node.lineno)
400 except KeyError:
401 if not importStarred and name not in self.builtIns:
402 if (os.path.basename(self.filename) == '__init__.py' and name == '__path__'):
403 # the special name __path__ is valid only in packages
404 pass
405 else:
406 self.report(messages.UndefinedName, node.lineno, name)
407
408 def handleNodeStore(self, node):
409 name = getNodeName(node)
410 if not name:
411 return
412 # if the name hasn't already been defined in the current scope
413 if isinstance(self.scope, FunctionScope) and name not in self.scope:
414 # for each function or module scope above us
415 for scope in self.scopeStack[:-1]:
416 if not isinstance(scope, (FunctionScope, ModuleScope)):
417 continue
418 # if the name was defined in that scope, and the name has
419 # been accessed already in the current scope, and hasn't
420 # been declared global
421 if (name in scope and scope[name].used and scope[name].used[0] is self.scope
422 and name not in self.scope.globals):
423 # then it's probably a mistake
424 self.report(messages.UndefinedLocal,
425 scope[name].used[1], name, scope[name].source.lineno)
426 break
427
428 parent = getattr(node, 'parent', None)
429 if isinstance(parent, (ast.For, ast.comprehension, ast.Tuple, ast.List)):
430 binding = Binding(name, node)
431 elif parent is not None and name == '__all__' and isinstance(self.scope, ModuleScope):
432 binding = ExportBinding(name, parent.value)
433 else:
434 binding = Assignment(name, node)
435 if name in self.scope:
436 binding.used = self.scope[name].used
437 self.addBinding(node, binding)
438
439 def handleNodeDelete(self, node):
440 name = getNodeName(node)
441 if not name:
442 return
443 if isinstance(self.scope, FunctionScope) and name in self.scope.globals:
444 del self.scope.globals[name]
445 else:
446 self.addBinding(node, UnBinding(name, node))
447
448 def handleChildren(self, tree):
449 for node in iter_child_nodes(tree):
450 self.handleNode(node, tree)
451
452 def isDocstring(self, node):
453 """
454 Determine if the given node is a docstring, as long as it is at the
455 correct place in the node tree.
456 """
457 return isinstance(node, ast.Str) or (isinstance(node, ast.Expr) and
458 isinstance(node.value, ast.Str))
459
460 def handleNode(self, node, parent):
461 if node is None:
462 return
463 node.parent = parent
464 if self.traceTree:
465 print(' ' * self.nodeDepth + node.__class__.__name__)
466 self.nodeDepth += 1
467 if self.futuresAllowed and not (isinstance(node, ast.ImportFrom) or
468 self.isDocstring(node)):
469 self.futuresAllowed = False
470 nodeType = node.__class__.__name__.upper()
471 node.level = self.nodeDepth
472 try:
473 handler = getattr(self, nodeType)
474 handler(node)
475 finally:
476 self.nodeDepth -= 1
477 if self.traceTree:
478 print(' ' * self.nodeDepth + 'end ' + node.__class__.__name__)
479
480 def ignore(self, node):
481 pass
482
483 # "stmt" type nodes
484 RETURN = DELETE = PRINT = WHILE = IF = WITH = WITHITEM = RAISE = \
485 TRYEXCEPT = TRYFINALLY = TRY = ASSERT = EXEC = EXPR = handleChildren
486
487 CONTINUE = BREAK = PASS = ignore
488
489 # "expr" type nodes
490 BOOLOP = BINOP = UNARYOP = IFEXP = DICT = SET = YIELD = YIELDFROM = \
491 COMPARE = CALL = REPR = ATTRIBUTE = SUBSCRIPT = LIST = TUPLE = \
492 STARRED = handleChildren
493
494 NUM = STR = BYTES = ELLIPSIS = ignore
495
496 # "slice" type nodes
497 SLICE = EXTSLICE = INDEX = handleChildren
498
499 # expression contexts are node instances too, though being constants
500 LOAD = STORE = DEL = AUGLOAD = AUGSTORE = PARAM = ignore
501
502 # same for operators
503 AND = OR = ADD = SUB = MULT = DIV = MOD = POW = LSHIFT = RSHIFT = \
504 BITOR = BITXOR = BITAND = FLOORDIV = INVERT = NOT = UADD = USUB = \
505 EQ = NOTEQ = LT = LTE = GT = GTE = IS = ISNOT = IN = NOTIN = ignore
506
507 # additional node types
508 COMPREHENSION = KEYWORD = handleChildren
509
510 def GLOBAL(self, node):
511 """
512 Keep track of globals declarations.
513 """
514 if isinstance(self.scope, FunctionScope):
515 self.scope.globals.update(dict.fromkeys(node.names))
516
517 NONLOCAL = GLOBAL
518
519 def LISTCOMP(self, node):
520 # handle generators before element
521 for gen in node.generators:
522 self.handleNode(gen, node)
523 self.handleNode(node.elt, node)
524
525 GENERATOREXP = SETCOMP = LISTCOMP
526
527 def DICTCOMP(self, node):
528 for gen in node.generators:
529 self.handleNode(gen, node)
530 self.handleNode(node.key, node)
531 self.handleNode(node.value, node)
532
533 def FOR(self, node):
534 """
535 Process bindings for loop variables.
536 """
537 vars = []
538
539 def collectLoopVars(n):
540 if isinstance(n, ast.Name):
541 vars.append(n.id)
542 elif isinstance(n, ast.expr_context):
543 return
544 else:
545 for c in iter_child_nodes(n):
546 collectLoopVars(c)
547
548 collectLoopVars(node.target)
549 for varn in vars:
550 if (isinstance(self.scope.get(varn), Importation)
551 # unused ones will get an unused import warning
552 and self.scope[varn].used):
553 self.report(messages.ImportShadowedByLoopVar,
554 node.lineno, varn, self.scope[varn].source.lineno)
555
556 self.handleChildren(node)
557
558 def NAME(self, node):
559 """
560 Handle occurrence of Name (which can be a load/store/delete access.)
561 """
562 if node.id == 'locals' and isinstance(node.parent, ast.Call):
563 # we are doing locals() call in current scope
564 self.scope.usesLocals = True
565 # Locate the name in locals / function / globals scopes.
566 if isinstance(node.ctx, (ast.Load, ast.AugLoad)):
567 self.handleNodeLoad(node)
568 elif isinstance(node.ctx, (ast.Store, ast.AugStore)):
569 self.handleNodeStore(node)
570 elif isinstance(node.ctx, ast.Del):
571 self.handleNodeDelete(node)
572 else:
573 # must be a Param context -- this only happens for names in function
574 # arguments, but these aren't dispatched through here
575 raise RuntimeError("Got impossible expression context: %r" % (node.ctx,))
576
577 def FUNCTIONDEF(self, node):
578 if not hasattr(node, 'decorator_list'): # Python 2.5
579 node.decorator_list = node.decorators
580 for deco in node.decorator_list:
581 self.handleNode(deco, node)
582 self.addBinding(node, FunctionDefinition(node.name, node))
583 self.LAMBDA(node)
584
585 def LAMBDA(self, node):
586 args = []
587
588 if PY2:
589 def addArgs(arglist):
590 for arg in arglist:
591 if isinstance(arg, ast.Tuple):
592 addArgs(arg.elts)
593 else:
594 if arg.id in args:
595 self.report(messages.DuplicateArgument,
596 node.lineno, arg.id)
597 args.append(arg.id)
598 addArgs(node.args.args)
599 defaults = node.args.defaults
600 else:
601 for arg in node.args.args + node.args.kwonlyargs:
602 if arg.arg in args:
603 self.report(messages.DuplicateArgument,
604 node.lineno, arg.arg)
605 args.append(arg.arg)
606 self.handleNode(arg.annotation, node)
607 if hasattr(node, 'returns'): # Only for FunctionDefs
608 for annotation in (node.args.varargannotation,
609 node.args.kwargannotation, node.returns):
610 self.handleNode(annotation, node)
611 defaults = node.args.defaults + node.args.kw_defaults
612
613 # vararg/kwarg identifiers are not Name nodes
614 for wildcard in (node.args.vararg, node.args.kwarg):
615 if not wildcard:
616 continue
617 if wildcard in args:
618 self.report(messages.DuplicateArgument, node.lineno, wildcard)
619 args.append(wildcard)
620 for default in defaults:
621 self.handleNode(default, node)
622
623 def runFunction():
624
625 self.pushFunctionScope()
626 for name in args:
627 self.addBinding(node, Argument(name, node), reportRedef=False)
628 if isinstance(node.body, list):
629 # case for FunctionDefs
630 for stmt in node.body:
631 self.handleNode(stmt, node)
632 else:
633 # case for Lambdas
634 self.handleNode(node.body, node)
635
636 def checkUnusedAssignments():
637 """
638 Check to see if any assignments have not been used.
639 """
640 for name, binding in self.scope.items():
641 if (not binding.used and name not in self.scope.globals
642 and not self.scope.usesLocals
643 and isinstance(binding, Assignment)):
644 self.report(messages.UnusedVariable,
645 binding.source.lineno, name)
646 self.deferAssignment(checkUnusedAssignments)
647 self.popScope()
648
649 self.deferFunction(runFunction)
650
651 def CLASSDEF(self, node):
652 """
653 Check names used in a class definition, including its decorators, base
654 classes, and the body of its definition. Additionally, add its name to
655 the current scope.
656 """
657 # no class decorator in Python 2.5
658 for deco in getattr(node, 'decorator_list', ''):
659 self.handleNode(deco, node)
660 for baseNode in node.bases:
661 self.handleNode(baseNode, node)
662 if not PY2:
663 for keywordNode in node.keywords:
664 self.handleNode(keywordNode, node)
665 self.pushClassScope()
666 for stmt in node.body:
667 self.handleNode(stmt, node)
668 self.popScope()
669 self.addBinding(node, ClassDefinition(node.name, node))
670
671 def ASSIGN(self, node):
672 self.handleNode(node.value, node)
673 for target in node.targets:
674 self.handleNode(target, node)
675
676 def AUGASSIGN(self, node):
677 self.handleNodeLoad(node.target)
678 self.handleNode(node.value, node)
679 self.handleNode(node.target, node)
680
681 def IMPORT(self, node):
682 for alias in node.names:
683 name = alias.asname or alias.name
684 importation = Importation(name, node)
685 self.addBinding(node, importation)
686
687 def IMPORTFROM(self, node):
688 if node.module == '__future__':
689 if not self.futuresAllowed:
690 self.report(messages.LateFutureImport,
691 node.lineno, [n.name for n in node.names])
692 else:
693 self.futuresAllowed = False
694
695 for alias in node.names:
696 if alias.name == '*':
697 self.scope.importStarred = True
698 self.report(messages.ImportStarUsed, node.lineno, node.module)
699 continue
700 name = alias.asname or alias.name
701 importation = Importation(name, node)
702 if node.module == '__future__':
703 importation.used = (self.scope, node.lineno)
704 self.addBinding(node, importation)
705
706 def EXCEPTHANDLER(self, node):
707 # 3.x: in addition to handling children, we must handle the name of
708 # the exception, which is not a Name node, but a simple string.
709 if isinstance(node.name, str):
710 self.handleNodeStore(node)
711 self.handleChildren(node)

eric ide

mercurial