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