| OLD | NEW |
| 1 # Copyright (c) 2006-2013 LOGILAB S.A. (Paris, FRANCE). | 1 # Copyright (c) 2006-2010 LOGILAB S.A. (Paris, FRANCE). |
| 2 # http://www.logilab.fr/ -- mailto:contact@logilab.fr | 2 # http://www.logilab.fr/ -- mailto:contact@logilab.fr |
| 3 # | 3 # |
| 4 # This program is free software; you can redistribute it and/or modify it under | 4 # This program is free software; you can redistribute it and/or modify it under |
| 5 # the terms of the GNU General Public License as published by the Free Software | 5 # the terms of the GNU General Public License as published by the Free Software |
| 6 # Foundation; either version 2 of the License, or (at your option) any later | 6 # Foundation; either version 2 of the License, or (at your option) any later |
| 7 # version. | 7 # version. |
| 8 # | 8 # |
| 9 # This program is distributed in the hope that it will be useful, but WITHOUT | 9 # This program is distributed in the hope that it will be useful, but WITHOUT |
| 10 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | 10 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| 11 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. | 11 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
| 12 # | 12 # |
| 13 # You should have received a copy of the GNU General Public License along with | 13 # You should have received a copy of the GNU General Public License along with |
| 14 # this program; if not, write to the Free Software Foundation, Inc., | 14 # this program; if not, write to the Free Software Foundation, Inc., |
| 15 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | 15 # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| 16 """try to find more bugs in the code using astroid inference capabilities | 16 """try to find more bugs in the code using astng inference capabilities |
| 17 """ | 17 """ |
| 18 | 18 |
| 19 import re | 19 import re |
| 20 import shlex | 20 import shlex |
| 21 | 21 |
| 22 import astroid | 22 from logilab import astng |
| 23 from astroid import InferenceError, NotFoundError, YES, Instance | 23 from logilab.astng import InferenceError, NotFoundError, YES, Instance |
| 24 from astroid.bases import BUILTINS | |
| 25 | 24 |
| 26 from pylint.interfaces import IAstroidChecker | 25 from pylint.interfaces import IASTNGChecker |
| 27 from pylint.checkers import BaseChecker | 26 from pylint.checkers import BaseChecker |
| 28 from pylint.checkers.utils import safe_infer, is_super, check_messages | 27 from pylint.checkers.utils import safe_infer, is_super, check_messages |
| 29 | 28 |
| 30 MSGS = { | 29 MSGS = { |
| 31 'E1101': ('%s %r has no %r member', | 30 'E1101': ('%s %r has no %r member', |
| 32 'no-member', | |
| 33 'Used when a variable is accessed for an unexistent member.'), | 31 'Used when a variable is accessed for an unexistent member.'), |
| 34 'E1102': ('%s is not callable', | 32 'E1102': ('%s is not callable', |
| 35 'not-callable', | |
| 36 'Used when an object being called has been inferred to a non \ | 33 'Used when an object being called has been inferred to a non \ |
| 37 callable object'), | 34 callable object'), |
| 38 'E1103': ('%s %r has no %r member (but some types could not be inferred)', | 35 'E1103': ('%s %r has no %r member (but some types could not be inferred)', |
| 39 'maybe-no-member', | |
| 40 'Used when a variable is accessed for an unexistent member, but \ | 36 'Used when a variable is accessed for an unexistent member, but \ |
| 41 astroid was not able to interpret all possible types of this \ | 37 astng was not able to interpret all possible types of this \ |
| 42 variable.'), | 38 variable.'), |
| 43 'E1111': ('Assigning to function call which doesn\'t return', | 39 'E1111': ('Assigning to function call which doesn\'t return', |
| 44 'assignment-from-no-return', | |
| 45 'Used when an assignment is done on a function call but the \ | 40 'Used when an assignment is done on a function call but the \ |
| 46 inferred function doesn\'t return anything.'), | 41 inferred function doesn\'t return anything.'), |
| 47 'W1111': ('Assigning to function call which only returns None', | 42 'W1111': ('Assigning to function call which only returns None', |
| 48 'assignment-from-none', | |
| 49 'Used when an assignment is done on a function call but the \ | 43 'Used when an assignment is done on a function call but the \ |
| 50 inferred function returns nothing but None.'), | 44 inferred function returns nothing but None.'), |
| 51 | 45 |
| 52 'E1120': ('No value for argument %s in %s call', | 46 'E1120': ('No value passed for parameter %s in function call', |
| 53 'no-value-for-parameter', | |
| 54 'Used when a function call passes too few arguments.'), | 47 'Used when a function call passes too few arguments.'), |
| 55 'E1121': ('Too many positional arguments for %s call', | 48 'E1121': ('Too many positional arguments for function call', |
| 56 'too-many-function-args', | |
| 57 'Used when a function call passes too many positional \ | 49 'Used when a function call passes too many positional \ |
| 58 arguments.'), | 50 arguments.'), |
| 59 'E1122': ('Duplicate keyword argument %r in %s call', | 51 'E1122': ('Duplicate keyword argument %r in function call', |
| 60 'duplicate-keyword-arg', | |
| 61 'Used when a function call passes the same keyword argument \ | 52 'Used when a function call passes the same keyword argument \ |
| 62 multiple times.', | 53 multiple times.'), |
| 63 {'maxversion': (2, 6)}), | 54 'E1123': ('Passing unexpected keyword argument %r in function call', |
| 64 'E1123': ('Unexpected keyword argument %r in %s call', | |
| 65 'unexpected-keyword-arg', | |
| 66 'Used when a function call passes a keyword argument that \ | 55 'Used when a function call passes a keyword argument that \ |
| 67 doesn\'t correspond to one of the function\'s parameter names.'), | 56 doesn\'t correspond to one of the function\'s parameter names.'), |
| 68 'E1124': ('Argument %r passed by position and keyword in %s call', | 57 'E1124': ('Multiple values passed for parameter %r in function call', |
| 69 'redundant-keyword-arg', | |
| 70 'Used when a function call would result in assigning multiple \ | 58 'Used when a function call would result in assigning multiple \ |
| 71 values to a function parameter, one value from a positional \ | 59 values to a function parameter, one value from a positional \ |
| 72 argument and one from a keyword argument.'), | 60 argument and one from a keyword argument.'), |
| 73 'E1125': ('Missing mandatory keyword argument %r in %s call', | |
| 74 'missing-kwoa', | |
| 75 ('Used when a function call does not pass a mandatory' | |
| 76 ' keyword-only argument.'), | |
| 77 {'minversion': (3, 0)}), | |
| 78 'E1126': ('Sequence index is not an int, slice, or instance with __index__', | |
| 79 'invalid-sequence-index', | |
| 80 'Used when a sequence type is indexed with an invalid type. ' | |
| 81 'Valid types are ints, slices, and objects with an __index__ ' | |
| 82 'method.'), | |
| 83 'E1127': ('Slice index is not an int, None, or instance with __index__', | |
| 84 'invalid-slice-index', | |
| 85 'Used when a slice index is not an integer, None, or an object \ | |
| 86 with an __index__ method.'), | |
| 87 } | 61 } |
| 88 | 62 |
| 89 # builtin sequence types in Python 2 and 3. | |
| 90 SEQUENCE_TYPES = set(['str', 'unicode', 'list', 'tuple', 'bytearray', | |
| 91 'xrange', 'range', 'bytes', 'memoryview']) | |
| 92 | |
| 93 def _determine_callable(callable_obj): | |
| 94 # Ordering is important, since BoundMethod is a subclass of UnboundMethod, | |
| 95 # and Function inherits Lambda. | |
| 96 if isinstance(callable_obj, astroid.BoundMethod): | |
| 97 # Bound methods have an extra implicit 'self' argument. | |
| 98 return callable_obj, 1, callable_obj.type | |
| 99 elif isinstance(callable_obj, astroid.UnboundMethod): | |
| 100 return callable_obj, 0, 'unbound method' | |
| 101 elif isinstance(callable_obj, astroid.Function): | |
| 102 return callable_obj, 0, callable_obj.type | |
| 103 elif isinstance(callable_obj, astroid.Lambda): | |
| 104 return callable_obj, 0, 'lambda' | |
| 105 elif isinstance(callable_obj, astroid.Class): | |
| 106 # Class instantiation, lookup __new__ instead. | |
| 107 # If we only find object.__new__, we can safely check __init__ | |
| 108 # instead. | |
| 109 try: | |
| 110 # Use the last definition of __new__. | |
| 111 new = callable_obj.local_attr('__new__')[-1] | |
| 112 except astroid.NotFoundError: | |
| 113 new = None | |
| 114 | |
| 115 if not new or new.parent.scope().name == 'object': | |
| 116 try: | |
| 117 # Use the last definition of __init__. | |
| 118 callable_obj = callable_obj.local_attr('__init__')[-1] | |
| 119 except astroid.NotFoundError: | |
| 120 # do nothing, covered by no-init. | |
| 121 raise ValueError | |
| 122 else: | |
| 123 callable_obj = new | |
| 124 | |
| 125 if not isinstance(callable_obj, astroid.Function): | |
| 126 raise ValueError | |
| 127 # both have an extra implicit 'cls'/'self' argument. | |
| 128 return callable_obj, 1, 'constructor' | |
| 129 else: | |
| 130 raise ValueError | |
| 131 | |
| 132 class TypeChecker(BaseChecker): | 63 class TypeChecker(BaseChecker): |
| 133 """try to find bugs in the code using type inference | 64 """try to find bugs in the code using type inference |
| 134 """ | 65 """ |
| 135 | 66 |
| 136 __implements__ = (IAstroidChecker,) | 67 __implements__ = (IASTNGChecker,) |
| 137 | 68 |
| 138 # configuration section name | 69 # configuration section name |
| 139 name = 'typecheck' | 70 name = 'typecheck' |
| 140 # messages | 71 # messages |
| 141 msgs = MSGS | 72 msgs = MSGS |
| 142 priority = -1 | 73 priority = -1 |
| 143 # configuration options | 74 # configuration options |
| 144 options = (('ignore-mixin-members', | 75 options = (('ignore-mixin-members', |
| 145 {'default' : True, 'type' : 'yn', 'metavar': '<y_or_n>', | 76 {'default' : True, 'type' : 'yn', 'metavar': '<y_or_n>', |
| 146 'help' : 'Tells whether missing members accessed in mixin \ | 77 'help' : 'Tells whether missing members accessed in mixin \ |
| 147 class should be ignored. A mixin class is detected if its name ends with \ | 78 class should be ignored. A mixin class is detected if its name ends with \ |
| 148 "mixin" (case insensitive).'} | 79 "mixin" (case insensitive).'} |
| 149 ), | 80 ), |
| 150 ('ignored-modules', | 81 |
| 151 {'default': (), | |
| 152 'type': 'csv', | |
| 153 'metavar': '<module names>', | |
| 154 'help': 'List of module names for which member attributes \ | |
| 155 should not be checked (useful for modules/projects where namespaces are \ | |
| 156 manipulated during runtime and thus existing member attributes cannot be \ | |
| 157 deduced by static analysis'}, | |
| 158 ), | |
| 159 ('ignored-classes', | 82 ('ignored-classes', |
| 160 {'default' : ('SQLObject',), | 83 {'default' : ('SQLObject',), |
| 161 'type' : 'csv', | 84 'type' : 'csv', |
| 162 'metavar' : '<members names>', | 85 'metavar' : '<members names>', |
| 163 'help' : 'List of classes names for which member attributes \ | 86 'help' : 'List of classes names for which member attributes \ |
| 164 should not be checked (useful for classes with attributes dynamically set).'} | 87 should not be checked (useful for classes with attributes dynamically set).'} |
| 165 ), | 88 ), |
| 166 | 89 |
| 167 ('zope', | 90 ('zope', |
| 168 {'default' : False, 'type' : 'yn', 'metavar': '<y_or_n>', | 91 {'default' : False, 'type' : 'yn', 'metavar': '<y_or_n>', |
| 169 'help' : 'When zope mode is activated, add a predefined set \ | 92 'help' : 'When zope mode is activated, add a predefined set \ |
| 170 of Zope acquired attributes to generated-members.'} | 93 of Zope acquired attributes to generated-members.'} |
| 171 ), | 94 ), |
| 172 ('generated-members', | 95 ('generated-members', |
| 173 {'default' : ('REQUEST', 'acl_users', 'aq_parent'), | 96 {'default' : ( |
| 97 'REQUEST', 'acl_users', 'aq_parent'), |
| 174 'type' : 'string', | 98 'type' : 'string', |
| 175 'metavar' : '<members names>', | 99 'metavar' : '<members names>', |
| 176 'help' : 'List of members which are set dynamically and \ | 100 'help' : 'List of members which are set dynamically and \ |
| 177 missed by pylint inference system, and so shouldn\'t trigger E0201 when \ | 101 missed by pylint inference system, and so shouldn\'t trigger E0201 when \ |
| 178 accessed. Python regular expressions are accepted.'} | 102 accessed. Python regular expressions are accepted.'} |
| 179 ), | 103 ), |
| 180 ) | 104 ) |
| 181 | 105 |
| 182 def open(self): | 106 def open(self): |
| 183 # do this in open since config not fully initialized in __init__ | 107 # do this in open since config not fully initialized in __init__ |
| 184 self.generated_members = list(self.config.generated_members) | 108 self.generated_members = list(self.config.generated_members) |
| 185 if self.config.zope: | 109 if self.config.zope: |
| 186 self.generated_members.extend(('REQUEST', 'acl_users', 'aq_parent')) | 110 self.generated_members.extend(('REQUEST', 'acl_users', 'aq_parent')) |
| 187 | 111 |
| 188 def visit_assattr(self, node): | 112 def visit_assattr(self, node): |
| 189 if isinstance(node.ass_type(), astroid.AugAssign): | 113 if isinstance(node.ass_type(), astng.AugAssign): |
| 190 self.visit_getattr(node) | 114 self.visit_getattr(node) |
| 191 | 115 |
| 192 def visit_delattr(self, node): | 116 def visit_delattr(self, node): |
| 193 self.visit_getattr(node) | 117 self.visit_getattr(node) |
| 194 | 118 |
| 195 @check_messages('no-member', 'maybe-no-member') | 119 @check_messages('E1101', 'E1103') |
| 196 def visit_getattr(self, node): | 120 def visit_getattr(self, node): |
| 197 """check that the accessed attribute exists | 121 """check that the accessed attribute exists |
| 198 | 122 |
| 199 to avoid to much false positives for now, we'll consider the code as | 123 to avoid to much false positives for now, we'll consider the code as |
| 200 correct if a single of the inferred nodes has the accessed attribute. | 124 correct if a single of the inferred nodes has the accessed attribute. |
| 201 | 125 |
| 202 function/method, super call and metaclasses are ignored | 126 function/method, super call and metaclasses are ignored |
| 203 """ | 127 """ |
| 204 # generated_members may containt regular expressions | 128 # generated_members may containt regular expressions |
| 205 # (surrounded by quote `"` and followed by a comma `,`) | 129 # (surrounded by quote `"` and followed by a comma `,`) |
| 206 # REQUEST,aq_parent,"[a-zA-Z]+_set{1,2}"' => | 130 # REQUEST,aq_parent,"[a-zA-Z]+_set{1,2}"' => |
| 207 # ('REQUEST', 'aq_parent', '[a-zA-Z]+_set{1,2}') | 131 # ('REQUEST', 'aq_parent', '[a-zA-Z]+_set{1,2}') |
| 208 if isinstance(self.config.generated_members, str): | 132 if isinstance(self.config.generated_members, str): |
| 209 gen = shlex.shlex(self.config.generated_members) | 133 gen = shlex.shlex(self.config.generated_members) |
| 210 gen.whitespace += ',' | 134 gen.whitespace += ',' |
| 211 gen.wordchars += '[]-+' | |
| 212 self.config.generated_members = tuple(tok.strip('"') for tok in gen) | 135 self.config.generated_members = tuple(tok.strip('"') for tok in gen) |
| 213 for pattern in self.config.generated_members: | 136 for pattern in self.config.generated_members: |
| 214 # attribute is marked as generated, stop here | 137 # attribute is marked as generated, stop here |
| 215 if re.match(pattern, node.attrname): | 138 if re.match(pattern, node.attrname): |
| 216 return | 139 return |
| 217 try: | 140 try: |
| 218 infered = list(node.expr.infer()) | 141 infered = list(node.expr.infer()) |
| 219 except InferenceError: | 142 except InferenceError: |
| 220 return | 143 return |
| 221 # list of (node, nodename) which are missing the attribute | 144 # list of (node, nodename) which are missing the attribute |
| 222 missingattr = set() | 145 missingattr = set() |
| 223 ignoremim = self.config.ignore_mixin_members | 146 ignoremim = self.config.ignore_mixin_members |
| 224 inference_failure = False | 147 inference_failure = False |
| 225 for owner in infered: | 148 for owner in infered: |
| 226 # skip yes object | 149 # skip yes object |
| 227 if owner is YES: | 150 if owner is YES: |
| 228 inference_failure = True | 151 inference_failure = True |
| 229 continue | 152 continue |
| 230 # skip None anyway | 153 # skip None anyway |
| 231 if isinstance(owner, astroid.Const) and owner.value is None: | 154 if isinstance(owner, astng.Const) and owner.value is None: |
| 232 continue | 155 continue |
| 233 # XXX "super" / metaclass call | 156 # XXX "super" / metaclass call |
| 234 if is_super(owner) or getattr(owner, 'type', None) == 'metaclass': | 157 if is_super(owner) or getattr(owner, 'type', None) == 'metaclass': |
| 235 continue | 158 continue |
| 236 name = getattr(owner, 'name', 'None') | 159 name = getattr(owner, 'name', 'None') |
| 237 if name in self.config.ignored_classes: | 160 if name in self.config.ignored_classes: |
| 238 continue | 161 continue |
| 239 if ignoremim and name[-5:].lower() == 'mixin': | 162 if ignoremim and name[-5:].lower() == 'mixin': |
| 240 continue | 163 continue |
| 241 try: | 164 try: |
| 242 if not [n for n in owner.getattr(node.attrname) | 165 if not [n for n in owner.getattr(node.attrname) |
| 243 if not isinstance(n.statement(), astroid.AugAssign)]: | 166 if not isinstance(n.statement(), astng.AugAssign)]: |
| 244 missingattr.add((owner, name)) | 167 missingattr.add((owner, name)) |
| 245 continue | 168 continue |
| 246 except AttributeError: | 169 except AttributeError: |
| 247 # XXX method / function | 170 # XXX method / function |
| 248 continue | 171 continue |
| 249 except NotFoundError: | 172 except NotFoundError: |
| 250 if isinstance(owner, astroid.Function) and owner.decorators: | 173 if isinstance(owner, astng.Function) and owner.decorators: |
| 251 continue | 174 continue |
| 252 if isinstance(owner, Instance) and owner.has_dynamic_getattr(): | 175 if isinstance(owner, Instance) and owner.has_dynamic_getattr(): |
| 253 continue | 176 continue |
| 254 # explicit skipping of module member access | 177 # explicit skipping of optparse'Values class |
| 255 if owner.root().name in self.config.ignored_modules: | 178 if owner.name == 'Values' and owner.root().name == 'optparse': |
| 256 continue | 179 continue |
| 257 missingattr.add((owner, name)) | 180 missingattr.add((owner, name)) |
| 258 continue | 181 continue |
| 259 # stop on the first found | 182 # stop on the first found |
| 260 break | 183 break |
| 261 else: | 184 else: |
| 262 # we have not found any node with the attributes, display the | 185 # we have not found any node with the attributes, display the |
| 263 # message for infered nodes | 186 # message for infered nodes |
| 264 done = set() | 187 done = set() |
| 265 for owner, name in missingattr: | 188 for owner, name in missingattr: |
| 266 if isinstance(owner, Instance): | 189 if isinstance(owner, Instance): |
| 267 actual = owner._proxied | 190 actual = owner._proxied |
| 268 else: | 191 else: |
| 269 actual = owner | 192 actual = owner |
| 270 if actual in done: | 193 if actual in done: |
| 271 continue | 194 continue |
| 272 done.add(actual) | 195 done.add(actual) |
| 273 if inference_failure: | 196 if inference_failure: |
| 274 msgid = 'maybe-no-member' | 197 msgid = 'E1103' |
| 275 else: | 198 else: |
| 276 msgid = 'no-member' | 199 msgid = 'E1101' |
| 277 self.add_message(msgid, node=node, | 200 self.add_message(msgid, node=node, |
| 278 args=(owner.display_type(), name, | 201 args=(owner.display_type(), name, |
| 279 node.attrname)) | 202 node.attrname)) |
| 280 | 203 |
| 281 @check_messages('assignment-from-no-return', 'assignment-from-none') | 204 |
| 282 def visit_assign(self, node): | 205 def visit_assign(self, node): |
| 283 """check that if assigning to a function call, the function is | 206 """check that if assigning to a function call, the function is |
| 284 possibly returning something valuable | 207 possibly returning something valuable |
| 285 """ | 208 """ |
| 286 if not isinstance(node.value, astroid.CallFunc): | 209 if not isinstance(node.value, astng.CallFunc): |
| 287 return | 210 return |
| 288 function_node = safe_infer(node.value.func) | 211 function_node = safe_infer(node.value.func) |
| 289 # skip class, generator and incomplete function definition | 212 # skip class, generator and incomplete function definition |
| 290 if not (isinstance(function_node, astroid.Function) and | 213 if not (isinstance(function_node, astng.Function) and |
| 291 function_node.root().fully_defined()): | 214 function_node.root().fully_defined()): |
| 292 return | 215 return |
| 293 if function_node.is_generator() \ | 216 if function_node.is_generator() \ |
| 294 or function_node.is_abstract(pass_is_abstract=False): | 217 or function_node.is_abstract(pass_is_abstract=False): |
| 295 return | 218 return |
| 296 returns = list(function_node.nodes_of_class(astroid.Return, | 219 returns = list(function_node.nodes_of_class(astng.Return, |
| 297 skip_klass=astroid.Function)
) | 220 skip_klass=astng.Function)) |
| 298 if len(returns) == 0: | 221 if len(returns) == 0: |
| 299 self.add_message('assignment-from-no-return', node=node) | 222 self.add_message('E1111', node=node) |
| 300 else: | 223 else: |
| 301 for rnode in returns: | 224 for rnode in returns: |
| 302 if not (isinstance(rnode.value, astroid.Const) | 225 if not (isinstance(rnode.value, astng.Const) |
| 303 and rnode.value.value is None | 226 and rnode.value.value is None): |
| 304 or rnode.value is None): | |
| 305 break | 227 break |
| 306 else: | 228 else: |
| 307 self.add_message('assignment-from-none', node=node) | 229 self.add_message('W1111', node=node) |
| 308 | 230 |
| 309 def _check_uninferable_callfunc(self, node): | |
| 310 """ | |
| 311 Check that the given uninferable CallFunc node does not | |
| 312 call an actual function. | |
| 313 """ | |
| 314 if not isinstance(node.func, astroid.Getattr): | |
| 315 return | |
| 316 | |
| 317 # Look for properties. First, obtain | |
| 318 # the lhs of the Getattr node and search the attribute | |
| 319 # there. If that attribute is a property or a subclass of properties, | |
| 320 # then most likely it's not callable. | |
| 321 | |
| 322 # TODO: since astroid doesn't understand descriptors very well | |
| 323 # we will not handle them here, right now. | |
| 324 | |
| 325 expr = node.func.expr | |
| 326 klass = safe_infer(expr) | |
| 327 if (klass is None or klass is astroid.YES or | |
| 328 not isinstance(klass, astroid.Instance)): | |
| 329 return | |
| 330 | |
| 331 try: | |
| 332 attrs = klass._proxied.getattr(node.func.attrname) | |
| 333 except astroid.NotFoundError: | |
| 334 return | |
| 335 | |
| 336 stop_checking = False | |
| 337 for attr in attrs: | |
| 338 if attr is astroid.YES: | |
| 339 continue | |
| 340 if stop_checking: | |
| 341 break | |
| 342 if not isinstance(attr, astroid.Function): | |
| 343 continue | |
| 344 | |
| 345 # Decorated, see if it is decorated with a property | |
| 346 if not attr.decorators: | |
| 347 continue | |
| 348 for decorator in attr.decorators.nodes: | |
| 349 if not isinstance(decorator, astroid.Name): | |
| 350 continue | |
| 351 try: | |
| 352 for infered in decorator.infer(): | |
| 353 property_like = False | |
| 354 if isinstance(infered, astroid.Class): | |
| 355 if (infered.root().name == BUILTINS and | |
| 356 infered.name == 'property'): | |
| 357 property_like = True | |
| 358 else: | |
| 359 for ancestor in infered.ancestors(): | |
| 360 if (ancestor.name == 'property' and | |
| 361 ancestor.root().name == BUILTINS): | |
| 362 property_like = True | |
| 363 break | |
| 364 if property_like: | |
| 365 self.add_message('not-callable', node=node, | |
| 366 args=node.func.as_string()) | |
| 367 stop_checking = True | |
| 368 break | |
| 369 except InferenceError: | |
| 370 pass | |
| 371 if stop_checking: | |
| 372 break | |
| 373 | |
| 374 @check_messages(*(list(MSGS.keys()))) | |
| 375 def visit_callfunc(self, node): | 231 def visit_callfunc(self, node): |
| 376 """check that called functions/methods are inferred to callable objects, | 232 """check that called functions/methods are inferred to callable objects, |
| 377 and that the arguments passed to the function match the parameters in | 233 and that the arguments passed to the function match the parameters in |
| 378 the inferred function's definition | 234 the inferred function's definition |
| 379 """ | 235 """ |
| 236 |
| 380 # Build the set of keyword arguments, checking for duplicate keywords, | 237 # Build the set of keyword arguments, checking for duplicate keywords, |
| 381 # and count the positional arguments. | 238 # and count the positional arguments. |
| 382 keyword_args = set() | 239 keyword_args = set() |
| 383 num_positional_args = 0 | 240 num_positional_args = 0 |
| 384 for arg in node.args: | 241 for arg in node.args: |
| 385 if isinstance(arg, astroid.Keyword): | 242 if isinstance(arg, astng.Keyword): |
| 386 keyword = arg.arg | 243 keyword = arg.arg |
| 387 if keyword in keyword_args: | 244 if keyword in keyword_args: |
| 388 self.add_message('duplicate-keyword-arg', node=node, | 245 self.add_message('E1122', node=node, args=keyword) |
| 389 args=(keyword, 'function')) | |
| 390 keyword_args.add(keyword) | 246 keyword_args.add(keyword) |
| 391 else: | 247 else: |
| 392 num_positional_args += 1 | 248 num_positional_args += 1 |
| 393 | 249 |
| 394 called = safe_infer(node.func) | 250 called = safe_infer(node.func) |
| 395 # only function, generator and object defining __call__ are allowed | 251 # only function, generator and object defining __call__ are allowed |
| 396 if called is not None and not called.callable(): | 252 if called is not None and not called.callable(): |
| 397 self.add_message('not-callable', node=node, | 253 self.add_message('E1102', node=node, args=node.func.as_string()) |
| 398 args=node.func.as_string()) | |
| 399 | 254 |
| 400 self._check_uninferable_callfunc(node) | 255 # Note that BoundMethod is a subclass of UnboundMethod (huh?), so must |
| 256 # come first in this 'if..else'. |
| 257 if isinstance(called, astng.BoundMethod): |
| 258 # Bound methods have an extra implicit 'self' argument. |
| 259 num_positional_args += 1 |
| 260 elif isinstance(called, astng.UnboundMethod): |
| 261 if called.decorators is not None: |
| 262 for d in called.decorators.nodes: |
| 263 if isinstance(d, astng.Name) and (d.name == 'classmethod'): |
| 264 # Class methods have an extra implicit 'cls' argument. |
| 265 num_positional_args += 1 |
| 266 break |
| 267 elif (isinstance(called, astng.Function) or |
| 268 isinstance(called, astng.Lambda)): |
| 269 pass |
| 270 else: |
| 271 return |
| 401 | 272 |
| 402 try: | |
| 403 called, implicit_args, callable_name = _determine_callable(called) | |
| 404 except ValueError: | |
| 405 # Any error occurred during determining the function type, most of | |
| 406 # those errors are handled by different warnings. | |
| 407 return | |
| 408 num_positional_args += implicit_args | |
| 409 if called.args.args is None: | 273 if called.args.args is None: |
| 410 # Built-in functions have no argument information. | 274 # Built-in functions have no argument information. |
| 411 return | 275 return |
| 412 | 276 |
| 413 if len(called.argnames()) != len(set(called.argnames())): | 277 if len( called.argnames() ) != len( set( called.argnames() ) ): |
| 414 # Duplicate parameter name (see E9801). We can't really make sense | 278 # Duplicate parameter name (see E9801). We can't really make sense |
| 415 # of the function call in this case, so just return. | 279 # of the function call in this case, so just return. |
| 416 return | 280 return |
| 417 | 281 |
| 418 # Analyze the list of formal parameters. | 282 # Analyze the list of formal parameters. |
| 419 num_mandatory_parameters = len(called.args.args) - len(called.args.defau
lts) | 283 num_mandatory_parameters = len(called.args.args) - len(called.args.defau
lts) |
| 420 parameters = [] | 284 parameters = [] |
| 421 parameter_name_to_index = {} | 285 parameter_name_to_index = {} |
| 422 for i, arg in enumerate(called.args.args): | 286 for i, arg in enumerate(called.args.args): |
| 423 if isinstance(arg, astroid.Tuple): | 287 if isinstance(arg, astng.Tuple): |
| 424 name = None | 288 name = None |
| 425 # Don't store any parameter names within the tuple, since those | 289 # Don't store any parameter names within the tuple, since those |
| 426 # are not assignable from keyword arguments. | 290 # are not assignable from keyword arguments. |
| 427 else: | 291 else: |
| 428 if isinstance(arg, astroid.Keyword): | 292 if isinstance(arg, astng.Keyword): |
| 429 name = arg.arg | 293 name = arg.arg |
| 430 else: | 294 else: |
| 431 assert isinstance(arg, astroid.AssName) | 295 assert isinstance(arg, astng.AssName) |
| 432 # This occurs with: | 296 # This occurs with: |
| 433 # def f( (a), (b) ): pass | 297 # def f( (a), (b) ): pass |
| 434 name = arg.name | 298 name = arg.name |
| 435 parameter_name_to_index[name] = i | 299 parameter_name_to_index[name] = i |
| 436 if i >= num_mandatory_parameters: | 300 if i >= num_mandatory_parameters: |
| 437 defval = called.args.defaults[i - num_mandatory_parameters] | 301 defval = called.args.defaults[i - num_mandatory_parameters] |
| 438 else: | 302 else: |
| 439 defval = None | 303 defval = None |
| 440 parameters.append([(name, defval), False]) | 304 parameters.append([(name, defval), False]) |
| 441 | 305 |
| 442 kwparams = {} | |
| 443 for i, arg in enumerate(called.args.kwonlyargs): | |
| 444 if isinstance(arg, astroid.Keyword): | |
| 445 name = arg.arg | |
| 446 else: | |
| 447 assert isinstance(arg, astroid.AssName) | |
| 448 name = arg.name | |
| 449 kwparams[name] = [called.args.kw_defaults[i], False] | |
| 450 | |
| 451 # Match the supplied arguments against the function parameters. | 306 # Match the supplied arguments against the function parameters. |
| 452 | 307 |
| 453 # 1. Match the positional arguments. | 308 # 1. Match the positional arguments. |
| 454 for i in range(num_positional_args): | 309 for i in range(num_positional_args): |
| 455 if i < len(parameters): | 310 if i < len(parameters): |
| 456 parameters[i][1] = True | 311 parameters[i][1] = True |
| 457 elif called.args.vararg is not None: | 312 elif called.args.vararg is not None: |
| 458 # The remaining positional arguments get assigned to the *args | 313 # The remaining positional arguments get assigned to the *args |
| 459 # parameter. | 314 # parameter. |
| 460 break | 315 break |
| 461 else: | 316 else: |
| 462 # Too many positional arguments. | 317 # Too many positional arguments. |
| 463 self.add_message('too-many-function-args', | 318 self.add_message('E1121', node=node) |
| 464 node=node, args=(callable_name,)) | |
| 465 break | 319 break |
| 466 | 320 |
| 467 # 2. Match the keyword arguments. | 321 # 2. Match the keyword arguments. |
| 468 for keyword in keyword_args: | 322 for keyword in keyword_args: |
| 469 if keyword in parameter_name_to_index: | 323 if keyword in parameter_name_to_index: |
| 470 i = parameter_name_to_index[keyword] | 324 i = parameter_name_to_index[keyword] |
| 471 if parameters[i][1]: | 325 if parameters[i][1]: |
| 472 # Duplicate definition of function parameter. | 326 # Duplicate definition of function parameter. |
| 473 self.add_message('redundant-keyword-arg', | 327 self.add_message('E1124', node=node, args=keyword) |
| 474 node=node, args=(keyword, callable_name)) | |
| 475 else: | 328 else: |
| 476 parameters[i][1] = True | 329 parameters[i][1] = True |
| 477 elif keyword in kwparams: | |
| 478 if kwparams[keyword][1]: # XXX is that even possible? | |
| 479 # Duplicate definition of function parameter. | |
| 480 self.add_message('redundant-keyword-arg', node=node, | |
| 481 args=(keyword, callable_name)) | |
| 482 else: | |
| 483 kwparams[keyword][1] = True | |
| 484 elif called.args.kwarg is not None: | 330 elif called.args.kwarg is not None: |
| 485 # The keyword argument gets assigned to the **kwargs parameter. | 331 # The keyword argument gets assigned to the **kwargs parameter. |
| 486 pass | 332 pass |
| 487 else: | 333 else: |
| 488 # Unexpected keyword argument. | 334 # Unexpected keyword argument. |
| 489 self.add_message('unexpected-keyword-arg', node=node, | 335 self.add_message('E1123', node=node, args=keyword) |
| 490 args=(keyword, callable_name)) | |
| 491 | 336 |
| 492 # 3. Match the *args, if any. Note that Python actually processes | 337 # 3. Match the *args, if any. Note that Python actually processes |
| 493 # *args _before_ any keyword arguments, but we wait until after | 338 # *args _before_ any keyword arguments, but we wait until after |
| 494 # looking at the keyword arguments so as to make a more conservative | 339 # looking at the keyword arguments so as to make a more conservative |
| 495 # guess at how many values are in the *args sequence. | 340 # guess at how many values are in the *args sequence. |
| 496 if node.starargs is not None: | 341 if node.starargs is not None: |
| 497 for i in range(num_positional_args, len(parameters)): | 342 for i in range(num_positional_args, len(parameters)): |
| 498 [(name, defval), assigned] = parameters[i] | 343 [(name, defval), assigned] = parameters[i] |
| 499 # Assume that *args provides just enough values for all | 344 # Assume that *args provides just enough values for all |
| 500 # non-default parameters after the last parameter assigned by | 345 # non-default parameters after the last parameter assigned by |
| (...skipping 13 matching lines...) Expand all Loading... |
| 514 parameters[i][1] = True | 359 parameters[i][1] = True |
| 515 else: | 360 else: |
| 516 # **kwargs can't assign to tuples. | 361 # **kwargs can't assign to tuples. |
| 517 pass | 362 pass |
| 518 | 363 |
| 519 # Check that any parameters without a default have been assigned | 364 # Check that any parameters without a default have been assigned |
| 520 # values. | 365 # values. |
| 521 for [(name, defval), assigned] in parameters: | 366 for [(name, defval), assigned] in parameters: |
| 522 if (defval is None) and not assigned: | 367 if (defval is None) and not assigned: |
| 523 if name is None: | 368 if name is None: |
| 524 display_name = '<tuple>' | 369 display = '<tuple>' |
| 525 else: | 370 else: |
| 526 display_name = repr(name) | 371 display_name = repr(name) |
| 527 self.add_message('no-value-for-parameter', node=node, | 372 self.add_message('E1120', node=node, args=display_name) |
| 528 args=(display_name, callable_name)) | |
| 529 | |
| 530 for name in kwparams: | |
| 531 defval, assigned = kwparams[name] | |
| 532 if defval is None and not assigned: | |
| 533 self.add_message('missing-kwoa', node=node, | |
| 534 args=(name, callable_name)) | |
| 535 | |
| 536 @check_messages('invalid-sequence-index') | |
| 537 def visit_extslice(self, node): | |
| 538 # Check extended slice objects as if they were used as a sequence | |
| 539 # index to check if the object being sliced can support them | |
| 540 return self.visit_index(node) | |
| 541 | |
| 542 @check_messages('invalid-sequence-index') | |
| 543 def visit_index(self, node): | |
| 544 if not node.parent or not hasattr(node.parent, "value"): | |
| 545 return | |
| 546 | |
| 547 # Look for index operations where the parent is a sequence type. | |
| 548 # If the types can be determined, only allow indices to be int, | |
| 549 # slice or instances with __index__. | |
| 550 | |
| 551 parent_type = safe_infer(node.parent.value) | |
| 552 | |
| 553 if not isinstance(parent_type, (astroid.Class, astroid.Instance)): | |
| 554 return | |
| 555 | |
| 556 # Determine what method on the parent this index will use | |
| 557 # The parent of this node will be a Subscript, and the parent of that | |
| 558 # node determines if the Subscript is a get, set, or delete operation. | |
| 559 operation = node.parent.parent | |
| 560 if isinstance(operation, astroid.Assign): | |
| 561 methodname = '__setitem__' | |
| 562 elif isinstance(operation, astroid.Delete): | |
| 563 methodname = '__delitem__' | |
| 564 else: | |
| 565 methodname = '__getitem__' | |
| 566 | |
| 567 # Check if this instance's __getitem__, __setitem__, or __delitem__, as | |
| 568 # appropriate to the statement, is implemented in a builtin sequence | |
| 569 # type. This way we catch subclasses of sequence types but skip classes | |
| 570 # that override __getitem__ and which may allow non-integer indices. | |
| 571 try: | |
| 572 methods = parent_type.getattr(methodname) | |
| 573 if methods is astroid.YES: | |
| 574 return | |
| 575 itemmethod = methods[0] | |
| 576 except (astroid.NotFoundError, IndexError): | |
| 577 return | |
| 578 | |
| 579 if not isinstance(itemmethod, astroid.Function): | |
| 580 return | |
| 581 | |
| 582 if itemmethod.root().name != BUILTINS: | |
| 583 return | |
| 584 | |
| 585 if not itemmethod.parent: | |
| 586 return | |
| 587 | |
| 588 if itemmethod.parent.name not in SEQUENCE_TYPES: | |
| 589 return | |
| 590 | |
| 591 # For ExtSlice objects coming from visit_extslice, no further | |
| 592 # inference is necessary, since if we got this far the ExtSlice | |
| 593 # is an error. | |
| 594 if isinstance(node, astroid.ExtSlice): | |
| 595 index_type = node | |
| 596 else: | |
| 597 index_type = safe_infer(node) | |
| 598 | |
| 599 if index_type is None or index_type is astroid.YES: | |
| 600 return | |
| 601 | |
| 602 # Constants must be of type int | |
| 603 if isinstance(index_type, astroid.Const): | |
| 604 if isinstance(index_type.value, int): | |
| 605 return | |
| 606 # Instance values must be int, slice, or have an __index__ method | |
| 607 elif isinstance(index_type, astroid.Instance): | |
| 608 if index_type.pytype() in (BUILTINS + '.int', BUILTINS + '.slice'): | |
| 609 return | |
| 610 | |
| 611 try: | |
| 612 index_type.getattr('__index__') | |
| 613 return | |
| 614 except astroid.NotFoundError: | |
| 615 pass | |
| 616 | |
| 617 # Anything else is an error | |
| 618 self.add_message('invalid-sequence-index', node=node) | |
| 619 | |
| 620 @check_messages('invalid-slice-index') | |
| 621 def visit_slice(self, node): | |
| 622 # Check the type of each part of the slice | |
| 623 for index in (node.lower, node.upper, node.step): | |
| 624 if index is None: | |
| 625 continue | |
| 626 | |
| 627 index_type = safe_infer(index) | |
| 628 | |
| 629 if index_type is None or index_type is astroid.YES: | |
| 630 continue | |
| 631 | |
| 632 # Constants must of type int or None | |
| 633 if isinstance(index_type, astroid.Const): | |
| 634 if isinstance(index_type.value, (int, type(None))): | |
| 635 continue | |
| 636 # Instance values must be of type int, None or an object | |
| 637 # with __index__ | |
| 638 elif isinstance(index_type, astroid.Instance): | |
| 639 if index_type.pytype() in (BUILTINS + '.int', | |
| 640 BUILTINS + '.NoneType'): | |
| 641 continue | |
| 642 | |
| 643 try: | |
| 644 index_type.getattr('__index__') | |
| 645 return | |
| 646 except astroid.NotFoundError: | |
| 647 pass | |
| 648 | |
| 649 # Anything else is an error | |
| 650 self.add_message('invalid-slice-index', node=node) | |
| 651 | 373 |
| 652 def register(linter): | 374 def register(linter): |
| 653 """required method to auto register this checker """ | 375 """required method to auto register this checker """ |
| 654 linter.register_checker(TypeChecker(linter)) | 376 linter.register_checker(TypeChecker(linter)) |
| OLD | NEW |