コード例 #1
0
ファイル: classes.py プロジェクト: yannack/pylint
class ClassChecker(BaseChecker):
    """checks for :
    * methods without self as first argument
    * overridden methods signature
    * access only to existent members via self
    * attributes not defined in the __init__ method
    * unreachable code
    """

    __implements__ = (IAstroidChecker, )

    # configuration section name
    name = 'classes'
    # messages
    msgs = MSGS
    priority = -2
    # configuration options
    options = (
        (
            'ignore-iface-methods',
            # TODO(cpopa): remove this in Pylint 1.6.
            deprecated_option(opt_type="csv",
                              help_msg="This is deprecated, because "
                              "it is not used anymore.")),
        ('defining-attr-methods', {
            'default': ('__init__', '__new__', 'setUp'),
            'type':
            'csv',
            'metavar':
            '<method names>',
            'help':
            'List of method names used to declare (i.e. assign) \
instance attributes.'
        }),
        ('valid-classmethod-first-arg', {
            'default': ('cls', ),
            'type':
            'csv',
            'metavar':
            '<argument names>',
            'help':
            'List of valid names for the first argument in \
a class method.'
        }),
        ('valid-metaclass-classmethod-first-arg', {
            'default': ('mcs', ),
            'type':
            'csv',
            'metavar':
            '<argument names>',
            'help':
            'List of valid names for the first argument in \
a metaclass class method.'
        }),
        (
            'exclude-protected',
            {
                'default': (
                    # namedtuple public API.
                    '_asdict',
                    '_fields',
                    '_replace',
                    '_source',
                    '_make'),
                'type':
                'csv',
                'metavar':
                '<protected access exclusions>',
                'help': ('List of member names, which should be excluded '
                         'from the protected access warning.')
            }))

    def __init__(self, linter=None):
        BaseChecker.__init__(self, linter)
        self._accessed = []
        self._first_attrs = []
        self._meth_could_be_func = None

    def visit_classdef(self, node):
        """init visit variable _accessed
        """
        self._accessed.append(defaultdict(list))
        self._check_bases_classes(node)
        # if not an exception or a metaclass
        if node.type == 'class' and has_known_bases(node):
            try:
                node.local_attr('__init__')
            except astroid.NotFoundError:
                self.add_message('no-init', args=node, node=node)
        self._check_slots(node)
        self._check_proper_bases(node)
        self._check_consistent_mro(node)

    def _check_consistent_mro(self, node):
        """Detect that a class has a consistent mro or duplicate bases."""
        try:
            node.mro()
        except InconsistentMroError:
            self.add_message('inconsistent-mro', args=node.name, node=node)
        except DuplicateBasesError:
            self.add_message('duplicate-bases', args=node.name, node=node)
        except NotImplementedError:
            # Old style class, there's no mro so don't do anything.
            pass

    def _check_proper_bases(self, node):
        """
        Detect that a class inherits something which is not
        a class or a type.
        """
        for base in node.bases:
            ancestor = safe_infer(base)
            if ancestor in (astroid.YES, None):
                continue
            if (isinstance(ancestor, astroid.Instance)
                    and ancestor.is_subtype_of('%s.type' % (BUILTINS, ))):
                continue

            if (not isinstance(ancestor, astroid.ClassDef)
                    or _is_invalid_base_class(ancestor)):
                self.add_message('inherit-non-class',
                                 args=base.as_string(),
                                 node=node)

    def leave_classdef(self, cnode):
        """close a class node:
        check that instance attributes are defined in __init__ and check
        access to existent members
        """
        # check access to existent members on non metaclass classes
        ignore_mixins = get_global_option(self,
                                          'ignore-mixin-members',
                                          default=True)
        if ignore_mixins and cnode.name[-5:].lower() == 'mixin':
            # We are in a mixin class. No need to try to figure out if
            # something is missing, since it is most likely that it will
            # miss.
            return

        accessed = self._accessed.pop()
        if cnode.type != 'metaclass':
            self._check_accessed_members(cnode, accessed)
        # checks attributes are defined in an allowed method such as __init__
        if not self.linter.is_message_enabled(
                'attribute-defined-outside-init'):
            return
        defining_methods = self.config.defining_attr_methods
        current_module = cnode.root()
        for attr, nodes in six.iteritems(cnode.instance_attrs):
            # skip nodes which are not in the current module and it may screw up
            # the output, while it's not worth it
            nodes = [
                n for n in nodes
                if not isinstance(n.statement(), (astroid.Delete,
                                                  astroid.AugAssign))
                and n.root() is current_module
            ]
            if not nodes:
                continue  # error detected by typechecking
            # check if any method attr is defined in is a defining method
            if any(node.frame().name in defining_methods for node in nodes):
                continue

            # check attribute is defined in a parent's __init__
            for parent in cnode.instance_attr_ancestors(attr):
                attr_defined = False
                # check if any parent method attr is defined in is a defining method
                for node in parent.instance_attrs[attr]:
                    if node.frame().name in defining_methods:
                        attr_defined = True
                if attr_defined:
                    # we're done :)
                    break
            else:
                # check attribute is defined as a class attribute
                try:
                    cnode.local_attr(attr)
                except astroid.NotFoundError:
                    for node in nodes:
                        if node.frame().name not in defining_methods:
                            # If the attribute was set by a callfunc in any
                            # of the defining methods, then don't emit
                            # the warning.
                            if _called_in_methods(node.frame(), cnode,
                                                  defining_methods):
                                continue
                            self.add_message('attribute-defined-outside-init',
                                             args=attr,
                                             node=node)

    def visit_functiondef(self, node):
        """check method arguments, overriding"""
        # ignore actual functions
        if not node.is_method():
            return
        klass = node.parent.frame()
        self._meth_could_be_func = True
        # check first argument is self if this is actually a method
        self._check_first_arg_for_type(node, klass.type == 'metaclass')
        if node.name == '__init__':
            self._check_init(node)
            return
        # check signature if the method overloads inherited method
        for overridden in klass.local_attr_ancestors(node.name):
            # get astroid for the searched method
            try:
                meth_node = overridden[node.name]
            except KeyError:
                # we have found the method but it's not in the local
                # dictionary.
                # This may happen with astroid build from living objects
                continue
            if not isinstance(meth_node, astroid.FunctionDef):
                continue
            self._check_signature(node, meth_node, 'overridden', klass)
            break
        if node.decorators:
            for decorator in node.decorators.nodes:
                if isinstance(decorator, astroid.Attribute) and \
                        decorator.attrname in ('getter', 'setter', 'deleter'):
                    # attribute affectation will call this method, not hiding it
                    return
                if isinstance(decorator,
                              astroid.Name) and decorator.name == 'property':
                    # attribute affectation will either call a setter or raise
                    # an attribute error, anyway not hiding the function
                    return
        # check if the method is hidden by an attribute
        try:
            overridden = klass.instance_attr(node.name)[0]  # XXX
            overridden_frame = overridden.frame()
            if (isinstance(overridden_frame, astroid.FunctionDef)
                    and overridden_frame.type == 'method'):
                overridden_frame = overridden_frame.parent.frame()
            if (isinstance(overridden_frame, astroid.ClassDef)
                    and klass.is_subtype_of(overridden_frame.qname())):
                args = (overridden.root().name, overridden.fromlineno)
                self.add_message('method-hidden', args=args, node=node)
        except astroid.NotFoundError:
            pass

    visit_asyncfunctiondef = visit_functiondef

    def _check_slots(self, node):
        if '__slots__' not in node.locals:
            return
        for slots in node.igetattr('__slots__'):
            # check if __slots__ is a valid type
            if slots is astroid.YES:
                continue
            if not is_iterable(slots) and not is_comprehension(slots):
                self.add_message('invalid-slots', node=node)
                continue

            if isinstance(slots, astroid.Const):
                # a string, ignore the following checks
                continue
            if not hasattr(slots, 'itered'):
                # we can't obtain the values, maybe a .deque?
                continue

            if isinstance(slots, astroid.Dict):
                values = [item[0] for item in slots.items]
            else:
                values = slots.itered()
            if values is astroid.YES:
                return

            for elt in values:
                try:
                    self._check_slots_elt(elt)
                except astroid.InferenceError:
                    continue

    def _check_slots_elt(self, elt):
        for infered in elt.infer():
            if infered is astroid.YES:
                continue
            if (not isinstance(infered, astroid.Const)
                    or not isinstance(infered.value, six.string_types)):
                self.add_message('invalid-slots-object',
                                 args=infered.as_string(),
                                 node=elt)
                continue
            if not infered.value:
                self.add_message('invalid-slots-object',
                                 args=infered.as_string(),
                                 node=elt)

    def leave_functiondef(self, node):
        """on method node, check if this method couldn't be a function

        ignore class, static and abstract methods, initializer,
        methods overridden from a parent class.
        """
        if node.is_method():
            if node.args.args is not None:
                self._first_attrs.pop()
            if not self.linter.is_message_enabled('no-self-use'):
                return
            class_node = node.parent.frame()
            if (self._meth_could_be_func and node.type == 'method'
                    and node.name not in PYMETHODS
                    and not (node.is_abstract()
                             or overrides_a_method(class_node, node.name)
                             or decorated_with_property(node) or
                             (six.PY3 and _has_bare_super_call(node)))):
                self.add_message('no-self-use', node=node)

    def visit_attribute(self, node):
        """check if the getattr is an access to a class member
        if so, register it. Also check for access to protected
        class member from outside its class (but ignore __special__
        methods)
        """
        attrname = node.attrname
        # Check self
        if self.is_first_attr(node):
            self._accessed[-1][attrname].append(node)
            return
        if not self.linter.is_message_enabled('protected-access'):
            return

        self._check_protected_attribute_access(node)

    def visit_assignattr(self, node):
        if isinstance(node.assign_type(),
                      astroid.AugAssign) and self.is_first_attr(node):
            self._accessed[-1][node.attrname].append(node)
        self._check_in_slots(node)

    def _check_in_slots(self, node):
        """ Check that the given assattr node
        is defined in the class slots.
        """
        infered = safe_infer(node.expr)
        if infered and isinstance(infered, astroid.Instance):
            klass = infered._proxied
            if '__slots__' not in klass.locals or not klass.newstyle:
                return

            slots = klass.slots()
            if slots is None:
                return
            # If any ancestor doesn't use slots, the slots
            # defined for this class are superfluous.
            if any('__slots__' not in ancestor.locals
                   and ancestor.name != 'object'
                   for ancestor in klass.ancestors()):
                return

            if not any(slot.value == node.attrname for slot in slots):
                # If we have a '__dict__' in slots, then
                # assigning any name is valid.
                if not any(slot.value == '__dict__' for slot in slots):
                    if _is_attribute_property(node.attrname, klass):
                        # Properties circumvent the slots mechanism,
                        # so we should not emit a warning for them.
                        return
                    if (node.attrname in klass.locals
                            and _has_data_descriptor(klass, node.attrname)):
                        # Descriptors circumvent the slots mechanism as well.
                        return
                    self.add_message('assigning-non-slot',
                                     args=(node.attrname, ),
                                     node=node)

    @check_messages('protected-access', 'no-classmethod-decorator',
                    'no-staticmethod-decorator')
    def visit_assign(self, assign_node):
        self._check_classmethod_declaration(assign_node)
        node = assign_node.targets[0]
        if not isinstance(node, astroid.AssignAttr):
            return

        if self.is_first_attr(node):
            return
        self._check_protected_attribute_access(node)

    def _check_classmethod_declaration(self, node):
        """Checks for uses of classmethod() or staticmethod()

        When a @classmethod or @staticmethod decorator should be used instead.
        A message will be emitted only if the assignment is at a class scope
        and only if the classmethod's argument belongs to the class where it
        is defined.
        `node` is an assign node.
        """
        if not isinstance(node.value, astroid.Call):
            return

        # check the function called is "classmethod" or "staticmethod"
        func = node.value.func
        if (not isinstance(func, astroid.Name)
                or func.name not in ('classmethod', 'staticmethod')):
            return

        msg = ('no-classmethod-decorator'
               if func.name == 'classmethod' else 'no-staticmethod-decorator')
        # assignment must be at a class scope
        parent_class = node.scope()
        if not isinstance(parent_class, astroid.ClassDef):
            return

        # Check if the arg passed to classmethod is a class member
        classmeth_arg = node.value.args[0]
        if not isinstance(classmeth_arg, astroid.Name):
            return

        method_name = classmeth_arg.name
        if any(method_name == member.name
               for member in parent_class.mymethods()):
            self.add_message(msg, node=node.targets[0])

    def _check_protected_attribute_access(self, node):
        '''Given an attribute access node (set or get), check if attribute
        access is legitimate. Call _check_first_attr with node before calling
        this method. Valid cases are:
        * self._attr in a method or cls._attr in a classmethod. Checked by
        _check_first_attr.
        * Klass._attr inside "Klass" class.
        * Klass2._attr inside "Klass" class when Klass2 is a base class of
            Klass.
        '''
        attrname = node.attrname

        if (is_attr_protected(attrname)
                and attrname not in self.config.exclude_protected):

            klass = node_frame_class(node)

            # XXX infer to be more safe and less dirty ??
            # in classes, check we are not getting a parent method
            # through the class object or through super
            callee = node.expr.as_string()

            # We are not in a class, no remaining valid case
            if klass is None:
                self.add_message('protected-access', node=node, args=attrname)
                return

            # If the expression begins with a call to super, that's ok.
            if isinstance(node.expr, astroid.Call) and \
               isinstance(node.expr.func, astroid.Name) and \
               node.expr.func.name == 'super':
                return

            # We are in a class, one remaining valid cases, Klass._attr inside
            # Klass
            if not (callee == klass.name or callee in klass.basenames):
                # Detect property assignments in the body of the class.
                # This is acceptable:
                #
                # class A:
                #     b = property(lambda: self._b)

                stmt = node.parent.statement()
                if (isinstance(stmt, astroid.Assign) and len(stmt.targets) == 1
                        and isinstance(stmt.targets[0], astroid.AssignName)):
                    name = stmt.targets[0].name
                    if _is_attribute_property(name, klass):
                        return

                self.add_message('protected-access', node=node, args=attrname)

    def visit_name(self, node):
        """check if the name handle an access to a class member
        if so, register it
        """
        if self._first_attrs and (node.name == self._first_attrs[-1]
                                  or not self._first_attrs[-1]):
            self._meth_could_be_func = False

    def _check_accessed_members(self, node, accessed):
        """check that accessed members are defined"""
        # XXX refactor, probably much simpler now that E0201 is in type checker
        excs = ('AttributeError', 'Exception', 'BaseException')
        for attr, nodes in six.iteritems(accessed):
            try:
                # is it a class attribute ?
                node.local_attr(attr)
                # yes, stop here
                continue
            except astroid.NotFoundError:
                pass
            # is it an instance attribute of a parent class ?
            try:
                next(node.instance_attr_ancestors(attr))
                # yes, stop here
                continue
            except StopIteration:
                pass
            # is it an instance attribute ?
            try:
                defstmts = node.instance_attr(attr)
            except astroid.NotFoundError:
                pass
            else:
                # filter out augment assignment nodes
                defstmts = [stmt for stmt in defstmts if stmt not in nodes]
                if not defstmts:
                    # only augment assignment for this node, no-member should be
                    # triggered by the typecheck checker
                    continue
                # filter defstmts to only pick the first one when there are
                # several assignments in the same scope
                scope = defstmts[0].scope()
                defstmts = [
                    stmt for i, stmt in enumerate(defstmts)
                    if i == 0 or stmt.scope() is not scope
                ]
                # if there are still more than one, don't attempt to be smarter
                # than we can be
                if len(defstmts) == 1:
                    defstmt = defstmts[0]
                    # check that if the node is accessed in the same method as
                    # it's defined, it's accessed after the initial assignment
                    frame = defstmt.frame()
                    lno = defstmt.fromlineno
                    for _node in nodes:
                        if _node.frame() is frame and _node.fromlineno < lno \
                           and not astroid.are_exclusive(_node.statement(), defstmt, excs):
                            self.add_message('access-member-before-definition',
                                             node=_node,
                                             args=(attr, lno))

    def _check_first_arg_for_type(self, node, metaclass=0):
        """check the name of first argument, expect:

        * 'self' for a regular method
        * 'cls' for a class method or a metaclass regular method (actually
          valid-classmethod-first-arg value)
        * 'mcs' for a metaclass class method (actually
          valid-metaclass-classmethod-first-arg)
        * not one of the above for a static method
        """
        # don't care about functions with unknown argument (builtins)
        if node.args.args is None:
            return
        first_arg = node.args.args and node.argnames()[0]
        self._first_attrs.append(first_arg)
        first = self._first_attrs[-1]
        # static method
        if node.type == 'staticmethod':
            if (first_arg == 'self'
                    or first_arg in self.config.valid_classmethod_first_arg
                    or first_arg
                    in self.config.valid_metaclass_classmethod_first_arg):
                self.add_message('bad-staticmethod-argument',
                                 args=first,
                                 node=node)
                return
            self._first_attrs[-1] = None
        # class / regular method with no args
        elif not node.args.args:
            self.add_message('no-method-argument', node=node)
        # metaclass
        elif metaclass:
            # metaclass __new__ or classmethod
            if node.type == 'classmethod':
                self._check_first_arg_config(
                    first, self.config.valid_metaclass_classmethod_first_arg,
                    node, 'bad-mcs-classmethod-argument', node.name)
            # metaclass regular method
            else:
                self._check_first_arg_config(
                    first, self.config.valid_classmethod_first_arg, node,
                    'bad-mcs-method-argument', node.name)
        # regular class
        else:
            # class method
            if node.type == 'classmethod':
                self._check_first_arg_config(
                    first, self.config.valid_classmethod_first_arg, node,
                    'bad-classmethod-argument', node.name)
            # regular method without self as argument
            elif first != 'self':
                self.add_message('no-self-argument', node=node)

    def _check_first_arg_config(self, first, config, node, message,
                                method_name):
        if first not in config:
            if len(config) == 1:
                valid = repr(config[0])
            else:
                valid = ', '.join(repr(v) for v in config[:-1])
                valid = '%s or %r' % (valid, config[-1])
            self.add_message(message, args=(method_name, valid), node=node)

    def _check_bases_classes(self, node):
        """check that the given class node implements abstract methods from
        base classes
        """
        def is_abstract(method):
            return method.is_abstract(pass_is_abstract=False)

        # check if this class abstract
        if class_is_abstract(node):
            return

        methods = sorted(
            unimplemented_abstract_methods(node, is_abstract).items(),
            key=lambda item: item[0],
        )
        for name, method in methods:
            owner = method.parent.frame()
            if owner is node:
                continue
            # owner is not this class, it must be a parent class
            # check that the ancestor's method is not abstract
            if name in node.locals:
                # it is redefined as an attribute or with a descriptor
                continue
            self.add_message('abstract-method',
                             node=node,
                             args=(name, owner.name))

    def _check_init(self, node):
        """check that the __init__ method call super or ancestors'__init__
        method
        """
        if (not self.linter.is_message_enabled('super-init-not-called') and
                not self.linter.is_message_enabled('non-parent-init-called')):
            return
        klass_node = node.parent.frame()
        to_call = _ancestors_to_call(klass_node)
        not_called_yet = dict(to_call)
        for stmt in node.nodes_of_class(astroid.Call):
            expr = stmt.func
            if not isinstance(expr, astroid.Attribute) \
                   or expr.attrname != '__init__':
                continue
            # skip the test if using super
            if isinstance(expr.expr, astroid.Call) and \
                   isinstance(expr.expr.func, astroid.Name) and \
               expr.expr.func.name == 'super':
                return
            try:
                for klass in expr.expr.infer():
                    if klass is astroid.YES:
                        continue
                    # The infered klass can be super(), which was
                    # assigned to a variable and the `__init__`
                    # was called later.
                    #
                    # base = super()
                    # base.__init__(...)

                    if (isinstance(klass, astroid.Instance)
                            and isinstance(klass._proxied, astroid.ClassDef)
                            and is_builtin_object(klass._proxied)
                            and klass._proxied.name == 'super'):
                        return
                    elif isinstance(klass, objects.Super):
                        return
                    try:
                        del not_called_yet[klass]
                    except KeyError:
                        if klass not in to_call:
                            self.add_message('non-parent-init-called',
                                             node=expr,
                                             args=klass.name)
            except astroid.InferenceError:
                continue
        for klass, method in six.iteritems(not_called_yet):
            cls = node_frame_class(method)
            if klass.name == 'object' or (cls and cls.name == 'object'):
                continue
            self.add_message('super-init-not-called',
                             args=klass.name,
                             node=node)

    def _check_signature(self, method1, refmethod, class_type, cls):
        """check that the signature of the two given methods match
        """
        if not (isinstance(method1, astroid.FunctionDef)
                and isinstance(refmethod, astroid.FunctionDef)):
            self.add_message('method-check-failed',
                             args=(method1, refmethod),
                             node=method1)
            return

        instance = cls.instanciate_class()
        method1 = function_to_method(method1, instance)
        refmethod = function_to_method(refmethod, instance)

        # Don't care about functions with unknown argument (builtins).
        if method1.args.args is None or refmethod.args.args is None:
            return
        # If we use *args, **kwargs, skip the below checks.
        if method1.args.vararg or method1.args.kwarg:
            return
        # Ignore private to class methods.
        if is_attr_private(method1.name):
            return
        # Ignore setters, they have an implicit extra argument,
        # which shouldn't be taken in consideration.
        if method1.decorators:
            for decorator in method1.decorators.nodes:
                if (isinstance(decorator, astroid.Attribute)
                        and decorator.attrname == 'setter'):
                    return

        method1_args = _get_method_args(method1)
        refmethod_args = _get_method_args(refmethod)
        if method1_args != refmethod_args:
            self.add_message('arguments-differ',
                             args=(class_type, method1.name),
                             node=method1)
        elif len(method1.args.defaults) < len(refmethod.args.defaults):
            self.add_message('signature-differs',
                             args=(class_type, method1.name),
                             node=method1)

    def is_first_attr(self, node):
        """Check that attribute lookup name use first attribute variable name
        (self for method, cls for classmethod and mcs for metaclass).
        """
        return self._first_attrs and isinstance(node.expr, astroid.Name) and \
                   node.expr.name == self._first_attrs[-1]
