def _check_name_used_prior_global(self, node: nodes.FunctionDef) -> None: scope_globals = { name: child for child in node.nodes_of_class(nodes.Global) for name in child.names if child.scope() is node } if not scope_globals: return for node_name in node.nodes_of_class(nodes.Name): if node_name.scope() is not node: continue name = node_name.name corresponding_global = scope_globals.get(name) if not corresponding_global: continue global_lineno = corresponding_global.fromlineno if global_lineno and global_lineno > node_name.fromlineno: self.add_message("used-prior-global-declaration", node=node_name, args=(name, ))
def visit_functiondef(self, node: nodes.FunctionDef) -> None: self._check_nonlocal_and_global(node) self._check_name_used_prior_global(node) if not redefined_by_decorator( node) and not utils.is_registered_in_singledispatch_function( node): self._check_redefinition( node.is_method() and "method" or "function", node) # checks for max returns, branch, return in __init__ returns = node.nodes_of_class(nodes.Return, skip_klass=(nodes.FunctionDef, nodes.ClassDef)) if node.is_method() and node.name == "__init__": if node.is_generator(): self.add_message("init-is-generator", node=node) else: values = [r.value for r in returns] # Are we returning anything but None from constructors if any(v for v in values if not utils.is_none(v)): self.add_message("return-in-init", node=node) # Check for duplicate names by clustering args with same name for detailed report arg_clusters = {} arguments: Iterator[Any] = filter( None, [node.args.args, node.args.kwonlyargs]) for arg in itertools.chain.from_iterable(arguments): if arg.name in arg_clusters: self.add_message( "duplicate-argument-name", node=arg, args=(arg.name, ), confidence=HIGH, ) else: arg_clusters[arg.name] = arg
def check_functiondef_returns(self, node: nodes.FunctionDef, node_doc: Docstring) -> None: if (not node_doc.supports_yields and node.is_generator()) or node.is_abstract(): return return_nodes = node.nodes_of_class(astroid.Return) if (node_doc.has_returns() or node_doc.has_rtype()) and not any( utils.returns_something(ret_node) for ret_node in return_nodes): self.add_message("redundant-returns-doc", node=node)
def _check_nonlocal_and_global(self, node: nodes.FunctionDef) -> None: """Check that a name is both nonlocal and global.""" def same_scope(current: nodes.Global | nodes.Nonlocal) -> bool: return current.scope() is node from_iter = itertools.chain.from_iterable nonlocals = set( from_iter(child.names for child in node.nodes_of_class(nodes.Nonlocal) if same_scope(child))) if not nonlocals: return global_vars = set( from_iter(child.names for child in node.nodes_of_class(nodes.Global) if same_scope(child))) for name in nonlocals.intersection(global_vars): self.add_message("nonlocal-and-global", args=(name, ), node=node)
def visit_functiondef(self, node: nodes.FunctionDef) -> None: """check use of super""" # ignore actual functions or method within a new style class if not node.is_method(): return klass = node.parent.frame() for stmt in node.nodes_of_class(nodes.Call): if node_frame_class(stmt) != node_frame_class(node): # Don't look down in other scopes. continue expr = stmt.func if not isinstance(expr, nodes.Attribute): continue call = expr.expr # skip the test if using super if not (isinstance(call, nodes.Call) and isinstance( call.func, nodes.Name) and call.func.name == "super"): continue # super should not be used on an old style class if klass.newstyle or not has_known_bases(klass): # super first arg should not be the class if not call.args: continue # calling super(type(self), self) can lead to recursion loop # in derived classes arg0 = call.args[0] if (isinstance(arg0, nodes.Call) and isinstance(arg0.func, nodes.Name) and arg0.func.name == "type"): self.add_message("bad-super-call", node=call, args=("type", )) continue # calling super(self.__class__, self) can lead to recursion loop # in derived classes if (len(call.args) >= 2 and isinstance(call.args[1], nodes.Name) and call.args[1].name == "self" and isinstance(arg0, nodes.Attribute) and arg0.attrname == "__class__"): self.add_message("bad-super-call", node=call, args=("self.__class__", )) continue try: supcls = call.args and next(call.args[0].infer(), None) except astroid.InferenceError: continue if klass is not supcls: name = None # if supcls is not Uninferable, then supcls was inferred # and use its name. Otherwise, try to look # for call.args[0].name if supcls: name = supcls.name elif call.args and hasattr(call.args[0], "name"): name = call.args[0].name if name: self.add_message("bad-super-call", node=call, args=(name, ))
def _has_bare_super_call(fundef_node: nodes.FunctionDef) -> bool: for call in fundef_node.nodes_of_class(nodes.Call): func = call.func if isinstance(func, nodes.Name) and func.name == "super" and not call.args: return True return False