| Index: third_party/pylint/checkers/typecheck.py
 | 
| diff --git a/third_party/pylint/checkers/typecheck.py b/third_party/pylint/checkers/typecheck.py
 | 
| index fb8f3c6e769c5217992bb7d82e4e5fb2d0dd1368..10b9f8669fb9353ab9e4766e5174a6ef5a257078 100644
 | 
| --- a/third_party/pylint/checkers/typecheck.py
 | 
| +++ b/third_party/pylint/checkers/typecheck.py
 | 
| @@ -23,23 +23,21 @@ import astroid
 | 
|  from astroid import InferenceError, NotFoundError, YES, Instance
 | 
|  from astroid.bases import BUILTINS
 | 
|  
 | 
| -from pylint.interfaces import IAstroidChecker
 | 
| +from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE
 | 
|  from pylint.checkers import BaseChecker
 | 
| -from pylint.checkers.utils import safe_infer, is_super, check_messages
 | 
| +from pylint.checkers.utils import (
 | 
| +    safe_infer, is_super,
 | 
| +    check_messages, decorated_with_property)
 | 
|  
 | 
|  MSGS = {
 | 
|      'E1101': ('%s %r has no %r member',
 | 
|                'no-member',
 | 
| -              'Used when a variable is accessed for an unexistent member.'),
 | 
| +              'Used when a variable is accessed for an unexistent member.',
 | 
| +              {'old_names': [('E1103', 'maybe-no-member')]}),
 | 
