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

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

Issue 739393004: Revert "Revert "pylint: upgrade to 1.3.1"" (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools/
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 | Annotate | Revision Log
« 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-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
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))
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