243 |
243 |
244 def handleBody(self, tree): |
244 def handleBody(self, tree): |
245 for node in tree.body: |
245 for node in tree.body: |
246 self.handleNode(node, tree) |
246 self.handleNode(node, tree) |
247 |
247 |
|
248 def handleChildren(self, tree): |
|
249 for node in ast.iter_child_nodes(tree): |
|
250 self.handleNode(node, tree) |
|
251 |
|
252 def isDocstring(self, node): |
|
253 """ |
|
254 Determine if the given node is a docstring, as long as it is at the |
|
255 correct place in the node tree. |
|
256 """ |
|
257 return isinstance(node, ast.Str) or \ |
|
258 (isinstance(node, ast.Expr) and |
|
259 isinstance(node.value, ast.Str)) |
|
260 |
248 def handleNode(self, node, parent): |
261 def handleNode(self, node, parent): |
249 if node: |
262 if node: |
250 node.parent = parent |
263 node.parent = parent |
251 if self.traceTree: |
264 if self.traceTree: |
252 print(' ' * self.nodeDepth + node.__class__.__name__) |
265 print(' ' * self.nodeDepth + node.__class__.__name__) |
253 self.nodeDepth += 1 |
266 self.nodeDepth += 1 |
|
267 if self.futuresAllowed and not \ |
|
268 (isinstance(node, ast.ImportFrom) or self.isDocstring(node)): |
|
269 self.futuresAllowed = False |
254 nodeType = node.__class__.__name__.upper() |
270 nodeType = node.__class__.__name__.upper() |
255 try: |
271 try: |
256 handler = getattr(self, nodeType) |
272 handler = getattr(self, nodeType) |
257 handler(node) |
273 handler(node) |
258 finally: |
274 finally: |
263 def ignore(self, node): |
279 def ignore(self, node): |
264 pass |
280 pass |
265 |
281 |
266 # ast nodes to be ignored |
282 # ast nodes to be ignored |
267 PASS = CONTINUE = BREAK = ELLIPSIS = NUM = STR = BYTES = \ |
283 PASS = CONTINUE = BREAK = ELLIPSIS = NUM = STR = BYTES = \ |
|
284 LOAD = STORE = DEL = AUGLOAD = AUGSTORE = PARAM = \ |
268 ATTRIBUTES = AND = OR = ADD = SUB = MULT = DIV = \ |
285 ATTRIBUTES = AND = OR = ADD = SUB = MULT = DIV = \ |
269 MOD = POW = LSHIFT = RSHIFT = BITOR = BITXOR = BITAND = FLOORDIV = \ |
286 MOD = POW = LSHIFT = RSHIFT = BITOR = BITXOR = BITAND = FLOORDIV = \ |
270 INVERT = NOT = UADD = USUB = INVERT = NOT = UADD = USUB = ignore |
287 INVERT = NOT = UADD = USUB = EQ = NOTEQ = LT = LTE = GT = GTE = IS = \ |
271 |
288 ISNOT = IN = NOTIN = ignore |
|
289 |
|
290 # "stmt" type nodes |
|
291 RETURN = DELETE = PRINT = WHILE = IF = WITH = RAISE = TRYEXCEPT = \ |
|
292 TRYFINALLY = ASSERT = EXEC = EXPR = handleChildren |
|
293 |
|
294 # "expr" type nodes |
|
295 BOOLOP = BINOP = UNARYOP = IFEXP = DICT = SET = YIELD = COMPARE = \ |
|
296 CALL = REPR = ATTRIBUTE = SUBSCRIPT = LIST = TUPLE = handleChildren |
|
297 |
|
298 # "slice" type nodes |
|
299 SLICE = EXTSLICE = INDEX = handleChildren |
|
300 |
|
301 # additional node types |
|
302 COMPREHENSION = KEYWORD = handleChildren |
|
303 |
272 def addBinding(self, lineno, value, reportRedef = True): |
304 def addBinding(self, lineno, value, reportRedef = True): |
273 '''Called when a binding is altered. |
305 '''Called when a binding is altered. |
274 |
306 |
275 @param lineno line of the statement responsible for the change (integer) |
307 @param lineno line of the statement responsible for the change (integer) |
276 @param value the optional new value, a Binding instance, associated |
308 @param value the optional new value, a Binding instance, associated |
304 |
336 |
305 ############################################################ |
337 ############################################################ |
306 ## individual handler methods below |
338 ## individual handler methods below |
307 ############################################################ |
339 ############################################################ |
308 |
340 |
309 def LIST(self, node): |
|
310 for elt in node.elts: |
|
311 self.handleNode(elt, node) |
|
312 |
|
313 SET = TUPLE = LIST |
|
314 |
|
315 def DICT(self, node): |
|
316 for key in node.keys: |
|
317 self.handleNode(key, node) |
|
318 for val in node.values: |
|
319 self.handleNode(val, node) |
|
320 |
|
321 def WITH(self, node): |
|
322 """ |
|
323 Handle with by checking the target of the statement (which can be an |
|
324 identifier, a list or tuple of targets, an attribute, etc) for |
|
325 undefined names and defining any it adds to the scope and by continuing |
|
326 to process the suite within the statement. |
|
327 """ |
|
328 # Check the "foo" part of a "with foo as bar" statement. Do this no |
|
329 # matter what, since there's always a "foo" part. |
|
330 self.handleNode(node.context_expr, node) |
|
331 |
|
332 arg = None |
|
333 if node.optional_vars is not None: |
|
334 arg = Argument(node.optional_vars.id, node) |
|
335 self.addBinding(node.lineno, arg, reportRedef=False) |
|
336 self.handleBody(node) |
|
337 if arg: |
|
338 del self.scope[arg.name] |
|
339 |
|
340 def GLOBAL(self, node): |
341 def GLOBAL(self, node): |
341 """ |
342 """ |
342 Keep track of globals declarations. |
343 Keep track of globals declarations. |
343 """ |
344 """ |
344 if isinstance(self.scope, FunctionScope): |
345 if isinstance(self.scope, FunctionScope): |
357 for generator in node.generators: |
358 for generator in node.generators: |
358 self.handleNode(generator, node) |
359 self.handleNode(generator, node) |
359 self.handleNode(node.key, node) |
360 self.handleNode(node.key, node) |
360 self.handleNode(node.value, node) |
361 self.handleNode(node.value, node) |
361 |
362 |
362 def COMPREHENSION(self, node): |
|
363 node.target.parent = node |
|
364 self.handleAssignName(node.target) |
|
365 self.handleNode(node.iter, node) |
|
366 for elt in node.ifs: |
|
367 self.handleNode(elt, node) |
|
368 |
|
369 def FOR(self, node): |
363 def FOR(self, node): |
370 """ |
364 """ |
371 Process bindings for loop variables. |
365 Process bindings for loop variables. |
372 """ |
366 """ |
373 vars = [] |
367 vars = [] |
374 def collectLoopVars(n): |
368 def collectLoopVars(n): |
375 if isinstance(n, ast.Name): |
369 if isinstance(n, ast.Name): |
376 vars.append(n.id) |
370 vars.append(n.id) |
|
371 elif isinstance(n, ast.expr_context): |
|
372 return |
|
373 else: |
|
374 for c in ast.iter_child_nodes(n): |
|
375 collectLoopVars(c) |
377 |
376 |
378 collectLoopVars(node.target) |
377 collectLoopVars(node.target) |
379 for varn in vars: |
378 for varn in vars: |
380 if (isinstance(self.scope.get(varn), Importation) |
379 if (isinstance(self.scope.get(varn), Importation) |
381 # unused ones will get an unused import warning |
380 # unused ones will get an unused import warning |
382 and self.scope[varn].used): |
381 and self.scope[varn].used): |
383 self.report(messages.ImportShadowedByLoopVar, |
382 self.report(messages.ImportShadowedByLoopVar, |
384 node.lineno, varn, self.scope[varn].source.lineno) |
383 node.lineno, varn, self.scope[varn].source.lineno) |
385 |
384 |
386 node.target.parent = node |
385 self.handleChildren(node) |
387 self.handleAssignName(node.target) |
|
388 self.handleNode(node.iter, node) |
|
389 self.handleBody(node) |
|
390 |
386 |
391 def NAME(self, node): |
387 def NAME(self, node): |
392 """ |
388 """ |
393 Locate the name in locals / function / globals scopes. |
389 Handle occurrence of Name (which can be a load/store/delete access.) |
394 """ |
390 """ |
395 # try local scope |
391 # Locate the name in locals / function / globals scopes. |
396 importStarred = self.scope.importStarred |
392 if isinstance(node.ctx, (ast.Load, ast.AugLoad)): |
397 try: |
393 # try local scope |
398 self.scope[node.id].used = (self.scope, node.lineno) |
394 importStarred = self.scope.importStarred |
399 except KeyError: |
|
400 pass |
|
401 else: |
|
402 return |
|
403 |
|
404 # try enclosing function scopes |
|
405 for scope in self.scopeStack[-2:0:-1]: |
|
406 importStarred = importStarred or scope.importStarred |
|
407 if not isinstance(scope, FunctionScope): |
|
408 continue |
|
409 try: |
395 try: |
410 scope[node.id].used = (self.scope, node.lineno) |
396 self.scope[node.id].used = (self.scope, node.lineno) |
411 except KeyError: |
397 except KeyError: |
412 pass |
398 pass |
413 else: |
399 else: |
414 return |
400 return |
415 |
401 |
416 # try global scope |
402 # try enclosing function scopes |
417 importStarred = importStarred or self.scopeStack[0].importStarred |
403 for scope in self.scopeStack[-2:0:-1]: |
418 try: |
404 importStarred = importStarred or scope.importStarred |
419 self.scopeStack[0][node.id].used = (self.scope, node.lineno) |
405 if not isinstance(scope, FunctionScope): |
420 except KeyError: |
406 continue |
421 if ((not hasattr(builtins, node.id)) |
407 try: |
422 and node.id not in _MAGIC_GLOBALS |
408 scope[node.id].used = (self.scope, node.lineno) |
423 and not importStarred): |
409 except KeyError: |
424 if (os.path.basename(self.filename) == '__init__.py' and |
|
425 node.id == '__path__'): |
|
426 # the special name __path__ is valid only in packages |
|
427 pass |
410 pass |
428 else: |
411 else: |
429 self.report(messages.UndefinedName, node.lineno, node.id) |
412 return |
|
413 |
|
414 # try global scope |
|
415 importStarred = importStarred or self.scopeStack[0].importStarred |
|
416 try: |
|
417 self.scopeStack[0][node.id].used = (self.scope, node.lineno) |
|
418 except KeyError: |
|
419 if ((not hasattr(builtins, node.id)) |
|
420 and node.id not in _MAGIC_GLOBALS |
|
421 and not importStarred): |
|
422 if (os.path.basename(self.filename) == '__init__.py' and |
|
423 node.id == '__path__'): |
|
424 # the special name __path__ is valid only in packages |
|
425 pass |
|
426 else: |
|
427 self.report(messages.UndefinedName, node.lineno, node.id) |
|
428 elif isinstance(node.ctx, (ast.Store, ast.AugStore)): |
|
429 # if the name hasn't already been defined in the current scope |
|
430 if isinstance(self.scope, FunctionScope) and node.id not in self.scope: |
|
431 # for each function or module scope above us |
|
432 for scope in self.scopeStack[:-1]: |
|
433 if not isinstance(scope, (FunctionScope, ModuleScope)): |
|
434 continue |
|
435 # if the name was defined in that scope, and the name has |
|
436 # been accessed already in the current scope, and hasn't |
|
437 # been declared global |
|
438 if (node.id in scope |
|
439 and scope[node.id].used |
|
440 and scope[node.id].used[0] is self.scope |
|
441 and node.id not in self.scope.globals): |
|
442 # then it's probably a mistake |
|
443 self.report(messages.UndefinedLocal, |
|
444 scope[node.id].used[1], |
|
445 node.id, |
|
446 scope[node.id].source.lineno) |
|
447 break |
|
448 |
|
449 if isinstance(node.parent, |
|
450 (ast.For, ast.comprehension, ast.Tuple, ast.List)): |
|
451 binding = Binding(node.id, node) |
|
452 elif (node.id == '__all__' and |
|
453 isinstance(self.scope, ModuleScope)): |
|
454 binding = ExportBinding(node.id, node.parent.value) |
|
455 else: |
|
456 binding = Assignment(node.id, node) |
|
457 if node.id in self.scope: |
|
458 binding.used = self.scope[node.id].used |
|
459 self.addBinding(node.lineno, binding) |
|
460 elif isinstance(node.ctx, ast.Del): |
|
461 if isinstance(self.scope, FunctionScope) and \ |
|
462 node.id in self.scope.globals: |
|
463 del self.scope.globals[node.id] |
|
464 else: |
|
465 self.addBinding(node.lineno, UnBinding(node.id, node)) |
|
466 else: |
|
467 # must be a Param context -- this only happens for names in function |
|
468 # arguments, but these aren't dispatched through here |
|
469 raise RuntimeError( |
|
470 "Got impossible expression context: {0:r}".format(node.ctx,)) |
430 |
471 |
431 def FUNCTIONDEF(self, node): |
472 def FUNCTIONDEF(self, node): |
432 if getattr(node, "decorator_list", None) is not None: |
473 if getattr(node, "decorator_list", None) is not None: |
433 for decorator in node.decorator_list: |
474 for decorator in node.decorator_list: |
434 self.handleNode(decorator, node) |
475 self.handleNode(decorator, node) |
462 binding.source.lineno, name) |
503 binding.source.lineno, name) |
463 |
504 |
464 self.pushFunctionScope() |
505 self.pushFunctionScope() |
465 addArgs(node.args.args) |
506 addArgs(node.args.args) |
466 addArgs(node.args.kwonlyargs) |
507 addArgs(node.args.kwonlyargs) |
|
508 # vararg/kwarg identifiers are not Name nodes |
|
509 if node.args.vararg: |
|
510 args.append(node.args.vararg) |
|
511 if node.args.kwarg: |
|
512 args.append(node.args.kwarg) |
467 for name in args: |
513 for name in args: |
468 self.addBinding(node.lineno, Argument(name, node), reportRedef=False) |
514 self.addBinding(node.lineno, Argument(name, node), reportRedef=False) |
469 if node.args.vararg: |
|
470 self.addBinding(node.lineno, Argument(node.args.vararg, node), |
|
471 reportRedef=False) |
|
472 if node.args.kwarg: |
|
473 self.addBinding(node.lineno, Argument(node.args.kwarg, node), |
|
474 reportRedef=False) |
|
475 if isinstance(node.body, list): |
515 if isinstance(node.body, list): |
476 self.handleBody(node) |
516 self.handleBody(node) |
477 else: |
517 else: |
478 self.handleNode(node.body, node) |
518 self.handleNode(node.body, node) |
479 self.deferAssignment(checkUnusedAssignments) |
519 self.deferAssignment(checkUnusedAssignments) |
554 binding.used = self.scope[node.id].used |
594 binding.used = self.scope[node.id].used |
555 self.addBinding(node.lineno, binding) |
595 self.addBinding(node.lineno, binding) |
556 |
596 |
557 def ASSIGN(self, node): |
597 def ASSIGN(self, node): |
558 self.handleNode(node.value, node) |
598 self.handleNode(node.value, node) |
559 for subnode in node.targets[::-1]: |
599 for target in node.targets: |
560 subnode.parent = node |
600 self.handleNode(target, node) |
561 if isinstance(subnode, ast.Attribute): |
|
562 self.handleNode(subnode.value, subnode) |
|
563 else: |
|
564 self.handleAssignName(subnode) |
|
565 |
601 |
566 def AUGASSIGN(self, node): |
602 def AUGASSIGN(self, node): |
|
603 # AugAssign is awkward: must set the context explicitly and visit twice, |
|
604 # once with AugLoad context, once with AugStore context |
|
605 node.target.ctx = ast.AugLoad() |
|
606 self.handleNode(node.target, node) |
567 self.handleNode(node.value, node) |
607 self.handleNode(node.value, node) |
|
608 node.target.ctx = ast.AugStore() |
568 self.handleNode(node.target, node) |
609 self.handleNode(node.target, node) |
569 |
610 |
570 def IMPORT(self, node): |
611 def IMPORT(self, node): |
571 for alias in node.names: |
612 for alias in node.names: |
572 name = alias.asname or alias.name |
613 name = alias.asname or alias.name |
590 importation = Importation(name, node) |
631 importation = Importation(name, node) |
591 if node.module == '__future__': |
632 if node.module == '__future__': |
592 importation.used = (self.scope, node.lineno) |
633 importation.used = (self.scope, node.lineno) |
593 self.addBinding(node.lineno, importation) |
634 self.addBinding(node.lineno, importation) |
594 |
635 |
595 def CALL(self, node): |
|
596 self.handleNode(node.func, node) |
|
597 for arg in node.args: |
|
598 self.handleNode(arg, node) |
|
599 for kw in node.keywords: |
|
600 self.handleNode(kw, node) |
|
601 node.starargs and self.handleNode(node.starargs, node) |
|
602 node.kwargs and self.handleNode(node.kwargs, node) |
|
603 |
|
604 def KEYWORD(self, node): |
|
605 self.handleNode(node.value, node) |
|
606 |
|
607 def BOOLOP(self, node): |
|
608 for val in node.values: |
|
609 self.handleNode(val, node) |
|
610 |
|
611 def BINOP(self, node): |
|
612 self.handleNode(node.left, node) |
|
613 self.handleNode(node.right, node) |
|
614 |
|
615 def UNARYOP(self, node): |
|
616 self.handleNode(node.operand, node) |
|
617 |
|
618 def RETURN(self, node): |
|
619 node.value and self.handleNode(node.value, node) |
|
620 |
|
621 def DELETE(self, node): |
|
622 for tgt in node.targets: |
|
623 self.handleNode(tgt, node) |
|
624 |
|
625 def EXPR(self, node): |
|
626 self.handleNode(node.value, node) |
|
627 |
|
628 def ATTRIBUTE(self, node): |
|
629 self.handleNode(node.value, node) |
|
630 |
|
631 def IF(self, node): |
|
632 self.handleNode(node.test, node) |
|
633 self.handleBody(node) |
|
634 for stmt in node.orelse: |
|
635 self.handleNode(stmt, node) |
|
636 |
|
637 WHILE = IF |
|
638 |
|
639 def RAISE(self, node): |
|
640 node.exc and self.handleNode(node.exc, node) |
|
641 node.cause and self.handleNode(node.cause, node) |
|
642 |
|
643 def TRYEXCEPT(self, node): |
|
644 self.handleBody(node) |
|
645 for handler in node.handlers: |
|
646 self.handleNode(handler, node) |
|
647 for stmt in node.orelse: |
|
648 self.handleNode(stmt, node) |
|
649 |
|
650 def TRYFINALLY(self, node): |
|
651 self.handleBody(node) |
|
652 for stmt in node.finalbody: |
|
653 self.handleNode(stmt, node) |
|
654 |
|
655 def EXCEPTHANDLER(self, node): |
636 def EXCEPTHANDLER(self, node): |
656 node.type and self.handleNode(node.type, node) |
637 node.type and self.handleNode(node.type, node) |
657 if node.name: |
638 if node.name: |
658 node.id = node.name |
639 node.id = node.name |
659 self.handleAssignName(node) |
640 self.handleAssignName(node) |
660 self.handleBody(node) |
641 self.handleBody(node) |
661 |
642 |
662 def ASSERT(self, node): |
|
663 self.handleNode(node.test, node) |
|
664 node.msg and self.handleNode(node.msg, node) |
|
665 |
|
666 def COMPARE(self, node): |
|
667 self.handleNode(node.left, node) |
|
668 for comparator in node.comparators: |
|
669 self.handleNode(comparator, node) |
|
670 |
|
671 def YIELD(self, node): |
|
672 node.value and self.handleNode(node.value, node) |
|
673 |
|
674 def SUBSCRIPT(self, node): |
|
675 self.handleNode(node.value, node) |
|
676 self.handleNode(node.slice, node) |
|
677 |
|
678 def SLICE(self, node): |
|
679 node.lower and self.handleNode(node.lower, node) |
|
680 node.upper and self.handleNode(node.upper, node) |
|
681 node.step and self.handleNode(node.step, node) |
|
682 |
|
683 def EXTSLICE(self, node): |
|
684 for slice in node.dims: |
|
685 self.handleNode(slice, node) |
|
686 |
|
687 def INDEX(self, node): |
|
688 self.handleNode(node.value, node) |
|
689 |
|
690 def IFEXP(self, node): |
|
691 self.handleNode(node.test, node) |
|
692 self.handleNode(node.body, node) |
|
693 self.handleNode(node.orelse, node) |
|
694 |
|
695 def STARRED(self, node): |
643 def STARRED(self, node): |
696 self.handleNode(node.value, node) |
644 self.handleNode(node.value, node) |