typecheck.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. # Copyright (c) 2006-2010 LOGILAB S.A. (Paris, FRANCE).
  2. # http://www.logilab.fr/ -- mailto:contact@logilab.fr
  3. #
  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
  6. # Foundation; either version 2 of the License, or (at your option) any later
  7. # version.
  8. #
  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
  11. # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  12. #
  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.,
  15. # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  16. """try to find more bugs in the code using astng inference capabilities
  17. """
  18. import re
  19. import shlex
  20. from logilab import astng
  21. from logilab.astng import InferenceError, NotFoundError, YES, Instance
  22. from pylint.interfaces import IASTNGChecker
  23. from pylint.checkers import BaseChecker
  24. from pylint.checkers.utils import safe_infer, is_super, check_messages
  25. MSGS = {
  26. 'E1101': ('%s %r has no %r member',
  27. 'Used when a variable is accessed for an unexistent member.'),
  28. 'E1102': ('%s is not callable',
  29. 'Used when an object being called has been inferred to a non \
  30. callable object'),
  31. 'E1103': ('%s %r has no %r member (but some types could not be inferred)',
  32. 'Used when a variable is accessed for an unexistent member, but \
  33. astng was not able to interpret all possible types of this \
  34. variable.'),
  35. 'E1111': ('Assigning to function call which doesn\'t return',
  36. 'Used when an assignment is done on a function call but the \
  37. inferred function doesn\'t return anything.'),
  38. 'W1111': ('Assigning to function call which only returns None',
  39. 'Used when an assignment is done on a function call but the \
  40. inferred function returns nothing but None.'),
  41. 'E1120': ('No value passed for parameter %s in function call',
  42. 'Used when a function call passes too few arguments.'),
  43. 'E1121': ('Too many positional arguments for function call',
  44. 'Used when a function call passes too many positional \
  45. arguments.'),
  46. 'E1122': ('Duplicate keyword argument %r in function call',
  47. 'Used when a function call passes the same keyword argument \
  48. multiple times.'),
  49. 'E1123': ('Passing unexpected keyword argument %r in function call',
  50. 'Used when a function call passes a keyword argument that \
  51. doesn\'t correspond to one of the function\'s parameter names.'),
  52. 'E1124': ('Multiple values passed for parameter %r in function call',
  53. 'Used when a function call would result in assigning multiple \
  54. values to a function parameter, one value from a positional \
  55. argument and one from a keyword argument.'),
  56. }
  57. class TypeChecker(BaseChecker):
  58. """try to find bugs in the code using type inference
  59. """
  60. __implements__ = (IASTNGChecker,)
  61. # configuration section name
  62. name = 'typecheck'
  63. # messages
  64. msgs = MSGS
  65. priority = -1
  66. # configuration options
  67. options = (('ignore-mixin-members',
  68. {'default' : True, 'type' : 'yn', 'metavar': '<y_or_n>',
  69. 'help' : 'Tells whether missing members accessed in mixin \
  70. class should be ignored. A mixin class is detected if its name ends with \
  71. "mixin" (case insensitive).'}
  72. ),
  73. ('ignored-classes',
  74. {'default' : ('SQLObject',),
  75. 'type' : 'csv',
  76. 'metavar' : '<members names>',
  77. 'help' : 'List of classes names for which member attributes \
  78. should not be checked (useful for classes with attributes dynamically set).'}
  79. ),
  80. ('zope',
  81. {'default' : False, 'type' : 'yn', 'metavar': '<y_or_n>',
  82. 'help' : 'When zope mode is activated, add a predefined set \
  83. of Zope acquired attributes to generated-members.'}
  84. ),
  85. ('generated-members',
  86. {'default' : (
  87. 'REQUEST', 'acl_users', 'aq_parent'),
  88. 'type' : 'string',
  89. 'metavar' : '<members names>',
  90. 'help' : 'List of members which are set dynamically and \
  91. missed by pylint inference system, and so shouldn\'t trigger E0201 when \
  92. accessed. Python regular expressions are accepted.'}
  93. ),
  94. )
  95. def open(self):
  96. # do this in open since config not fully initialized in __init__
  97. self.generated_members = list(self.config.generated_members)
  98. if self.config.zope:
  99. self.generated_members.extend(('REQUEST', 'acl_users', 'aq_parent'))
  100. def visit_assattr(self, node):
  101. if isinstance(node.ass_type(), astng.AugAssign):
  102. self.visit_getattr(node)
  103. def visit_delattr(self, node):
  104. self.visit_getattr(node)
  105. @check_messages('E1101', 'E1103')
  106. def visit_getattr(self, node):
  107. """check that the accessed attribute exists
  108. to avoid to much false positives for now, we'll consider the code as
  109. correct if a single of the inferred nodes has the accessed attribute.
  110. function/method, super call and metaclasses are ignored
  111. """
  112. # generated_members may containt regular expressions
  113. # (surrounded by quote `"` and followed by a comma `,`)
  114. # REQUEST,aq_parent,"[a-zA-Z]+_set{1,2}"' =>
  115. # ('REQUEST', 'aq_parent', '[a-zA-Z]+_set{1,2}')
  116. if isinstance(self.config.generated_members, str):
  117. gen = shlex.shlex(self.config.generated_members)
  118. gen.whitespace += ','
  119. gen.wordchars += '[]-+'
  120. self.config.generated_members = tuple(tok.strip('"') for tok in gen)
  121. for pattern in self.config.generated_members:
  122. # attribute is marked as generated, stop here
  123. if re.match(pattern, node.attrname):
  124. return
  125. try:
  126. infered = list(node.expr.infer())
  127. except InferenceError:
  128. return
  129. # list of (node, nodename) which are missing the attribute
  130. missingattr = set()
  131. ignoremim = self.config.ignore_mixin_members
  132. inference_failure = False
  133. for owner in infered:
  134. # skip yes object
  135. if owner is YES:
  136. inference_failure = True
  137. continue
  138. # skip None anyway
  139. if isinstance(owner, astng.Const) and owner.value is None:
  140. continue
  141. # XXX "super" / metaclass call
  142. if is_super(owner) or getattr(owner, 'type', None) == 'metaclass':
  143. continue
  144. name = getattr(owner, 'name', 'None')
  145. if name in self.config.ignored_classes:
  146. continue
  147. if ignoremim and name[-5:].lower() == 'mixin':
  148. continue
  149. try:
  150. if not [n for n in owner.getattr(node.attrname)
  151. if not isinstance(n.statement(), astng.AugAssign)]:
  152. missingattr.add((owner, name))
  153. continue
  154. except AttributeError:
  155. # XXX method / function
  156. continue
  157. except NotFoundError:
  158. if isinstance(owner, astng.Function) and owner.decorators:
  159. continue
  160. if isinstance(owner, Instance) and owner.has_dynamic_getattr():
  161. continue
  162. # explicit skipping of optparse'Values class
  163. if owner.name == 'Values' and owner.root().name == 'optparse':
  164. continue
  165. missingattr.add((owner, name))
  166. continue
  167. # stop on the first found
  168. break
  169. else:
  170. # we have not found any node with the attributes, display the
  171. # message for infered nodes
  172. done = set()
  173. for owner, name in missingattr:
  174. if isinstance(owner, Instance):
  175. actual = owner._proxied
  176. else:
  177. actual = owner
  178. if actual in done:
  179. continue
  180. done.add(actual)
  181. if inference_failure:
  182. msgid = 'E1103'
  183. else:
  184. msgid = 'E1101'
  185. self.add_message(msgid, node=node,
  186. args=(owner.display_type(), name,
  187. node.attrname))
  188. def visit_assign(self, node):
  189. """check that if assigning to a function call, the function is
  190. possibly returning something valuable
  191. """
  192. if not isinstance(node.value, astng.CallFunc):
  193. return
  194. function_node = safe_infer(node.value.func)
  195. # skip class, generator and incomplete function definition
  196. if not (isinstance(function_node, astng.Function) and
  197. function_node.root().fully_defined()):
  198. return
  199. if function_node.is_generator() \
  200. or function_node.is_abstract(pass_is_abstract=False):
  201. return
  202. returns = list(function_node.nodes_of_class(astng.Return,
  203. skip_klass=astng.Function))
  204. if len(returns) == 0:
  205. self.add_message('E1111', node=node)
  206. else:
  207. for rnode in returns:
  208. if not (isinstance(rnode.value, astng.Const)
  209. and rnode.value.value is None):
  210. break
  211. else:
  212. self.add_message('W1111', node=node)
  213. def visit_callfunc(self, node):
  214. """check that called functions/methods are inferred to callable objects,
  215. and that the arguments passed to the function match the parameters in
  216. the inferred function's definition
  217. """
  218. # Build the set of keyword arguments, checking for duplicate keywords,
  219. # and count the positional arguments.
  220. keyword_args = set()
  221. num_positional_args = 0
  222. for arg in node.args:
  223. if isinstance(arg, astng.Keyword):
  224. keyword = arg.arg
  225. if keyword in keyword_args:
  226. self.add_message('E1122', node=node, args=keyword)
  227. keyword_args.add(keyword)
  228. else:
  229. num_positional_args += 1
  230. called = safe_infer(node.func)
  231. # only function, generator and object defining __call__ are allowed
  232. if called is not None and not called.callable():
  233. self.add_message('E1102', node=node, args=node.func.as_string())
  234. # Note that BoundMethod is a subclass of UnboundMethod (huh?), so must
  235. # come first in this 'if..else'.
  236. if isinstance(called, astng.BoundMethod):
  237. # Bound methods have an extra implicit 'self' argument.
  238. num_positional_args += 1
  239. elif isinstance(called, astng.UnboundMethod):
  240. if called.decorators is not None:
  241. for d in called.decorators.nodes:
  242. if isinstance(d, astng.Name) and (d.name == 'classmethod'):
  243. # Class methods have an extra implicit 'cls' argument.
  244. num_positional_args += 1
  245. break
  246. elif (isinstance(called, astng.Function) or
  247. isinstance(called, astng.Lambda)):
  248. pass
  249. else:
  250. return
  251. if called.args.args is None:
  252. # Built-in functions have no argument information.
  253. return
  254. if len( called.argnames() ) != len( set( called.argnames() ) ):
  255. # Duplicate parameter name (see E9801). We can't really make sense
  256. # of the function call in this case, so just return.
  257. return
  258. # Analyze the list of formal parameters.
  259. num_mandatory_parameters = len(called.args.args) - len(called.args.defaults)
  260. parameters = []
  261. parameter_name_to_index = {}
  262. for i, arg in enumerate(called.args.args):
  263. if isinstance(arg, astng.Tuple):
  264. name = None
  265. # Don't store any parameter names within the tuple, since those
  266. # are not assignable from keyword arguments.
  267. else:
  268. if isinstance(arg, astng.Keyword):
  269. name = arg.arg
  270. else:
  271. assert isinstance(arg, astng.AssName)
  272. # This occurs with:
  273. # def f( (a), (b) ): pass
  274. name = arg.name
  275. parameter_name_to_index[name] = i
  276. if i >= num_mandatory_parameters:
  277. defval = called.args.defaults[i - num_mandatory_parameters]
  278. else:
  279. defval = None
  280. parameters.append([(name, defval), False])
  281. # Match the supplied arguments against the function parameters.
  282. # 1. Match the positional arguments.
  283. for i in range(num_positional_args):
  284. if i < len(parameters):
  285. parameters[i][1] = True
  286. elif called.args.vararg is not None:
  287. # The remaining positional arguments get assigned to the *args
  288. # parameter.
  289. break
  290. else:
  291. # Too many positional arguments.
  292. self.add_message('E1121', node=node)
  293. break
  294. # 2. Match the keyword arguments.
  295. for keyword in keyword_args:
  296. if keyword in parameter_name_to_index:
  297. i = parameter_name_to_index[keyword]
  298. if parameters[i][1]:
  299. # Duplicate definition of function parameter.
  300. self.add_message('E1124', node=node, args=keyword)
  301. else:
  302. parameters[i][1] = True
  303. elif called.args.kwarg is not None:
  304. # The keyword argument gets assigned to the **kwargs parameter.
  305. pass
  306. else:
  307. # Unexpected keyword argument.
  308. self.add_message('E1123', node=node, args=keyword)
  309. # 3. Match the *args, if any. Note that Python actually processes
  310. # *args _before_ any keyword arguments, but we wait until after
  311. # looking at the keyword arguments so as to make a more conservative
  312. # guess at how many values are in the *args sequence.
  313. if node.starargs is not None:
  314. for i in range(num_positional_args, len(parameters)):
  315. [(name, defval), assigned] = parameters[i]
  316. # Assume that *args provides just enough values for all
  317. # non-default parameters after the last parameter assigned by
  318. # the positional arguments but before the first parameter
  319. # assigned by the keyword arguments. This is the best we can
  320. # get without generating any false positives.
  321. if (defval is not None) or assigned:
  322. break
  323. parameters[i][1] = True
  324. # 4. Match the **kwargs, if any.
  325. if node.kwargs is not None:
  326. for i, [(name, defval), assigned] in enumerate(parameters):
  327. # Assume that *kwargs provides values for all remaining
  328. # unassigned named parameters.
  329. if name is not None:
  330. parameters[i][1] = True
  331. else:
  332. # **kwargs can't assign to tuples.
  333. pass
  334. # Check that any parameters without a default have been assigned
  335. # values.
  336. for [(name, defval), assigned] in parameters:
  337. if (defval is None) and not assigned:
  338. if name is None:
  339. display = '<tuple>'
  340. else:
  341. display_name = repr(name)
  342. self.add_message('E1120', node=node, args=display_name)
  343. def register(linter):
  344. """required method to auto register this checker """
  345. linter.register_checker(TypeChecker(linter))