|      'E1102': ('%s is not callable',
 | 
|                'not-callable',
 | 
|                'Used when an object being called has been inferred to a non \
 | 
|                callable object'),
 | 
| -    'E1103': ('%s %r has no %r member (but some types could not be inferred)',
 | 
| -              'maybe-no-member',
 | 
| -              'Used when a variable is accessed for an unexistent member, but \
 | 
| -              astroid was not able to interpret all possible types of this \
 | 
| -              variable.'),
 | 
|      'E1111': ('Assigning to function call which doesn\'t return',
 | 
|                'assignment-from-no-return',
 | 
|                'Used when an assignment is done on a function call but the \
 | 
| @@ -56,11 +54,6 @@ MSGS = {
 | 
|                'too-many-function-args',
 | 
|                'Used when a function call passes too many positional \
 | 
|                arguments.'),
 | 
| -    'E1122': ('Duplicate keyword argument %r in %s call',
 | 
| -              'duplicate-keyword-arg',
 | 
| -              'Used when a function call passes the same keyword argument \
 | 
| -              multiple times.',
 | 
| -              {'maxversion': (2, 6)}),
 | 
|      'E1123': ('Unexpected keyword argument %r in %s call',
 | 
|                'unexpected-keyword-arg',
 | 
|                'Used when a function call passes a keyword argument that \
 | 
| @@ -192,7 +185,7 @@ accessed. Python regular expressions are accepted.'}
 | 
|      def visit_delattr(self, node):
 | 
|          self.visit_getattr(node)
 | 
|  
 | 
| -    @check_messages('no-member', 'maybe-no-member')
 | 
| +    @check_messages('no-member')
 | 
|      def visit_getattr(self, node):
 | 
|          """check that the accessed attribute exists
 | 
|  
 | 
| @@ -254,6 +247,20 @@ accessed. Python regular expressions are accepted.'}
 | 
|                  # explicit skipping of module member access
 | 
|                  if owner.root().name in self.config.ignored_modules:
 | 
|                      continue
 | 
| +                if isinstance(owner, astroid.Class):
 | 
| +                    # Look up in the metaclass only if the owner is itself
 | 
| +                    # a class.
 | 
| +                    # TODO: getattr doesn't return by default members
 | 
| +                    # from the metaclass, because handling various cases
 | 
| +                    # of methods accessible from the metaclass itself
 | 
| +                    # and/or subclasses only is too complicated for little to
 | 
| +                    # no benefit.
 | 
| +                    metaclass = owner.metaclass()
 | 
| +                    try:
 | 
| +                        if metaclass and metaclass.getattr(node.attrname):
 | 
| +                            continue
 | 
| +                    except NotFoundError:
 | 
| +                        pass
 | 
|                  missingattr.add((owner, name))
 | 
|                  continue
 | 
|              # stop on the first found
 | 
| @@ -270,13 +277,11 @@ accessed. Python regular expressions are accepted.'}
 | 
|                  if actual in done:
 | 
|                      continue
 | 
|                  done.add(actual)
 | 
| -                if inference_failure:
 | 
| -                    msgid = 'maybe-no-member'
 | 
| -                else:
 | 
| -                    msgid = 'no-member'
 | 
| -                self.add_message(msgid, node=node,
 | 
| +                confidence = INFERENCE if not inference_failure else INFERENCE_FAILURE
 | 
| +                self.add_message('no-member', node=node,
 | 
|                                   args=(owner.display_type(), name,
 | 
| -                                       node.attrname))
 | 
| +                                       node.attrname),
 | 
| +                                 confidence=confidence)
 | 
|  
 | 
|      @check_messages('assignment-from-no-return', 'assignment-from-none')
 | 
|      def visit_assign(self, node):
 | 
| @@ -333,43 +338,17 @@ accessed. Python regular expressions are accepted.'}
 | 
|          except astroid.NotFoundError:
 | 
|              return
 | 
|  
 | 
| -        stop_checking = False
 | 
|          for attr in attrs:
 | 
|              if attr is astroid.YES:
 | 
|                  continue
 | 
| -            if stop_checking:
 | 
| -                break
 | 
|              if not isinstance(attr, astroid.Function):
 | 
|                  continue
 | 
|  
 | 
|              # Decorated, see if it is decorated with a property
 | 
| -            if not attr.decorators:
 | 
| -                continue
 | 
| -            for decorator in attr.decorators.nodes:
 | 
| -                if not isinstance(decorator, astroid.Name):
 | 
| -                    continue
 | 
| -                try:
 | 
| -                    for infered in decorator.infer():
 | 
| -                        property_like = False
 | 
| -                        if isinstance(infered, astroid.Class):
 | 
| -                            if (infered.root().name == BUILTINS and
 | 
| -                                    infered.name == 'property'):
 | 
| -                                property_like = True
 | 
| -                            else:
 | 
| -                                for ancestor in infered.ancestors():
 | 
| -                                    if (ancestor.name == 'property' and
 | 
| -                                            ancestor.root().name == BUILTINS):
 | 
| -                                        property_like = True
 | 
| -                                        break
 | 
| -                            if property_like:
 | 
| -                                self.add_message('not-callable', node=node,
 | 
| -                                                 args=node.func.as_string())
 | 
| -                                stop_checking = True
 | 
| -                                break
 | 
| -                except InferenceError:
 | 
| -                    pass
 | 
| -                if stop_checking:
 | 
| -                    break
 | 
| +            if decorated_with_property(attr):
 | 
| +                self.add_message('not-callable', node=node,
 | 
| +                                 args=node.func.as_string())
 | 
| +                break
 | 
|  
 | 
|      @check_messages(*(list(MSGS.keys())))
 | 
|      def visit_callfunc(self, node):
 | 
| @@ -383,11 +362,7 @@ accessed. Python regular expressions are accepted.'}
 | 
|          num_positional_args = 0
 | 
|          for arg in node.args:
 | 
|              if isinstance(arg, astroid.Keyword):
 | 
| -                keyword = arg.arg
 | 
| -                if keyword in keyword_args:
 | 
| -                    self.add_message('duplicate-keyword-arg', node=node,
 | 
| -                                     args=(keyword, 'function'))
 | 
| -                keyword_args.add(keyword)
 | 
| +                keyword_args.add(arg.arg)
 | 
|              else:
 | 
|                  num_positional_args += 1
 | 
|  
 | 
| @@ -549,7 +524,6 @@ accessed. Python regular expressions are accepted.'}
 | 
|          # slice or instances with __index__.
 | 
|  
 | 
|          parent_type = safe_infer(node.parent.value)
 | 
| -
 | 
|          if not isinstance(parent_type, (astroid.Class, astroid.Instance)):
 | 
|              return
 | 
|  
 | 
| @@ -578,13 +552,10 @@ accessed. Python regular expressions are accepted.'}
 | 
|  
 | 
|          if not isinstance(itemmethod, astroid.Function):
 | 
|              return
 | 
| -
 | 
|          if itemmethod.root().name != BUILTINS:
 | 
|              return
 | 
| -
 | 
|          if not itemmethod.parent:
 | 
|              return
 | 
| -
 | 
|          if itemmethod.parent.name not in SEQUENCE_TYPES:
 | 
|              return
 | 
|  
 | 
| @@ -595,7 +566,6 @@ accessed. Python regular expressions are accepted.'}
 | 
|              index_type = node
 | 
|          else:
 | 
|              index_type = safe_infer(node)
 | 
| -
 | 
|          if index_type is None or index_type is astroid.YES:
 | 
|              return
 | 
|  
 | 
| @@ -607,7 +577,6 @@ accessed. Python regular expressions are accepted.'}
 | 
|          elif isinstance(index_type, astroid.Instance):
 | 
|              if index_type.pytype() in (BUILTINS + '.int', BUILTINS + '.slice'):
 | 
|                  return
 | 
| -
 | 
|              try:
 | 
|                  index_type.getattr('__index__')
 | 
|                  return
 | 
| @@ -625,7 +594,6 @@ accessed. Python regular expressions are accepted.'}
 | 
|                  continue
 | 
|  
 | 
|              index_type = safe_infer(index)
 | 
| -
 | 
|              if index_type is None or index_type is astroid.YES:
 | 
|                  continue
 | 
|  
 | 
| 
 |