コード例 #2
0
class TypeChecker(BaseChecker):
    """try to find bugs in the code using type inference
    """

    __implements__ = (IAstroidChecker, )

    # configuration section name
    name = 'typecheck'
    # messages
    msgs = MSGS
    priority = -1
    # configuration options
    options = (
        ('ignore-mixin-members', {
            'default':
            True,
            'type':
            'yn',
            'metavar':
            '<y_or_n>',
            'help':
            'Tells whether missing members accessed in mixin \
class should be ignored. A mixin class is detected if its name ends with \
"mixin" (case insensitive).'
        }),
        ('ignored-modules', {
            'default': (),
            'type':
            'csv',
            'metavar':
            '<module names>',
            'help':
            'List of module names for which member attributes '
            'should not be checked (useful for modules/projects '
            'where namespaces are manipulated during runtime and '
            'thus existing member attributes cannot be '
            'deduced by static analysis. It supports qualified '
            'module names, as well as Unix pattern matching.'
        }),
        ('ignored-classes', {
            'default': (),
            'type':
            'csv',
            'metavar':
            '<members names>',
            'help':
            'List of classes names for which member attributes '
            'should not be checked (useful for classes with '
            'attributes dynamically set). This supports '
            'can work with qualified names.'
        }),
        ('zope',
         utils.deprecated_option(opt_type='yn', help_msg=_ZOPE_DEPRECATED)),
        ('generated-members', {
            'default': (),
            'type':
            'string',
            'metavar':
            '<members names>',
            'help':
            'List of members which are set dynamically and \
missed by pylint inference system, and so shouldn\'t trigger E1101 when \
accessed. Python regular expressions are accepted.'
        }),
    )

    def open(self):
        # do this in open since config not fully initialized in __init__
        self.generated_members = list(self.config.generated_members)

    def visit_assignattr(self, node):
        if isinstance(node.assign_type(), astroid.AugAssign):
            self.visit_attribute(node)

    def visit_delattr(self, node):
        self.visit_attribute(node)

    @check_messages('no-member')
    def visit_attribute(self, node):
        """check that the accessed attribute exists

        to avoid too much false positives for now, we'll consider the code as
        correct if a single of the inferred nodes has the accessed attribute.

        function/method, super call and metaclasses are ignored
        """
        # generated_members may contain regular expressions
        # (surrounded by quote `"` and followed by a comma `,`)
        # REQUEST,aq_parent,"[a-zA-Z]+_set{1,2}"' =>
        # ('REQUEST', 'aq_parent', '[a-zA-Z]+_set{1,2}')
        if isinstance(self.config.generated_members, str):
            gen = shlex.shlex(self.config.generated_members)
            gen.whitespace += ','
            gen.wordchars += '[]-+'
            self.config.generated_members = tuple(
                tok.strip('"') for tok in gen)
        for pattern in self.config.generated_members:
            # attribute is marked as generated, stop here
            if re.match(pattern, node.attrname):
                return
        try:
            infered = list(node.expr.infer())
        except exceptions.InferenceError:
            return
        # list of (node, nodename) which are missing the attribute
        missingattr = set()
        inference_failure = False
        for owner in infered:
            # skip yes object
            if owner is astroid.YES:
                inference_failure = True
                continue

            name = getattr(owner, 'name', None)
            if _is_owner_ignored(owner, name, self.config.ignored_classes,
                                 self.config.ignored_modules):
                continue

            try:
                if not [
                        n for n in owner.getattr(node.attrname)
                        if not isinstance(n.statement(), astroid.AugAssign)
                ]:
                    missingattr.add((owner, name))
                    continue
            except AttributeError:
                # XXX method / function
                continue
            except exceptions.NotFoundError:
                # This can't be moved before the actual .getattr call,
                # because there can be more values inferred and we are
                # stopping after the first one which has the attribute in question.
                # The problem is that if the first one has the attribute,
                # but we continue to the next values which doesn't have the
                # attribute, then we'll have a false positive.
                # So call this only after the call has been made.
                if not _emit_no_member(node, owner, name,
                                       self.config.ignore_mixin_members):
                    continue
                missingattr.add((owner, name))
                continue
            # stop on the first found
            break
        else:
            # we have not found any node with the attributes, display the
            # message for infered nodes
            done = set()
            for owner, name in missingattr:
                if isinstance(owner, astroid.Instance):
                    actual = owner._proxied
                else:
                    actual = owner
                if actual in done:
                    continue
                done.add(actual)
                confidence = INFERENCE if not inference_failure else INFERENCE_FAILURE
                self.add_message('no-member',
                                 node=node,
                                 args=(owner.display_type(), name,
                                       node.attrname),
                                 confidence=confidence)

    @check_messages('assignment-from-no-return', 'assignment-from-none')
    def visit_assign(self, node):
        """check that if assigning to a function call, the function is
        possibly returning something valuable
        """
        if not isinstance(node.value, astroid.Call):
            return
        function_node = safe_infer(node.value.func)
        # skip class, generator and incomplete function definition
        if not (isinstance(function_node, astroid.FunctionDef)
                and function_node.root().fully_defined()):
            return
        if function_node.is_generator() \
               or function_node.is_abstract(pass_is_abstract=False):
            return
        returns = list(
            function_node.nodes_of_class(astroid.Return,
                                         skip_klass=astroid.FunctionDef))
        if len(returns) == 0:
            self.add_message('assignment-from-no-return', node=node)
        else:
            for rnode in returns:
                if not (isinstance(rnode.value, astroid.Const)
                        and rnode.value.value is None or rnode.value is None):
                    break
            else:
                self.add_message('assignment-from-none', node=node)

    def _check_uninferable_callfunc(self, node):
        """
        Check that the given uninferable CallFunc node does not
        call an actual function.
        """
        if not isinstance(node.func, astroid.Attribute):
            return

        # Look for properties. First, obtain
        # the lhs of the Getattr node and search the attribute
        # there. If that attribute is a property or a subclass of properties,
        # then most likely it's not callable.

        # TODO: since astroid doesn't understand descriptors very well
        # we will not handle them here, right now.

        expr = node.func.expr
        klass = safe_infer(expr)
        if (klass is None or klass is astroid.YES
                or not isinstance(klass, astroid.Instance)):
            return

        try:
            attrs = klass._proxied.getattr(node.func.attrname)
        except exceptions.NotFoundError:
            return

        for attr in attrs:
            if attr is astroid.YES:
                continue
            if not isinstance(attr, astroid.FunctionDef):
                continue

            # Decorated, see if it is decorated with a property.
            # Also, check the returns and see if they are callable.
            if decorated_with_property(attr):
                if all(return_node.callable()
                       for return_node in attr.infer_call_result(node)):
                    continue
                else:
                    self.add_message('not-callable',
                                     node=node,
                                     args=node.func.as_string())
                    break

    @check_messages(*(list(MSGS.keys())))
    def visit_call(self, node):
        """check that called functions/methods are inferred to callable objects,
        and that the arguments passed to the function match the parameters in
        the inferred function's definition
        """
        # Build the set of keyword arguments, checking for duplicate keywords,
        # and count the positional arguments.
        call_site = astroid.arguments.CallSite.from_call(node)
        num_positional_args = len(call_site.positional_arguments)
        keyword_args = list(call_site.keyword_arguments.keys())

        called = safe_infer(node.func)
        # only function, generator and object defining __call__ are allowed
        if called is not None and not called.callable():
            self.add_message('not-callable',
                             node=node,
                             args=node.func.as_string())

        self._check_uninferable_callfunc(node)

        try:
            called, implicit_args, callable_name = _determine_callable(called)
        except ValueError:
            # Any error occurred during determining the function type, most of
            # those errors are handled by different warnings.
            return
        num_positional_args += implicit_args
        if called.args.args is None:
            # Built-in functions have no argument information.
            return

        if len(called.argnames()) != len(set(called.argnames())):
            # Duplicate parameter name (see duplicate-argument).  We can't really
            # make sense of the function call in this case, so just return.
            return

        # Warn about duplicated keyword arguments, such as `f=24, **{'f': 24}`
        for keyword in call_site.duplicated_keywords:
            self.add_message('repeated-keyword', node=node, args=(keyword, ))

        if call_site.has_invalid_arguments() or call_site.has_invalid_keywords(
        ):
            # Can't make sense of this.
            return

        # Analyze the list of formal parameters.
        num_mandatory_parameters = len(called.args.args) - len(
            called.args.defaults)
        parameters = []
        parameter_name_to_index = {}
        for i, arg in enumerate(called.args.args):
            if isinstance(arg, astroid.Tuple):
                name = None
                # Don't store any parameter names within the tuple, since those
                # are not assignable from keyword arguments.
            else:
                assert isinstance(arg, astroid.AssignName)
                # This occurs with:
                #    def f( (a), (b) ): pass
                name = arg.name
                parameter_name_to_index[name] = i
            if i >= num_mandatory_parameters:
                defval = called.args.defaults[i - num_mandatory_parameters]
            else:
                defval = None
            parameters.append([(name, defval), False])

        kwparams = {}
        for i, arg in enumerate(called.args.kwonlyargs):
            if isinstance(arg, astroid.Keyword):
                name = arg.arg
            else:
                assert isinstance(arg, astroid.AssignName)
                name = arg.name
            kwparams[name] = [called.args.kw_defaults[i], False]

        # Match the supplied arguments against the function parameters.

        # 1. Match the positional arguments.
        for i in range(num_positional_args):
            if i < len(parameters):
                parameters[i][1] = True
            elif called.args.vararg is not None:
                # The remaining positional arguments get assigned to the *args
                # parameter.
                break
            else:
                # Too many positional arguments.
                self.add_message('too-many-function-args',
                                 node=node,
                                 args=(callable_name, ))
                break

        # 2. Match the keyword arguments.
        for keyword in keyword_args:
            if keyword in parameter_name_to_index:
                i = parameter_name_to_index[keyword]
                if parameters[i][1]:
                    # Duplicate definition of function parameter.

                    # Might be too hardcoded, but this can actually
                    # happen when using str.format and `self` is passed
                    # by keyword argument, as in `.format(self=self)`.
                    # It's perfectly valid to so, so we're just skipping
                    # it if that's the case.
                    if not (keyword == 'self'
                            and called.qname() == STR_FORMAT):
                        self.add_message('redundant-keyword-arg',
                                         node=node,
                                         args=(keyword, callable_name))
                else:
                    parameters[i][1] = True
            elif keyword in kwparams:
                if kwparams[keyword][1]:  # XXX is that even possible?
                    # Duplicate definition of function parameter.
                    self.add_message('redundant-keyword-arg',
                                     node=node,
                                     args=(keyword, callable_name))
                else:
                    kwparams[keyword][1] = True
            elif called.args.kwarg is not None:
                # The keyword argument gets assigned to the **kwargs parameter.
                pass
            else:
                # Unexpected keyword argument.
                self.add_message('unexpected-keyword-arg',
                                 node=node,
                                 args=(keyword, callable_name))

        # 3. Match the **kwargs, if any.
        if node.kwargs:
            for i, [(name, defval), assigned] in enumerate(parameters):
                # Assume that *kwargs provides values for all remaining
                # unassigned named parameters.
                if name is not None:
                    parameters[i][1] = True
                else:
                    # **kwargs can't assign to tuples.
                    pass

        # Check that any parameters without a default have been assigned
        # values.
        for [(name, defval), assigned] in parameters:
            if (defval is None) and not assigned:
                if name is None:
                    display_name = '<tuple>'
                else:
                    display_name = repr(name)
                self.add_message('no-value-for-parameter',
                                 node=node,
                                 args=(display_name, callable_name))

        for name in kwparams:
            defval, assigned = kwparams[name]
            if defval is None and not assigned:
                self.add_message('missing-kwoa',
                                 node=node,
                                 args=(name, callable_name))

    @check_messages('invalid-sequence-index')
    def visit_extslice(self, node):
        # Check extended slice objects as if they were used as a sequence
        # index to check if the object being sliced can support them
        return self.visit_index(node)

    @check_messages('invalid-sequence-index')
    def visit_index(self, node):
        if not node.parent or not hasattr(node.parent, "value"):
            return
        # Look for index operations where the parent is a sequence type.
        # If the types can be determined, only allow indices to be int,
        # slice or instances with __index__.
        parent_type = safe_infer(node.parent.value)
        if not isinstance(parent_type, (astroid.ClassDef, astroid.Instance)):
            return

        # Determine what method on the parent this index will use
        # The parent of this node will be a Subscript, and the parent of that
        # node determines if the Subscript is a get, set, or delete operation.
        operation = node.parent.parent
        if isinstance(operation, astroid.Assign):
            methodname = '__setitem__'
        elif isinstance(operation, astroid.Delete):
            methodname = '__delitem__'
        else:
            methodname = '__getitem__'

        # Check if this instance's __getitem__, __setitem__, or __delitem__, as
        # appropriate to the statement, is implemented in a builtin sequence
        # type. This way we catch subclasses of sequence types but skip classes
        # that override __getitem__ and which may allow non-integer indices.
        try:
            methods = parent_type.getattr(methodname)
            if methods is astroid.YES:
                return
            itemmethod = methods[0]
        except (exceptions.NotFoundError, IndexError):
            return
        if not isinstance(itemmethod, astroid.FunctionDef):
            return
        if itemmethod.root().name != BUILTINS:
            return
        if not itemmethod.parent:
            return
        if itemmethod.parent.name not in SEQUENCE_TYPES:
            return

        # For ExtSlice objects coming from visit_extslice, no further
        # inference is necessary, since if we got this far the ExtSlice
        # is an error.
        if isinstance(node, astroid.ExtSlice):
            index_type = node
        else:
            index_type = safe_infer(node)
        if index_type is None or index_type is astroid.YES:
            return
        # Constants must be of type int
        if isinstance(index_type, astroid.Const):
            if isinstance(index_type.value, int):
                return
        # Instance values must be int, slice, or have an __index__ method
        elif isinstance(index_type, astroid.Instance):
            if index_type.pytype() in (BUILTINS + '.int', BUILTINS + '.slice'):
                return
            try:
                index_type.getattr('__index__')
                return
            except exceptions.NotFoundError:
                pass
        elif isinstance(index_type, astroid.Slice):
            # Delegate to visit_slice. A slice can be present
            # here after inferring the index node, which could
            # be a `slice(...)` call for instance.
            return self.visit_slice(index_type)

        # Anything else is an error
        self.add_message('invalid-sequence-index', node=node)

    @check_messages('invalid-slice-index')
    def visit_slice(self, node):
        # Check the type of each part of the slice
        for index in (node.lower, node.upper, node.step):
            if index is None:
                continue

            index_type = safe_infer(index)
            if index_type is None or index_type is astroid.YES:
                continue

            # Constants must of type int or None
            if isinstance(index_type, astroid.Const):
                if isinstance(index_type.value, (int, type(None))):
                    continue
            # Instance values must be of type int, None or an object
            # with __index__
            elif isinstance(index_type, astroid.Instance):
                if index_type.pytype() in (BUILTINS + '.int',
                                           BUILTINS + '.NoneType'):
                    continue

                try:
                    index_type.getattr('__index__')
                    return
                except exceptions.NotFoundError:
                    pass

            # Anything else is an error
            self.add_message('invalid-slice-index', node=node)

    @check_messages('not-context-manager')
    def visit_with(self, node):
        for ctx_mgr, _ in node.items:
            context = astroid.context.InferenceContext()
            infered = safe_infer(ctx_mgr, context=context)
            if infered is None or infered is astroid.YES:
                continue

            if isinstance(infered, bases.Generator):
                # Check if we are dealing with a function decorated
                # with contextlib.contextmanager.
                if decorated_with(infered.parent,
                                  ['contextlib.contextmanager']):
                    continue
                # If the parent of the generator is not the context manager itself,
                # that means that it could have been returned from another
                # function which was the real context manager.
                # The following approach is more of a hack rather than a real
                # solution: walk all the inferred statements for the
                # given *ctx_mgr* and if you find one function scope
                # which is decorated, consider it to be the real
                # manager and give up, otherwise emit not-context-manager.
                # See the test file for not_context_manager for a couple
                # of self explaining tests.
                for path in six.moves.filter(None, _unflatten(context.path)):
                    scope = path.scope()
                    if not isinstance(scope, astroid.FunctionDef):
                        continue
                    if decorated_with(scope, ['contextlib.contextmanager']):
                        break
                else:
                    self.add_message('not-context-manager',
                                     node=node,
                                     args=(infered.name, ))
            else:
                try:
                    infered.getattr('__enter__')
                    infered.getattr('__exit__')
                except exceptions.NotFoundError:
                    if isinstance(infered, astroid.Instance):
                        # If we do not know the bases of this class,
                        # just skip it.
                        if not has_known_bases(infered):
                            continue
                        # Just ignore mixin classes.
                        if self.config.ignore_mixin_members:
                            if infered.name[-5:].lower() == 'mixin':
                                continue

                    self.add_message('not-context-manager',
                                     node=node,
                                     args=(infered.name, ))

    # Disabled until we'll have a more capable astroid.
    @check_messages('invalid-unary-operand-type')
    def _visit_unaryop(self, node):
        """Detect TypeErrors for unary operands."""

        for error in node.type_errors():
            # Let the error customize its output.
            self.add_message('invalid-unary-operand-type',
                             args=str(error),
                             node=node)

    @check_messages('unsupported-binary-operation')
    def _visit_binop(self, node):
        """Detect TypeErrors for binary arithmetic operands."""
        self._check_binop_errors(node)

    @check_messages('unsupported-binary-operation')
    def _visit_augassign(self, node):
        """Detect TypeErrors for augmented binary arithmetic operands."""
        self._check_binop_errors(node)

    def _check_binop_errors(self, node):
        for error in node.type_errors():
            # Let the error customize its output.
            self.add_message('unsupported-binary-operation',
                             args=str(error),
                             node=node)

    def _check_membership_test(self, node):
        if is_inside_abstract_class(node):
            return
        if is_comprehension(node):
            return
        infered = safe_infer(node)
        if infered is None or infered is astroid.YES:
            return
        if not supports_membership_test(infered):
            self.add_message('unsupported-membership-test',
                             args=node.as_string(),
                             node=node)

    @check_messages('unsupported-membership-test')
    def visit_compare(self, node):
        if len(node.ops) != 1:
            return
        operator, right = node.ops[0]
        if operator in ['in', 'not in']:
            self._check_membership_test(right)

    @check_messages('unsubscriptable-object')
    def visit_subscript(self, node):
        if isinstance(node.value, (astroid.ListComp, astroid.DictComp)):
            return
        if isinstance(node.value, astroid.SetComp):
            self.add_message('unsubscriptable-object',
                             args=node.value.as_string(),
                             node=node.value)
            return

        infered = safe_infer(node.value)
        if infered is None or infered is astroid.YES:
            return

        if is_inside_abstract_class(node):
            return

        if not supports_subscript(infered):
            self.add_message('unsubscriptable-object',
                             args=node.value.as_string(),
                             node=node.value)