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

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

Issue 753543006: pylint: upgrade to 1.4.0 (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/depot_tools.git@master
Patch Set: Created 6 years 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-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 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 15 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 """try to find more bugs in the code using astroid 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 import astroid 22 import astroid
23 from astroid import InferenceError, NotFoundError, YES, Instance 23 from astroid import InferenceError, NotFoundError, YES, Instance
24 from astroid.bases import BUILTINS 24 from astroid.bases import BUILTINS
25 25
26 from pylint.interfaces import IAstroidChecker 26 from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE
27 from pylint.checkers import BaseChecker 27 from pylint.checkers import BaseChecker
28 from pylint.checkers.utils import safe_infer, is_super, check_messages 28 from pylint.checkers.utils import (
29 safe_infer, is_super,
30 check_messages, decorated_with_property)
29 31
30 MSGS = { 32 MSGS = {
31 'E1101': ('%s %r has no %r member', 33 'E1101': ('%s %r has no %r member',
32 'no-member', 34 'no-member',
33 'Used when a variable is accessed for an unexistent member.'), 35 'Used when a variable is accessed for an unexistent member.',
36 {'old_names': [('E1103', 'maybe-no-member')]}),
34 'E1102': ('%s is not callable', 37 'E1102': ('%s is not callable',
35 'not-callable', 38 'not-callable',
36 'Used when an object being called has been inferred to a non \ 39 'Used when an object being called has been inferred to a non \
37 callable object'), 40 callable object'),
38 '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 \
41 astroid was not able to interpret all possible types of this \
42 variable.'),
43 'E1111': ('Assigning to function call which doesn\'t return', 41 'E1111': ('Assigning to function call which doesn\'t return',
44 'assignment-from-no-return', 42 'assignment-from-no-return',
45 '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 \
46 inferred function doesn\'t return anything.'), 44 inferred function doesn\'t return anything.'),
47 'W1111': ('Assigning to function call which only returns None', 45 'W1111': ('Assigning to function call which only returns None',
48 'assignment-from-none', 46 'assignment-from-none',
49 'Used when an assignment is done on a function call but the \ 47 'Used when an assignment is done on a function call but the \
50 inferred function returns nothing but None.'), 48 inferred function returns nothing but None.'),
51 49
52 'E1120': ('No value for argument %s in %s call', 50 'E1120': ('No value for argument %s in %s call',
53 'no-value-for-parameter', 51 'no-value-for-parameter',
54 'Used when a function call passes too few arguments.'), 52 'Used when a function call passes too few arguments.'),
55 'E1121': ('Too many positional arguments for %s call', 53 'E1121': ('Too many positional arguments for %s call',
56 'too-many-function-args', 54 'too-many-function-args',
57 'Used when a function call passes too many positional \ 55 'Used when a function call passes too many positional \
58 arguments.'), 56 arguments.'),
59 'E1122': ('Duplicate keyword argument %r in %s call',
60 'duplicate-keyword-arg',
61 'Used when a function call passes the same keyword argument \
62 multiple times.',
63 {'maxversion': (2, 6)}),
64 'E1123': ('Unexpected keyword argument %r in %s call', 57 'E1123': ('Unexpected keyword argument %r in %s call',
65 'unexpected-keyword-arg', 58 'unexpected-keyword-arg',
66 'Used when a function call passes a keyword argument that \ 59 'Used when a function call passes a keyword argument that \
67 doesn\'t correspond to one of the function\'s parameter names.'), 60 doesn\'t correspond to one of the function\'s parameter names.'),
68 'E1124': ('Argument %r passed by position and keyword in %s call', 61 'E1124': ('Argument %r passed by position and keyword in %s call',
69 'redundant-keyword-arg', 62 'redundant-keyword-arg',
70 'Used when a function call would result in assigning multiple \ 63 'Used when a function call would result in assigning multiple \
71 values to a function parameter, one value from a positional \ 64 values to a function parameter, one value from a positional \
72 argument and one from a keyword argument.'), 65 argument and one from a keyword argument.'),
73 'E1125': ('Missing mandatory keyword argument %r in %s call', 66 'E1125': ('Missing mandatory keyword argument %r in %s call',
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after
185 if self.config.zope: 178 if self.config.zope:
186 self.generated_members.extend(('REQUEST', 'acl_users', 'aq_parent')) 179 self.generated_members.extend(('REQUEST', 'acl_users', 'aq_parent'))
187 180
188 def visit_assattr(self, node): 181 def visit_assattr(self, node):
189 if isinstance(node.ass_type(), astroid.AugAssign): 182 if isinstance(node.ass_type(), astroid.AugAssign):
190 self.visit_getattr(node) 183 self.visit_getattr(node)
191 184
192 def visit_delattr(self, node): 185 def visit_delattr(self, node):
193 self.visit_getattr(node) 186 self.visit_getattr(node)
194 187
195 @check_messages('no-member', 'maybe-no-member') 188 @check_messages('no-member')
196 def visit_getattr(self, node): 189 def visit_getattr(self, node):
197 """check that the accessed attribute exists 190 """check that the accessed attribute exists
198 191
199 to avoid to much false positives for now, we'll consider the code as 192 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. 193 correct if a single of the inferred nodes has the accessed attribute.
201 194
202 function/method, super call and metaclasses are ignored 195 function/method, super call and metaclasses are ignored
203 """ 196 """
204 # generated_members may containt regular expressions 197 # generated_members may containt regular expressions
205 # (surrounded by quote `"` and followed by a comma `,`) 198 # (surrounded by quote `"` and followed by a comma `,`)
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
247 # XXX method / function 240 # XXX method / function
248 continue 241 continue
249 except NotFoundError: 242 except NotFoundError:
250 if isinstance(owner, astroid.Function) and owner.decorators: 243 if isinstance(owner, astroid.Function) and owner.decorators:
251 continue 244 continue
252 if isinstance(owner, Instance) and owner.has_dynamic_getattr(): 245 if isinstance(owner, Instance) and owner.has_dynamic_getattr():
253 continue 246 continue
254 # explicit skipping of module member access 247 # explicit skipping of module member access
255 if owner.root().name in self.config.ignored_modules: 248 if owner.root().name in self.config.ignored_modules:
256 continue 249 continue
250 if isinstance(owner, astroid.Class):
251 # Look up in the metaclass only if the owner is itself
252 # a class.
253 # TODO: getattr doesn't return by default members
254 # from the metaclass, because handling various cases
255 # of methods accessible from the metaclass itself
256 # and/or subclasses only is too complicated for little to
257 # no benefit.
258 metaclass = owner.metaclass()
259 try:
260 if metaclass and metaclass.getattr(node.attrname):
261 continue
262 except NotFoundError:
263 pass
257 missingattr.add((owner, name)) 264 missingattr.add((owner, name))
258 continue 265 continue
259 # stop on the first found 266 # stop on the first found
260 break 267 break
261 else: 268 else:
262 # we have not found any node with the attributes, display the 269 # we have not found any node with the attributes, display the
263 # message for infered nodes 270 # message for infered nodes
264 done = set() 271 done = set()
265 for owner, name in missingattr: 272 for owner, name in missingattr:
266 if isinstance(owner, Instance): 273 if isinstance(owner, Instance):
267 actual = owner._proxied 274 actual = owner._proxied
268 else: 275 else:
269 actual = owner 276 actual = owner
270 if actual in done: 277 if actual in done:
271 continue 278 continue
272 done.add(actual) 279 done.add(actual)
273 if inference_failure: 280 confidence = INFERENCE if not inference_failure else INFERENCE_F AILURE
274 msgid = 'maybe-no-member' 281 self.add_message('no-member', node=node,
275 else:
276 msgid = 'no-member'
277 self.add_message(msgid, node=node,
278 args=(owner.display_type(), name, 282 args=(owner.display_type(), name,
279 node.attrname)) 283 node.attrname),
284 confidence=confidence)
280 285
281 @check_messages('assignment-from-no-return', 'assignment-from-none') 286 @check_messages('assignment-from-no-return', 'assignment-from-none')
282 def visit_assign(self, node): 287 def visit_assign(self, node):
283 """check that if assigning to a function call, the function is 288 """check that if assigning to a function call, the function is
284 possibly returning something valuable 289 possibly returning something valuable
285 """ 290 """
286 if not isinstance(node.value, astroid.CallFunc): 291 if not isinstance(node.value, astroid.CallFunc):
287 return 292 return
288 function_node = safe_infer(node.value.func) 293 function_node = safe_infer(node.value.func)
289 # skip class, generator and incomplete function definition 294 # skip class, generator and incomplete function definition
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
326 klass = safe_infer(expr) 331 klass = safe_infer(expr)
327 if (klass is None or klass is astroid.YES or 332 if (klass is None or klass is astroid.YES or
328 not isinstance(klass, astroid.Instance)): 333 not isinstance(klass, astroid.Instance)):
329 return 334 return
330 335
331 try: 336 try:
332 attrs = klass._proxied.getattr(node.func.attrname) 337 attrs = klass._proxied.getattr(node.func.attrname)
333 except astroid.NotFoundError: 338 except astroid.NotFoundError:
334 return 339 return
335 340
336 stop_checking = False
337 for attr in attrs: 341 for attr in attrs:
338 if attr is astroid.YES: 342 if attr is astroid.YES:
339 continue 343 continue
340 if stop_checking:
341 break
342 if not isinstance(attr, astroid.Function): 344 if not isinstance(attr, astroid.Function):
343 continue 345 continue
344 346
345 # Decorated, see if it is decorated with a property 347 # Decorated, see if it is decorated with a property
346 if not attr.decorators: 348 if decorated_with_property(attr):
347 continue 349 self.add_message('not-callable', node=node,
348 for decorator in attr.decorators.nodes: 350 args=node.func.as_string())
349 if not isinstance(decorator, astroid.Name): 351 break
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 352
374 @check_messages(*(list(MSGS.keys()))) 353 @check_messages(*(list(MSGS.keys())))
375 def visit_callfunc(self, node): 354 def visit_callfunc(self, node):
376 """check that called functions/methods are inferred to callable objects, 355 """check that called functions/methods are inferred to callable objects,
377 and that the arguments passed to the function match the parameters in 356 and that the arguments passed to the function match the parameters in
378 the inferred function's definition 357 the inferred function's definition
379 """ 358 """
380 # Build the set of keyword arguments, checking for duplicate keywords, 359 # Build the set of keyword arguments, checking for duplicate keywords,
381 # and count the positional arguments. 360 # and count the positional arguments.
382 keyword_args = set() 361 keyword_args = set()
383 num_positional_args = 0 362 num_positional_args = 0
384 for arg in node.args: 363 for arg in node.args:
385 if isinstance(arg, astroid.Keyword): 364 if isinstance(arg, astroid.Keyword):
386 keyword = arg.arg 365 keyword_args.add(arg.arg)
387 if keyword in keyword_args:
388 self.add_message('duplicate-keyword-arg', node=node,
389 args=(keyword, 'function'))
390 keyword_args.add(keyword)
391 else: 366 else:
392 num_positional_args += 1 367 num_positional_args += 1
393 368
394 called = safe_infer(node.func) 369 called = safe_infer(node.func)
395 # only function, generator and object defining __call__ are allowed 370 # only function, generator and object defining __call__ are allowed
396 if called is not None and not called.callable(): 371 if called is not None and not called.callable():
397 self.add_message('not-callable', node=node, 372 self.add_message('not-callable', node=node,
398 args=node.func.as_string()) 373 args=node.func.as_string())
399 374
400 self._check_uninferable_callfunc(node) 375 self._check_uninferable_callfunc(node)
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after
542 @check_messages('invalid-sequence-index') 517 @check_messages('invalid-sequence-index')
543 def visit_index(self, node): 518 def visit_index(self, node):
544 if not node.parent or not hasattr(node.parent, "value"): 519 if not node.parent or not hasattr(node.parent, "value"):
545 return 520 return
546 521
547 # Look for index operations where the parent is a sequence type. 522 # Look for index operations where the parent is a sequence type.
548 # If the types can be determined, only allow indices to be int, 523 # If the types can be determined, only allow indices to be int,
549 # slice or instances with __index__. 524 # slice or instances with __index__.
550 525
551 parent_type = safe_infer(node.parent.value) 526 parent_type = safe_infer(node.parent.value)
552
553 if not isinstance(parent_type, (astroid.Class, astroid.Instance)): 527 if not isinstance(parent_type, (astroid.Class, astroid.Instance)):
554 return 528 return
555 529
556 # Determine what method on the parent this index will use 530 # 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 531 # 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. 532 # node determines if the Subscript is a get, set, or delete operation.
559 operation = node.parent.parent 533 operation = node.parent.parent
560 if isinstance(operation, astroid.Assign): 534 if isinstance(operation, astroid.Assign):
561 methodname = '__setitem__' 535 methodname = '__setitem__'
562 elif isinstance(operation, astroid.Delete): 536 elif isinstance(operation, astroid.Delete):
563 methodname = '__delitem__' 537 methodname = '__delitem__'
564 else: 538 else:
565 methodname = '__getitem__' 539 methodname = '__getitem__'
566 540
567 # Check if this instance's __getitem__, __setitem__, or __delitem__, as 541 # Check if this instance's __getitem__, __setitem__, or __delitem__, as
568 # appropriate to the statement, is implemented in a builtin sequence 542 # appropriate to the statement, is implemented in a builtin sequence
569 # type. This way we catch subclasses of sequence types but skip classes 543 # type. This way we catch subclasses of sequence types but skip classes
570 # that override __getitem__ and which may allow non-integer indices. 544 # that override __getitem__ and which may allow non-integer indices.
571 try: 545 try:
572 methods = parent_type.getattr(methodname) 546 methods = parent_type.getattr(methodname)
573 if methods is astroid.YES: 547 if methods is astroid.YES:
574 return 548 return
575 itemmethod = methods[0] 549 itemmethod = methods[0]
576 except (astroid.NotFoundError, IndexError): 550 except (astroid.NotFoundError, IndexError):
577 return 551 return
578 552
579 if not isinstance(itemmethod, astroid.Function): 553 if not isinstance(itemmethod, astroid.Function):
580 return 554 return
581
582 if itemmethod.root().name != BUILTINS: 555 if itemmethod.root().name != BUILTINS:
583 return 556 return
584
585 if not itemmethod.parent: 557 if not itemmethod.parent:
586 return 558 return
587
588 if itemmethod.parent.name not in SEQUENCE_TYPES: 559 if itemmethod.parent.name not in SEQUENCE_TYPES:
589 return 560 return
590 561
591 # For ExtSlice objects coming from visit_extslice, no further 562 # For ExtSlice objects coming from visit_extslice, no further
592 # inference is necessary, since if we got this far the ExtSlice 563 # inference is necessary, since if we got this far the ExtSlice
593 # is an error. 564 # is an error.
594 if isinstance(node, astroid.ExtSlice): 565 if isinstance(node, astroid.ExtSlice):
595 index_type = node 566 index_type = node
596 else: 567 else:
597 index_type = safe_infer(node) 568 index_type = safe_infer(node)
598
599 if index_type is None or index_type is astroid.YES: 569 if index_type is None or index_type is astroid.YES:
600 return 570 return
601 571
602 # Constants must be of type int 572 # Constants must be of type int
603 if isinstance(index_type, astroid.Const): 573 if isinstance(index_type, astroid.Const):
604 if isinstance(index_type.value, int): 574 if isinstance(index_type.value, int):
605 return 575 return
606 # Instance values must be int, slice, or have an __index__ method 576 # Instance values must be int, slice, or have an __index__ method
607 elif isinstance(index_type, astroid.Instance): 577 elif isinstance(index_type, astroid.Instance):
608 if index_type.pytype() in (BUILTINS + '.int', BUILTINS + '.slice'): 578 if index_type.pytype() in (BUILTINS + '.int', BUILTINS + '.slice'):
609 return 579 return
610
611 try: 580 try:
612 index_type.getattr('__index__') 581 index_type.getattr('__index__')
613 return 582 return
614 except astroid.NotFoundError: 583 except astroid.NotFoundError:
615 pass 584 pass
616 585
617 # Anything else is an error 586 # Anything else is an error
618 self.add_message('invalid-sequence-index', node=node) 587 self.add_message('invalid-sequence-index', node=node)
619 588
620 @check_messages('invalid-slice-index') 589 @check_messages('invalid-slice-index')
621 def visit_slice(self, node): 590 def visit_slice(self, node):
622 # Check the type of each part of the slice 591 # Check the type of each part of the slice
623 for index in (node.lower, node.upper, node.step): 592 for index in (node.lower, node.upper, node.step):
624 if index is None: 593 if index is None:
625 continue 594 continue
626 595
627 index_type = safe_infer(index) 596 index_type = safe_infer(index)
628
629 if index_type is None or index_type is astroid.YES: 597 if index_type is None or index_type is astroid.YES:
630 continue 598 continue
631 599
632 # Constants must of type int or None 600 # Constants must of type int or None
633 if isinstance(index_type, astroid.Const): 601 if isinstance(index_type, astroid.Const):
634 if isinstance(index_type.value, (int, type(None))): 602 if isinstance(index_type.value, (int, type(None))):
635 continue 603 continue
636 # Instance values must be of type int, None or an object 604 # Instance values must be of type int, None or an object
637 # with __index__ 605 # with __index__
638 elif isinstance(index_type, astroid.Instance): 606 elif isinstance(index_type, astroid.Instance):
639 if index_type.pytype() in (BUILTINS + '.int', 607 if index_type.pytype() in (BUILTINS + '.int',
640 BUILTINS + '.NoneType'): 608 BUILTINS + '.NoneType'):
641 continue 609 continue
642 610
643 try: 611 try:
644 index_type.getattr('__index__') 612 index_type.getattr('__index__')
645 return 613 return
646 except astroid.NotFoundError: 614 except astroid.NotFoundError:
647 pass 615 pass
648 616
649 # Anything else is an error 617 # Anything else is an error
650 self.add_message('invalid-slice-index', node=node) 618 self.add_message('invalid-slice-index', node=node)
651 619
652 def register(linter): 620 def register(linter):
653 """required method to auto register this checker """ 621 """required method to auto register this checker """
654 linter.register_checker(TypeChecker(linter)) 622 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