512 |
512 |
513 class DoctestScope(ModuleScope): |
513 class DoctestScope(ModuleScope): |
514 """Scope for a doctest.""" |
514 """Scope for a doctest.""" |
515 |
515 |
516 |
516 |
|
517 class DummyNode(object): |
|
518 """Used in place of an `ast.AST` to set error message positions""" |
|
519 def __init__(self, lineno, col_offset): |
|
520 self.lineno = lineno |
|
521 self.col_offset = col_offset |
|
522 |
|
523 |
517 # Globally defined names which are not attributes of the builtins module, or |
524 # Globally defined names which are not attributes of the builtins module, or |
518 # are only present on some platforms. |
525 # are only present on some platforms. |
519 _MAGIC_GLOBALS = ['__file__', '__builtins__', 'WindowsError'] |
526 _MAGIC_GLOBALS = ['__file__', '__builtins__', 'WindowsError'] |
520 # module scope annotation will store in `__annotations__`, see also PEP 526. |
527 # module scope annotation will store in `__annotations__`, see also PEP 526. |
521 if PY36_PLUS: |
528 if PY36_PLUS: |
528 return node.id |
535 return node.id |
529 if hasattr(node, 'name'): # an ExceptHandler node |
536 if hasattr(node, 'name'): # an ExceptHandler node |
530 return node.name |
537 return node.name |
531 |
538 |
532 |
539 |
533 def is_typing_overload(value, scope): |
540 def is_typing_overload(value, scope_stack): |
|
541 def name_is_typing_overload(name): # type: (str) -> bool |
|
542 for scope in reversed(scope_stack): |
|
543 if name in scope: |
|
544 return ( |
|
545 isinstance(scope[name], ImportationFrom) and |
|
546 scope[name].fullName == 'typing.overload' |
|
547 ) |
|
548 else: |
|
549 return False |
|
550 |
534 def is_typing_overload_decorator(node): |
551 def is_typing_overload_decorator(node): |
535 return ( |
552 return ( |
536 ( |
553 ( |
537 isinstance(node, ast.Name) and |
554 isinstance(node, ast.Name) and name_is_typing_overload(node.id) |
538 node.id in scope and |
|
539 scope[node.id].fullName == 'typing.overload' |
|
540 ) or ( |
555 ) or ( |
541 isinstance(node, ast.Attribute) and |
556 isinstance(node, ast.Attribute) and |
542 isinstance(node.value, ast.Name) and |
557 isinstance(node.value, ast.Name) and |
543 node.value.id == 'typing' and |
558 node.value.id == 'typing' and |
544 node.attr == 'overload' |
559 node.attr == 'overload' |
545 ) |
560 ) |
546 ) |
561 ) |
547 |
562 |
548 return ( |
563 return ( |
549 isinstance(value.source, ast.FunctionDef) and |
564 isinstance(value.source, ast.FunctionDef) and |
550 len(value.source.decorator_list) == 1 and |
565 any( |
551 is_typing_overload_decorator(value.source.decorator_list[0]) |
566 is_typing_overload_decorator(dec) |
|
567 for dec in value.source.decorator_list |
|
568 ) |
552 ) |
569 ) |
553 |
570 |
554 |
571 |
555 def make_tokens(code): |
572 def make_tokens(code): |
556 # PY3: tokenize.tokenize requires readline of bytes |
573 # PY3: tokenize.tokenize requires readline of bytes |
885 (FOR_TYPES, ast.comprehension))): |
902 (FOR_TYPES, ast.comprehension))): |
886 self.report(messages.RedefinedInListComp, |
903 self.report(messages.RedefinedInListComp, |
887 node, value.name, existing.source) |
904 node, value.name, existing.source) |
888 elif not existing.used and value.redefines(existing): |
905 elif not existing.used and value.redefines(existing): |
889 if value.name != '_' or isinstance(existing, Importation): |
906 if value.name != '_' or isinstance(existing, Importation): |
890 if not is_typing_overload(existing, self.scope): |
907 if not is_typing_overload(existing, self.scopeStack): |
891 self.report(messages.RedefinedWhileUnused, |
908 self.report(messages.RedefinedWhileUnused, |
892 node, value.name, existing.source) |
909 node, value.name, existing.source) |
893 |
910 |
894 elif isinstance(existing, Importation) and value.redefines(existing): |
911 elif isinstance(existing, Importation) and value.redefines(existing): |
895 existing.redefined.append(node) |
912 existing.redefined.append(node) |
1060 for part in parts: |
1077 for part in parts: |
1061 if PY2: |
1078 if PY2: |
1062 part = part.replace('...', 'Ellipsis') |
1079 part = part.replace('...', 'Ellipsis') |
1063 self.deferFunction(functools.partial( |
1080 self.deferFunction(functools.partial( |
1064 self.handleStringAnnotation, |
1081 self.handleStringAnnotation, |
1065 part, node, lineno, col_offset, |
1082 part, DummyNode(lineno, col_offset), lineno, col_offset, |
1066 messages.CommentAnnotationSyntaxError, |
1083 messages.CommentAnnotationSyntaxError, |
1067 )) |
1084 )) |
1068 |
1085 |
1069 def handleChildren(self, tree, omit=None): |
1086 def handleChildren(self, tree, omit=None): |
1070 self._handle_type_comments(tree) |
1087 self._handle_type_comments(tree) |
1310 # One 'global' statement can bind multiple (comma-delimited) names. |
1327 # One 'global' statement can bind multiple (comma-delimited) names. |
1311 for node_name in node.names: |
1328 for node_name in node.names: |
1312 node_value = Assignment(node_name, node) |
1329 node_value = Assignment(node_name, node) |
1313 |
1330 |
1314 # Remove UndefinedName messages already reported for this name. |
1331 # Remove UndefinedName messages already reported for this name. |
1315 # TO DO: if the global is not used in this scope, it does not |
1332 # TODO: if the global is not used in this scope, it does not |
1316 # become a globally defined name. See test_unused_global. |
1333 # become a globally defined name. See test_unused_global. |
1317 self.messages = [ |
1334 self.messages = [ |
1318 m for m in self.messages if not |
1335 m for m in self.messages if not |
1319 isinstance(m, messages.UndefinedName) or |
1336 isinstance(m, messages.UndefinedName) or |
1320 m.message_args[0] != node_name] |
1337 m.message_args[0] != node_name] |