| Index: third_party/pylint/checkers/base.py
|
| diff --git a/third_party/pylint/checkers/base.py b/third_party/pylint/checkers/base.py
|
| index 8198d1647f93a7a4ef0e00dd4c03fe6f5d36f917..750d661f669e0597bc8534e757643ef9f1fdcfd2 100644
|
| --- a/third_party/pylint/checkers/base.py
|
| +++ b/third_party/pylint/checkers/base.py
|
| @@ -45,6 +45,7 @@ from pylint.checkers.utils import (
|
| has_known_bases,
|
| NoSuchArgumentError,
|
| is_import_error,
|
| + unimplemented_abstract_methods,
|
| )
|
|
|
|
|
| @@ -148,8 +149,7 @@ if sys.version_info < (3, 0):
|
| PROPERTY_CLASSES = set(('__builtin__.property', 'abc.abstractproperty'))
|
| else:
|
| PROPERTY_CLASSES = set(('builtins.property', 'abc.abstractproperty'))
|
| -ABC_METHODS = set(('abc.abstractproperty', 'abc.abstractmethod',
|
| - 'abc.abstractclassmethod', 'abc.abstractstaticmethod'))
|
| +
|
|
|
| def _determine_function_name_type(node):
|
| """Determine the name type whose regex the a function's name should match.
|
| @@ -179,26 +179,17 @@ def _determine_function_name_type(node):
|
| return 'attr'
|
| return 'method'
|
|
|
| -def decorated_with_abc(func):
|
| - """ Determine if the `func` node is decorated
|
| - with `abc` decorators (abstractmethod et co.)
|
| - """
|
| - if func.decorators:
|
| - for node in func.decorators.nodes:
|
| - try:
|
| - infered = next(node.infer())
|
| - except InferenceError:
|
| - continue
|
| - if infered and infered.qname() in ABC_METHODS:
|
| - return True
|
|
|
| -def has_abstract_methods(node):
|
| +
|
| +def _has_abstract_methods(node):
|
| """
|
| - Determine if the given `node` has
|
| - abstract methods, defined with `abc` module.
|
| + Determine if the given `node` has abstract methods.
|
| +
|
| + The methods should be made abstract by decorating them
|
| + with `abc` decorators.
|
| """
|
| - return any(decorated_with_abc(meth)
|
| - for meth in node.methods())
|
| + return len(unimplemented_abstract_methods(node)) > 0
|
| +
|
|
|
| def report_by_type_stats(sect, stats, old_stats):
|
| """make a report of
|
| @@ -298,7 +289,7 @@ class BasicErrorChecker(_BasicChecker):
|
| 'duplicate-argument-name',
|
| 'Duplicate argument names in function definitions are syntax'
|
| ' errors.'),
|
| - 'E0110': ('Abstract class with abstract methods instantiated',
|
| + 'E0110': ('Abstract class %r with abstract methods instantiated',
|
| 'abstract-class-instantiated',
|
| 'Used when an abstract class with `abc.ABCMeta` as metaclass '
|
| 'has abstract methods and is instantiated.'),
|
| @@ -398,17 +389,21 @@ class BasicErrorChecker(_BasicChecker):
|
| return
|
| # __init__ was called
|
| metaclass = infered.metaclass()
|
| - abstract_methods = has_abstract_methods(infered)
|
| + abstract_methods = _has_abstract_methods(infered)
|
| if metaclass is None:
|
| # Python 3.4 has `abc.ABC`, which won't be detected
|
| # by ClassNode.metaclass()
|
| for ancestor in infered.ancestors():
|
| if ancestor.qname() == 'abc.ABC' and abstract_methods:
|
| - self.add_message('abstract-class-instantiated', node=node)
|
| + self.add_message('abstract-class-instantiated',
|
| + args=(infered.name, ),
|
| + node=node)
|
| break
|
| return
|
| if metaclass.qname() == 'abc.ABCMeta' and abstract_methods:
|
| - self.add_message('abstract-class-instantiated', node=node)
|
| + self.add_message('abstract-class-instantiated',
|
| + args=(infered.name, ),
|
| + node=node)
|
|
|
| def _check_else_on_loop(self, node):
|
| """Check that any loop with an else clause has a break statement."""
|
| @@ -676,7 +671,13 @@ functions, methods
|
| variable names, max locals
|
| """
|
| self.stats[node.is_method() and 'method' or 'function'] += 1
|
| + self._check_dangerous_default(node)
|
| +
|
| + def _check_dangerous_default(self, node):
|
| # check for dangerous default values as arguments
|
| + is_iterable = lambda n: isinstance(n, (astroid.List,
|
| + astroid.Set,
|
| + astroid.Dict))
|
| for default in node.args.defaults:
|
| try:
|
| value = next(default.infer())
|
| @@ -685,21 +686,30 @@ functions, methods
|
|
|
| if (isinstance(value, astroid.Instance) and
|
| value.qname() in DEFAULT_ARGUMENT_SYMBOLS):
|
| +
|
| if value is default:
|
| msg = DEFAULT_ARGUMENT_SYMBOLS[value.qname()]
|
| - elif type(value) is astroid.Instance:
|
| - if isinstance(default, astroid.CallFunc):
|
| - # this argument is direct call to list() or dict() etc
|
| + elif type(value) is astroid.Instance or is_iterable(value):
|
| + # We are here in the following situation(s):
|
| + # * a dict/set/list/tuple call which wasn't inferred
|
| + # to a syntax node ({}, () etc.). This can happen
|
| + # when the arguments are invalid or unknown to
|
| + # the inference.
|
| + # * a variable from somewhere else, which turns out to be a list
|
| + # or a dict.
|
| + if is_iterable(default):
|
| + msg = value.pytype()
|
| + elif isinstance(default, astroid.CallFunc):
|
| msg = '%s() (%s)' % (value.name, value.qname())
|
| else:
|
| - # this argument is a variable from somewhere else which turns
|
| - # out to be a list or dict
|
| msg = '%s (%s)' % (default.as_string(), value.qname())
|
| else:
|
| # this argument is a name
|
| msg = '%s (%s)' % (default.as_string(),
|
| DEFAULT_ARGUMENT_SYMBOLS[value.qname()])
|
| - self.add_message('dangerous-default-value', node=node, args=(msg,))
|
| + self.add_message('dangerous-default-value',
|
| + node=node,
|
| + args=(msg, ))
|
|
|
| @check_messages('unreachable', 'lost-exception')
|
| def visit_return(self, node):
|
|
|