OLD | NEW |
1 # Copyright (c) 2006-2010 LOGILAB S.A. (Paris, FRANCE). | 1 # Copyright (c) 2006-2013 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 # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | 15 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
16 """try to find more bugs in the code using astng inference capabilities | 16 """try to find more bugs in the code using astroid inference capabilities |
17 """ | 17 """ |
18 | 18 |
19 import re | 19 import re |
20 import shlex | 20 import shlex |
21 | 21 |
22 from logilab import astng | 22 import astroid |
23 from logilab.astng import InferenceError, NotFoundError, YES, Instance | 23 from astroid import InferenceError, NotFoundError, YES, Instance |
| 24 from astroid.bases import BUILTINS |
24 | 25 |
25 from pylint.interfaces import IASTNGChecker | 26 from pylint.interfaces import IAstroidChecker |
26 from pylint.checkers import BaseChecker | 27 from pylint.checkers import BaseChecker |
27 from pylint.checkers.utils import safe_infer, is_super, check_messages | 28 from pylint.checkers.utils import safe_infer, is_super, check_messages |
28 | 29 |
29 MSGS = { | 30 MSGS = { |
30 'E1101': ('%s %r has no %r member', | 31 'E1101': ('%s %r has no %r member', |
| 32 'no-member', |
31 'Used when a variable is accessed for an unexistent member.'), | 33 'Used when a variable is accessed for an unexistent member.'), |
32 'E1102': ('%s is not callable', | 34 'E1102': ('%s is not callable', |
| 35 'not-callable', |
33 'Used when an object being called has been inferred to a non \ | 36 'Used when an object being called has been inferred to a non \ |
34 callable object'), | 37 callable object'), |
35 'E1103': ('%s %r has no %r member (but some types could not be inferred)', | 38 'E1103': ('%s %r has no %r member (but some types could not be inferred)', |
| 39 'maybe-no-member', |
36 'Used when a variable is accessed for an unexistent member, but \ | 40 'Used when a variable is accessed for an unexistent member, but \ |
37 astng was not able to interpret all possible types of this \ | 41 astroid was not able to interpret all possible types of this \ |
38 variable.'), | 42 variable.'), |
39 'E1111': ('Assigning to function call which doesn\'t return', | 43 'E1111': ('Assigning to function call which doesn\'t return', |
| 44 'assignment-from-no-return', |
40 'Used when an assignment is done on a function call but the \ | 45 'Used when an assignment is done on a function call but the \ |
41 inferred function doesn\'t return anything.'), | 46 inferred function doesn\'t return anything.'), |
42 'W1111': ('Assigning to function call which only returns None', | 47 'W1111': ('Assigning to function call which only returns None', |
| 48 'assignment-from-none', |
43 'Used when an assignment is done on a function call but the \ | 49 'Used when an assignment is done on a function call but the \ |
44 inferred function returns nothing but None.'), | 50 inferred function returns nothing but None.'), |
45 | 51 |
46 'E1120': ('No value passed for parameter %s in function call', | 52 'E1120': ('No value for argument %s in %s call', |
| 53 'no-value-for-parameter', |
47 'Used when a function call passes too few arguments.'), | 54 'Used when a function call passes too few arguments.'), |
48 'E1121': ('Too many positional arguments for function call', | 55 'E1121': ('Too many positional arguments for %s call', |
| 56 'too-many-function-args', |
49 'Used when a function call passes too many positional \ | 57 'Used when a function call passes too many positional \ |
50 arguments.'), | 58 arguments.'), |
51 'E1122': ('Duplicate keyword argument %r in function call', | 59 'E1122': ('Duplicate keyword argument %r in %s call', |
| 60 'duplicate-keyword-arg', |
52 'Used when a function call passes the same keyword argument \ | 61 'Used when a function call passes the same keyword argument \ |
53 multiple times.'), | 62 multiple times.', |
54 'E1123': ('Passing unexpected keyword argument %r in function call', | 63 {'maxversion': (2, 6)}), |
| 64 'E1123': ('Unexpected keyword argument %r in %s call', |
| 65 'unexpected-keyword-arg', |
55 'Used when a function call passes a keyword argument that \ | 66 'Used when a function call passes a keyword argument that \ |
56 doesn\'t correspond to one of the function\'s parameter names.'), | 67 doesn\'t correspond to one of the function\'s parameter names.'), |
57 'E1124': ('Multiple values passed for parameter %r in function call', | 68 'E1124': ('Argument %r passed by position and keyword in %s call', |
| 69 'redundant-keyword-arg', |
58 'Used when a function call would result in assigning multiple \ | 70 'Used when a function call would result in assigning multiple \ |
59 values to a function parameter, one value from a positional \ | 71 values to a function parameter, one value from a positional \ |
60 argument and one from a keyword argument.'), | 72 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.'), |
61 } | 87 } |
62 | 88 |
| 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 |
63 class TypeChecker(BaseChecker): | 132 class TypeChecker(BaseChecker): |
64 """try to find bugs in the code using type inference | 133 """try to find bugs in the code using type inference |
65 """ | 134 """ |
66 | 135 |
67 __implements__ = (IASTNGChecker,) | 136 __implements__ = (IAstroidChecker,) |
68 | 137 |
69 # configuration section name | 138 # configuration section name |
70 name = 'typecheck' | 139 name = 'typecheck' |
71 # messages | 140 # messages |
72 msgs = MSGS | 141 msgs = MSGS |
73 priority = -1 | 142 priority = -1 |
74 # configuration options | 143 # configuration options |
75 options = (('ignore-mixin-members', | 144 options = (('ignore-mixin-members', |
76 {'default' : True, 'type' : 'yn', 'metavar': '<y_or_n>', | 145 {'default' : True, 'type' : 'yn', 'metavar': '<y_or_n>', |
77 'help' : 'Tells whether missing members accessed in mixin \ | 146 'help' : 'Tells whether missing members accessed in mixin \ |
78 class should be ignored. A mixin class is detected if its name ends with \ | 147 class should be ignored. A mixin class is detected if its name ends with \ |
79 "mixin" (case insensitive).'} | 148 "mixin" (case insensitive).'} |
80 ), | 149 ), |
81 | 150 ('ignored-modules', |
| 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 ), |
82 ('ignored-classes', | 159 ('ignored-classes', |
83 {'default' : ('SQLObject',), | 160 {'default' : ('SQLObject',), |
84 'type' : 'csv', | 161 'type' : 'csv', |
85 'metavar' : '<members names>', | 162 'metavar' : '<members names>', |
86 'help' : 'List of classes names for which member attributes \ | 163 'help' : 'List of classes names for which member attributes \ |
87 should not be checked (useful for classes with attributes dynamically set).'} | 164 should not be checked (useful for classes with attributes dynamically set).'} |
88 ), | 165 ), |
89 | 166 |
90 ('zope', | 167 ('zope', |
91 {'default' : False, 'type' : 'yn', 'metavar': '<y_or_n>', | 168 {'default' : False, 'type' : 'yn', 'metavar': '<y_or_n>', |
92 'help' : 'When zope mode is activated, add a predefined set \ | 169 'help' : 'When zope mode is activated, add a predefined set \ |
93 of Zope acquired attributes to generated-members.'} | 170 of Zope acquired attributes to generated-members.'} |
94 ), | 171 ), |
95 ('generated-members', | 172 ('generated-members', |
96 {'default' : ( | 173 {'default' : ('REQUEST', 'acl_users', 'aq_parent'), |
97 'REQUEST', 'acl_users', 'aq_parent'), | |
98 'type' : 'string', | 174 'type' : 'string', |
99 'metavar' : '<members names>', | 175 'metavar' : '<members names>', |
100 'help' : 'List of members which are set dynamically and \ | 176 'help' : 'List of members which are set dynamically and \ |
101 missed by pylint inference system, and so shouldn\'t trigger E0201 when \ | 177 missed by pylint inference system, and so shouldn\'t trigger E0201 when \ |
102 accessed. Python regular expressions are accepted.'} | 178 accessed. Python regular expressions are accepted.'} |
103 ), | 179 ), |
104 ) | 180 ) |
105 | 181 |
106 def open(self): | 182 def open(self): |
107 # do this in open since config not fully initialized in __init__ | 183 # do this in open since config not fully initialized in __init__ |
108 self.generated_members = list(self.config.generated_members) | 184 self.generated_members = list(self.config.generated_members) |
109 if self.config.zope: | 185 if self.config.zope: |
110 self.generated_members.extend(('REQUEST', 'acl_users', 'aq_parent')) | 186 self.generated_members.extend(('REQUEST', 'acl_users', 'aq_parent')) |
111 | 187 |
112 def visit_assattr(self, node): | 188 def visit_assattr(self, node): |
113 if isinstance(node.ass_type(), astng.AugAssign): | 189 if isinstance(node.ass_type(), astroid.AugAssign): |
114 self.visit_getattr(node) | 190 self.visit_getattr(node) |
115 | 191 |
116 def visit_delattr(self, node): | 192 def visit_delattr(self, node): |
117 self.visit_getattr(node) | 193 self.visit_getattr(node) |
118 | 194 |
119 @check_messages('E1101', 'E1103') | 195 @check_messages('no-member', 'maybe-no-member') |
120 def visit_getattr(self, node): | 196 def visit_getattr(self, node): |
121 """check that the accessed attribute exists | 197 """check that the accessed attribute exists |
122 | 198 |
123 to avoid to much false positives for now, we'll consider the code as | 199 to avoid to much false positives for now, we'll consider the code as |
124 correct if a single of the inferred nodes has the accessed attribute. | 200 correct if a single of the inferred nodes has the accessed attribute. |
125 | 201 |
126 function/method, super call and metaclasses are ignored | 202 function/method, super call and metaclasses are ignored |
127 """ | 203 """ |
128 # generated_members may containt regular expressions | 204 # generated_members may containt regular expressions |
129 # (surrounded by quote `"` and followed by a comma `,`) | 205 # (surrounded by quote `"` and followed by a comma `,`) |
130 # REQUEST,aq_parent,"[a-zA-Z]+_set{1,2}"' => | 206 # REQUEST,aq_parent,"[a-zA-Z]+_set{1,2}"' => |
131 # ('REQUEST', 'aq_parent', '[a-zA-Z]+_set{1,2}') | 207 # ('REQUEST', 'aq_parent', '[a-zA-Z]+_set{1,2}') |
132 if isinstance(self.config.generated_members, str): | 208 if isinstance(self.config.generated_members, str): |
133 gen = shlex.shlex(self.config.generated_members) | 209 gen = shlex.shlex(self.config.generated_members) |
134 gen.whitespace += ',' | 210 gen.whitespace += ',' |
| 211 gen.wordchars += '[]-+' |
135 self.config.generated_members = tuple(tok.strip('"') for tok in gen) | 212 self.config.generated_members = tuple(tok.strip('"') for tok in gen) |
136 for pattern in self.config.generated_members: | 213 for pattern in self.config.generated_members: |
137 # attribute is marked as generated, stop here | 214 # attribute is marked as generated, stop here |
138 if re.match(pattern, node.attrname): | 215 if re.match(pattern, node.attrname): |
139 return | 216 return |
140 try: | 217 try: |
141 infered = list(node.expr.infer()) | 218 infered = list(node.expr.infer()) |
142 except InferenceError: | 219 except InferenceError: |
143 return | 220 return |
144 # list of (node, nodename) which are missing the attribute | 221 # list of (node, nodename) which are missing the attribute |
145 missingattr = set() | 222 missingattr = set() |
146 ignoremim = self.config.ignore_mixin_members | 223 ignoremim = self.config.ignore_mixin_members |
147 inference_failure = False | 224 inference_failure = False |
148 for owner in infered: | 225 for owner in infered: |
149 # skip yes object | 226 # skip yes object |
150 if owner is YES: | 227 if owner is YES: |
151 inference_failure = True | 228 inference_failure = True |
152 continue | 229 continue |
153 # skip None anyway | 230 # skip None anyway |
154 if isinstance(owner, astng.Const) and owner.value is None: | 231 if isinstance(owner, astroid.Const) and owner.value is None: |
155 continue | 232 continue |
156 # XXX "super" / metaclass call | 233 # XXX "super" / metaclass call |
157 if is_super(owner) or getattr(owner, 'type', None) == 'metaclass': | 234 if is_super(owner) or getattr(owner, 'type', None) == 'metaclass': |
158 continue | 235 continue |
159 name = getattr(owner, 'name', 'None') | 236 name = getattr(owner, 'name', 'None') |
160 if name in self.config.ignored_classes: | 237 if name in self.config.ignored_classes: |
161 continue | 238 continue |
162 if ignoremim and name[-5:].lower() == 'mixin': | 239 if ignoremim and name[-5:].lower() == 'mixin': |
163 continue | 240 continue |
164 try: | 241 try: |
165 if not [n for n in owner.getattr(node.attrname) | 242 if not [n for n in owner.getattr(node.attrname) |
166 if not isinstance(n.statement(), astng.AugAssign)]: | 243 if not isinstance(n.statement(), astroid.AugAssign)]: |
167 missingattr.add((owner, name)) | 244 missingattr.add((owner, name)) |
168 continue | 245 continue |
169 except AttributeError: | 246 except AttributeError: |
170 # XXX method / function | 247 # XXX method / function |
171 continue | 248 continue |
172 except NotFoundError: | 249 except NotFoundError: |
173 if isinstance(owner, astng.Function) and owner.decorators: | 250 if isinstance(owner, astroid.Function) and owner.decorators: |
174 continue | 251 continue |
175 if isinstance(owner, Instance) and owner.has_dynamic_getattr(): | 252 if isinstance(owner, Instance) and owner.has_dynamic_getattr(): |
176 continue | 253 continue |
177 # explicit skipping of optparse'Values class | 254 # explicit skipping of module member access |
178 if owner.name == 'Values' and owner.root().name == 'optparse': | 255 if owner.root().name in self.config.ignored_modules: |
179 continue | 256 continue |
180 missingattr.add((owner, name)) | 257 missingattr.add((owner, name)) |
181 continue | 258 continue |
182 # stop on the first found | 259 # stop on the first found |
183 break | 260 break |
184 else: | 261 else: |
185 # we have not found any node with the attributes, display the | 262 # we have not found any node with the attributes, display the |
186 # message for infered nodes | 263 # message for infered nodes |
187 done = set() | 264 done = set() |
188 for owner, name in missingattr: | 265 for owner, name in missingattr: |
189 if isinstance(owner, Instance): | 266 if isinstance(owner, Instance): |
190 actual = owner._proxied | 267 actual = owner._proxied |
191 else: | 268 else: |
192 actual = owner | 269 actual = owner |
193 if actual in done: | 270 if actual in done: |
194 continue | 271 continue |
195 done.add(actual) | 272 done.add(actual) |
196 if inference_failure: | 273 if inference_failure: |
197 msgid = 'E1103' | 274 msgid = 'maybe-no-member' |
198 else: | 275 else: |
199 msgid = 'E1101' | 276 msgid = 'no-member' |
200 self.add_message(msgid, node=node, | 277 self.add_message(msgid, node=node, |
201 args=(owner.display_type(), name, | 278 args=(owner.display_type(), name, |
202 node.attrname)) | 279 node.attrname)) |
203 | 280 |
204 | 281 @check_messages('assignment-from-no-return', 'assignment-from-none') |
205 def visit_assign(self, node): | 282 def visit_assign(self, node): |
206 """check that if assigning to a function call, the function is | 283 """check that if assigning to a function call, the function is |
207 possibly returning something valuable | 284 possibly returning something valuable |
208 """ | 285 """ |
209 if not isinstance(node.value, astng.CallFunc): | 286 if not isinstance(node.value, astroid.CallFunc): |
210 return | 287 return |
211 function_node = safe_infer(node.value.func) | 288 function_node = safe_infer(node.value.func) |
212 # skip class, generator and incomplete function definition | 289 # skip class, generator and incomplete function definition |
213 if not (isinstance(function_node, astng.Function) and | 290 if not (isinstance(function_node, astroid.Function) and |
214 function_node.root().fully_defined()): | 291 function_node.root().fully_defined()): |
215 return | 292 return |
216 if function_node.is_generator() \ | 293 if function_node.is_generator() \ |
217 or function_node.is_abstract(pass_is_abstract=False): | 294 or function_node.is_abstract(pass_is_abstract=False): |
218 return | 295 return |
219 returns = list(function_node.nodes_of_class(astng.Return, | 296 returns = list(function_node.nodes_of_class(astroid.Return, |
220 skip_klass=astng.Function)) | 297 skip_klass=astroid.Function)
) |
221 if len(returns) == 0: | 298 if len(returns) == 0: |
222 self.add_message('E1111', node=node) | 299 self.add_message('assignment-from-no-return', node=node) |
223 else: | 300 else: |
224 for rnode in returns: | 301 for rnode in returns: |
225 if not (isinstance(rnode.value, astng.Const) | 302 if not (isinstance(rnode.value, astroid.Const) |
226 and rnode.value.value is None): | 303 and rnode.value.value is None |
| 304 or rnode.value is None): |
227 break | 305 break |
228 else: | 306 else: |
229 self.add_message('W1111', node=node) | 307 self.add_message('assignment-from-none', node=node) |
230 | 308 |
| 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()))) |
231 def visit_callfunc(self, node): | 375 def visit_callfunc(self, node): |
232 """check that called functions/methods are inferred to callable objects, | 376 """check that called functions/methods are inferred to callable objects, |
233 and that the arguments passed to the function match the parameters in | 377 and that the arguments passed to the function match the parameters in |
234 the inferred function's definition | 378 the inferred function's definition |
235 """ | 379 """ |
236 | |
237 # Build the set of keyword arguments, checking for duplicate keywords, | 380 # Build the set of keyword arguments, checking for duplicate keywords, |
238 # and count the positional arguments. | 381 # and count the positional arguments. |
239 keyword_args = set() | 382 keyword_args = set() |
240 num_positional_args = 0 | 383 num_positional_args = 0 |
241 for arg in node.args: | 384 for arg in node.args: |
242 if isinstance(arg, astng.Keyword): | 385 if isinstance(arg, astroid.Keyword): |
243 keyword = arg.arg | 386 keyword = arg.arg |
244 if keyword in keyword_args: | 387 if keyword in keyword_args: |
245 self.add_message('E1122', node=node, args=keyword) | 388 self.add_message('duplicate-keyword-arg', node=node, |
| 389 args=(keyword, 'function')) |
246 keyword_args.add(keyword) | 390 keyword_args.add(keyword) |
247 else: | 391 else: |
248 num_positional_args += 1 | 392 num_positional_args += 1 |
249 | 393 |
250 called = safe_infer(node.func) | 394 called = safe_infer(node.func) |
251 # only function, generator and object defining __call__ are allowed | 395 # only function, generator and object defining __call__ are allowed |
252 if called is not None and not called.callable(): | 396 if called is not None and not called.callable(): |
253 self.add_message('E1102', node=node, args=node.func.as_string()) | 397 self.add_message('not-callable', node=node, |
| 398 args=node.func.as_string()) |
254 | 399 |
255 # Note that BoundMethod is a subclass of UnboundMethod (huh?), so must | 400 self._check_uninferable_callfunc(node) |
256 # come first in this 'if..else'. | 401 |
257 if isinstance(called, astng.BoundMethod): | 402 try: |
258 # Bound methods have an extra implicit 'self' argument. | 403 called, implicit_args, callable_name = _determine_callable(called) |
259 num_positional_args += 1 | 404 except ValueError: |
260 elif isinstance(called, astng.UnboundMethod): | 405 # Any error occurred during determining the function type, most of |
261 if called.decorators is not None: | 406 # those errors are handled by different warnings. |
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 | 407 return |
272 | 408 num_positional_args += implicit_args |
273 if called.args.args is None: | 409 if called.args.args is None: |
274 # Built-in functions have no argument information. | 410 # Built-in functions have no argument information. |
275 return | 411 return |
276 | 412 |
277 if len( called.argnames() ) != len( set( called.argnames() ) ): | 413 if len(called.argnames()) != len(set(called.argnames())): |
278 # Duplicate parameter name (see E9801). We can't really make sense | 414 # Duplicate parameter name (see E9801). We can't really make sense |
279 # of the function call in this case, so just return. | 415 # of the function call in this case, so just return. |
280 return | 416 return |
281 | 417 |
282 # Analyze the list of formal parameters. | 418 # Analyze the list of formal parameters. |
283 num_mandatory_parameters = len(called.args.args) - len(called.args.defau
lts) | 419 num_mandatory_parameters = len(called.args.args) - len(called.args.defau
lts) |
284 parameters = [] | 420 parameters = [] |
285 parameter_name_to_index = {} | 421 parameter_name_to_index = {} |
286 for i, arg in enumerate(called.args.args): | 422 for i, arg in enumerate(called.args.args): |
287 if isinstance(arg, astng.Tuple): | 423 if isinstance(arg, astroid.Tuple): |
288 name = None | 424 name = None |
289 # Don't store any parameter names within the tuple, since those | 425 # Don't store any parameter names within the tuple, since those |
290 # are not assignable from keyword arguments. | 426 # are not assignable from keyword arguments. |
291 else: | 427 else: |
292 if isinstance(arg, astng.Keyword): | 428 if isinstance(arg, astroid.Keyword): |
293 name = arg.arg | 429 name = arg.arg |
294 else: | 430 else: |
295 assert isinstance(arg, astng.AssName) | 431 assert isinstance(arg, astroid.AssName) |
296 # This occurs with: | 432 # This occurs with: |
297 # def f( (a), (b) ): pass | 433 # def f( (a), (b) ): pass |
298 name = arg.name | 434 name = arg.name |
299 parameter_name_to_index[name] = i | 435 parameter_name_to_index[name] = i |
300 if i >= num_mandatory_parameters: | 436 if i >= num_mandatory_parameters: |
301 defval = called.args.defaults[i - num_mandatory_parameters] | 437 defval = called.args.defaults[i - num_mandatory_parameters] |
302 else: | 438 else: |
303 defval = None | 439 defval = None |
304 parameters.append([(name, defval), False]) | 440 parameters.append([(name, defval), False]) |
305 | 441 |
| 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 |
306 # Match the supplied arguments against the function parameters. | 451 # Match the supplied arguments against the function parameters. |
307 | 452 |
308 # 1. Match the positional arguments. | 453 # 1. Match the positional arguments. |
309 for i in range(num_positional_args): | 454 for i in range(num_positional_args): |
310 if i < len(parameters): | 455 if i < len(parameters): |
311 parameters[i][1] = True | 456 parameters[i][1] = True |
312 elif called.args.vararg is not None: | 457 elif called.args.vararg is not None: |
313 # The remaining positional arguments get assigned to the *args | 458 # The remaining positional arguments get assigned to the *args |
314 # parameter. | 459 # parameter. |
315 break | 460 break |
316 else: | 461 else: |
317 # Too many positional arguments. | 462 # Too many positional arguments. |
318 self.add_message('E1121', node=node) | 463 self.add_message('too-many-function-args', |
| 464 node=node, args=(callable_name,)) |
319 break | 465 break |
320 | 466 |
321 # 2. Match the keyword arguments. | 467 # 2. Match the keyword arguments. |
322 for keyword in keyword_args: | 468 for keyword in keyword_args: |
323 if keyword in parameter_name_to_index: | 469 if keyword in parameter_name_to_index: |
324 i = parameter_name_to_index[keyword] | 470 i = parameter_name_to_index[keyword] |
325 if parameters[i][1]: | 471 if parameters[i][1]: |
326 # Duplicate definition of function parameter. | 472 # Duplicate definition of function parameter. |
327 self.add_message('E1124', node=node, args=keyword) | 473 self.add_message('redundant-keyword-arg', |
| 474 node=node, args=(keyword, callable_name)) |
328 else: | 475 else: |
329 parameters[i][1] = True | 476 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 |
330 elif called.args.kwarg is not None: | 484 elif called.args.kwarg is not None: |
331 # The keyword argument gets assigned to the **kwargs parameter. | 485 # The keyword argument gets assigned to the **kwargs parameter. |
332 pass | 486 pass |
333 else: | 487 else: |
334 # Unexpected keyword argument. | 488 # Unexpected keyword argument. |
335 self.add_message('E1123', node=node, args=keyword) | 489 self.add_message('unexpected-keyword-arg', node=node, |
| 490 args=(keyword, callable_name)) |
336 | 491 |
337 # 3. Match the *args, if any. Note that Python actually processes | 492 # 3. Match the *args, if any. Note that Python actually processes |
338 # *args _before_ any keyword arguments, but we wait until after | 493 # *args _before_ any keyword arguments, but we wait until after |
339 # looking at the keyword arguments so as to make a more conservative | 494 # looking at the keyword arguments so as to make a more conservative |
340 # guess at how many values are in the *args sequence. | 495 # guess at how many values are in the *args sequence. |
341 if node.starargs is not None: | 496 if node.starargs is not None: |
342 for i in range(num_positional_args, len(parameters)): | 497 for i in range(num_positional_args, len(parameters)): |
343 [(name, defval), assigned] = parameters[i] | 498 [(name, defval), assigned] = parameters[i] |
344 # Assume that *args provides just enough values for all | 499 # Assume that *args provides just enough values for all |
345 # non-default parameters after the last parameter assigned by | 500 # non-default parameters after the last parameter assigned by |
(...skipping 13 matching lines...) Expand all Loading... |
359 parameters[i][1] = True | 514 parameters[i][1] = True |
360 else: | 515 else: |
361 # **kwargs can't assign to tuples. | 516 # **kwargs can't assign to tuples. |
362 pass | 517 pass |
363 | 518 |
364 # Check that any parameters without a default have been assigned | 519 # Check that any parameters without a default have been assigned |
365 # values. | 520 # values. |
366 for [(name, defval), assigned] in parameters: | 521 for [(name, defval), assigned] in parameters: |
367 if (defval is None) and not assigned: | 522 if (defval is None) and not assigned: |
368 if name is None: | 523 if name is None: |
369 display = '<tuple>' | 524 display_name = '<tuple>' |
370 else: | 525 else: |
371 display_name = repr(name) | 526 display_name = repr(name) |
372 self.add_message('E1120', node=node, args=display_name) | 527 self.add_message('no-value-for-parameter', node=node, |
| 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) |
373 | 651 |
374 def register(linter): | 652 def register(linter): |
375 """required method to auto register this checker """ | 653 """required method to auto register this checker """ |
376 linter.register_checker(TypeChecker(linter)) | 654 linter.register_checker(TypeChecker(linter)) |
OLD | NEW |