Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(285)

Side by Side Diff: third_party/pylint/checkers/typecheck.py

Issue 719313003: Revert "pylint: upgrade to 1.3.1" (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/depot_tools.git@master
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « third_party/pylint/checkers/strings.py ('k') | third_party/pylint/checkers/utils.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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))
OLDNEW
« no previous file with comments | « third_party/pylint/checkers/strings.py ('k') | third_party/pylint/checkers/utils.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698