Example #1
0
    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, ))
Example #2
0
 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
Example #3
0
    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)
Example #4
0
    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)
Example #5
0
    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, ))
Example #6
0
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