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 |