Statistics
| Revision:

gvsig-scripting / org.gvsig.scripting / trunk / org.gvsig.scripting / org.gvsig.scripting.app / org.gvsig.scripting.app.mainplugin / src / main / resources-plugin / scripting / lib / pylint / checkers / base.py @ 745

History | View | Annotate | Download (84.4 KB)

1
# Copyright (c) 2003-2013 LOGILAB S.A. (Paris, FRANCE).
2
# http://www.logilab.fr/ -- mailto:contact@logilab.fr
3
# Copyright (c) 2009-2010 Arista Networks, Inc.
4
#
5
# This program is free software; you can redistribute it and/or modify it under
6
# the terms of the GNU General Public License as published by the Free Software
7
# Foundation; either version 2 of the License, or (at your option) any later
8
# version.
9
#
10
# This program is distributed in the hope that it will be useful, but WITHOUT
11
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License along with
15
# this program; if not, write to the Free Software Foundation, Inc.,
16
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
"""basic checker for Python code"""
18

    
19
import collections
20
import itertools
21
import sys
22
import re
23

    
24
import six
25
from six.moves import zip  # pylint: disable=redefined-builtin
26

    
27
import astroid
28
import astroid.bases
29
import astroid.scoped_nodes
30
from astroid import are_exclusive, InferenceError
31

    
32
from pylint.interfaces import (IAstroidChecker, ITokenChecker, INFERENCE,
33
                               INFERENCE_FAILURE, HIGH)
34
from pylint.utils import EmptyReport, deprecated_option
35
from pylint.reporters import diff_string
36
from pylint.checkers import BaseChecker, BaseTokenChecker
37
from pylint.checkers.utils import (
38
    check_messages,
39
    clobber_in_except,
40
    is_builtin_object,
41
    is_inside_except,
42
    overrides_a_method,
43
    get_argument_from_call,
44
    node_frame_class,
45
    NoSuchArgumentError,
46
    error_of_type,
47
    unimplemented_abstract_methods,
48
    has_known_bases,
49
    safe_infer
50
    )
51
from pylint.reporters.ureports.nodes import Table
52

    
53

    
54
# regex for class/function/variable/constant name
55
CLASS_NAME_RGX = re.compile('[A-Z_][a-zA-Z0-9]+$')
56
MOD_NAME_RGX = re.compile('(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$')
57
CONST_NAME_RGX = re.compile('(([A-Z_][A-Z0-9_]*)|(__.*__))$')
58
COMP_VAR_RGX = re.compile('[A-Za-z_][A-Za-z0-9_]*$')
59
DEFAULT_NAME_RGX = re.compile('[a-z_][a-z0-9_]{2,30}$')
60
CLASS_ATTRIBUTE_RGX = re.compile(r'([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$')
61
# do not require a doc string on private/system methods
62
NO_REQUIRED_DOC_RGX = re.compile('^_')
63
REVERSED_PROTOCOL_METHOD = '__reversed__'
64
SEQUENCE_PROTOCOL_METHODS = ('__getitem__', '__len__')
65
REVERSED_METHODS = (SEQUENCE_PROTOCOL_METHODS,
66
                    (REVERSED_PROTOCOL_METHOD, ))
67
TYPECHECK_COMPARISON_OPERATORS = frozenset(('is', 'is not', '==',
68
                                            '!=', 'in', 'not in'))
69
LITERAL_NODE_TYPES = (astroid.Const, astroid.Dict, astroid.List, astroid.Set)
70
UNITTEST_CASE = 'unittest.case'
71
BUILTINS = six.moves.builtins.__name__
72
TYPE_QNAME = "%s.type" % BUILTINS
73
PY33 = sys.version_info >= (3, 3)
74
PY3K = sys.version_info >= (3, 0)
75
PY35 = sys.version_info >= (3, 5)
76
BAD_FUNCTIONS = ['map', 'filter']
77
if sys.version_info < (3, 0):
78
    BAD_FUNCTIONS.append('input')
79

    
80
# Some hints regarding the use of bad builtins.
81
BUILTIN_HINTS = {
82
    'map': 'Using a list comprehension can be clearer.',
83
}
84
BUILTIN_HINTS['filter'] = BUILTIN_HINTS['map']
85

    
86
# Name categories that are always consistent with all naming conventions.
87
EXEMPT_NAME_CATEGORIES = set(('exempt', 'ignore'))
88

    
89
# A mapping from builtin-qname -> symbol, to be used when generating messages
90
# about dangerous default values as arguments
91
DEFAULT_ARGUMENT_SYMBOLS = dict(
92
    zip(['.'.join([BUILTINS, x]) for x in ('set', 'dict', 'list')],
93
        ['set()', '{}', '[]'])
94
)
95
REVERSED_COMPS = {'<': '>', '<=': '>=', '>': '<', '>=': '<='}
96

    
97
del re
98

    
99
def _redefines_import(node):
100
    """ Detect that the given node (AssName) is inside an
101
    exception handler and redefines an import from the tryexcept body.
102
    Returns True if the node redefines an import, False otherwise.
103
    """
104
    current = node
105
    while current and not isinstance(current.parent, astroid.ExceptHandler):
106
        current = current.parent
107
    if not current or not error_of_type(current.parent, ImportError):
108
        return False
109
    try_block = current.parent.parent
110
    for import_node in try_block.nodes_of_class((astroid.ImportFrom, astroid.Import)):
111
        for name, alias in import_node.names:
112
            if alias:
113
                if alias == node.name:
114
                    return True
115
            elif name == node.name:
116
                return True
117
    return False
118

    
119
def in_loop(node):
120
    """return True if the node is inside a kind of for loop"""
121
    parent = node.parent
122
    while parent is not None:
123
        if isinstance(parent, (astroid.For, astroid.ListComp, astroid.SetComp,
124
                               astroid.DictComp, astroid.GeneratorExp)):
125
            return True
126
        parent = parent.parent
127
    return False
128

    
129
def in_nested_list(nested_list, obj):
130
    """return true if the object is an element of <nested_list> or of a nested
131
    list
132
    """
133
    for elmt in nested_list:
134
        if isinstance(elmt, (list, tuple)):
135
            if in_nested_list(elmt, obj):
136
                return True
137
        elif elmt == obj:
138
            return True
139
    return False
140

    
141
def _loop_exits_early(loop):
142
    """Returns true if a loop has a break statement in its body."""
143
    loop_nodes = (astroid.For, astroid.While)
144
    # Loop over body explicitly to avoid matching break statements
145
    # in orelse.
146
    for child in loop.body:
147
        if isinstance(child, loop_nodes):
148
            # break statement may be in orelse of child loop.
149
            # pylint: disable=superfluous-parens
150
            for orelse in (child.orelse or ()):
151
                for _ in orelse.nodes_of_class(astroid.Break, skip_klass=loop_nodes):
152
                    return True
153
            continue
154
        for _ in child.nodes_of_class(astroid.Break, skip_klass=loop_nodes):
155
            return True
156
    return False
157

    
158
def _is_multi_naming_match(match, node_type, confidence):
159
    return (match is not None and
160
            match.lastgroup is not None and
161
            match.lastgroup not in EXEMPT_NAME_CATEGORIES
162
            and (node_type != 'method' or confidence != INFERENCE_FAILURE))
163

    
164

    
165
if sys.version_info < (3, 0):
166
    PROPERTY_CLASSES = set(('__builtin__.property', 'abc.abstractproperty'))
167
else:
168
    PROPERTY_CLASSES = set(('builtins.property', 'abc.abstractproperty'))
169

    
170

    
171
def _determine_function_name_type(node):
172
    """Determine the name type whose regex the a function's name should match.
173

174
    :param node: A function node.
175
    :returns: One of ('function', 'method', 'attr')
176
    """
177
    if not node.is_method():
178
        return 'function'
179
    if node.decorators:
180
        decorators = node.decorators.nodes
181
    else:
182
        decorators = []
183
    for decorator in decorators:
184
        # If the function is a property (decorated with @property
185
        # or @abc.abstractproperty), the name type is 'attr'.
186
        if (isinstance(decorator, astroid.Name) or
187
                (isinstance(decorator, astroid.Attribute) and
188
                 decorator.attrname == 'abstractproperty')):
189
            infered = safe_infer(decorator)
190
            if infered and infered.qname() in PROPERTY_CLASSES:
191
                return 'attr'
192
        # If the function is decorated using the prop_method.{setter,getter}
193
        # form, treat it like an attribute as well.
194
        elif (isinstance(decorator, astroid.Attribute) and
195
              decorator.attrname in ('setter', 'deleter')):
196
            return 'attr'
197
    return 'method'
198

    
199

    
200
def _is_none(node):
201
    return (node is None or
202
            (isinstance(node, astroid.Const) and node.value is None) or
203
            (isinstance(node, astroid.Name)  and node.name == 'None')
204
           )
205

    
206

    
207
def _has_abstract_methods(node):
208
    """
209
    Determine if the given `node` has abstract methods.
210

211
    The methods should be made abstract by decorating them
212
    with `abc` decorators.
213
    """
214
    return len(unimplemented_abstract_methods(node)) > 0
215

    
216

    
217
def report_by_type_stats(sect, stats, old_stats):
218
    """make a report of
219

220
    * percentage of different types documented
221
    * percentage of different types with a bad name
222
    """
223
    # percentage of different types documented and/or with a bad name
224
    nice_stats = {}
225
    for node_type in ('module', 'class', 'method', 'function'):
226
        try:
227
            total = stats[node_type]
228
        except KeyError:
229
            raise EmptyReport()
230
        nice_stats[node_type] = {}
231
        if total != 0:
232
            try:
233
                documented = total - stats['undocumented_'+node_type]
234
                percent = (documented * 100.) / total
235
                nice_stats[node_type]['percent_documented'] = '%.2f' % percent
236
            except KeyError:
237
                nice_stats[node_type]['percent_documented'] = 'NC'
238
            try:
239
                percent = (stats['badname_'+node_type] * 100.) / total
240
                nice_stats[node_type]['percent_badname'] = '%.2f' % percent
241
            except KeyError:
242
                nice_stats[node_type]['percent_badname'] = 'NC'
243
    lines = ('type', 'number', 'old number', 'difference',
244
             '%documented', '%badname')
245
    for node_type in ('module', 'class', 'method', 'function'):
246
        new = stats[node_type]
247
        old = old_stats.get(node_type, None)
248
        if old is not None:
249
            diff_str = diff_string(old, new)
250
        else:
251
            old, diff_str = 'NC', 'NC'
252
        lines += (node_type, str(new), str(old), diff_str,
253
                  nice_stats[node_type].get('percent_documented', '0'),
254
                  nice_stats[node_type].get('percent_badname', '0'))
255
    sect.append(Table(children=lines, cols=6, rheaders=1))
256

    
257
def redefined_by_decorator(node):
258
    """return True if the object is a method redefined via decorator.
259

260
    For example:
261
        @property
262
        def x(self): return self._x
263
        @x.setter
264
        def x(self, value): self._x = value
265
    """
266
    if node.decorators:
267
        for decorator in node.decorators.nodes:
268
            if (isinstance(decorator, astroid.Attribute) and
269
                    getattr(decorator.expr, 'name', None) == node.name):
270
                return True
271
    return False
272

    
273

    
274
def _node_type(node):
275
    """Return the inferred type for `node`
276

277
    If there is more than one possible type, or if inferred type is YES or None,
278
    return None
279
    """
280
    # check there is only one possible type for the assign node. Else we
281
    # don't handle it for now
282
    types = set()
283
    try:
284
        for var_type in node.infer():
285
            if var_type == astroid.YES or _is_none(var_type):
286
                continue
287
            types.add(var_type)
288
            if len(types) > 1:
289
                return
290
    except InferenceError:
291
        return
292
    return types.pop() if types else None
293

    
294

    
295
class _BasicChecker(BaseChecker):
296
    __implements__ = IAstroidChecker
297
    name = 'basic'
298

    
299
class BasicErrorChecker(_BasicChecker):
300
    msgs = {
301
        'E0100': ('__init__ method is a generator',
302
                  'init-is-generator',
303
                  'Used when the special class method __init__ is turned into a '
304
                  'generator by a yield in its body.'),
305
        'E0101': ('Explicit return in __init__',
306
                  'return-in-init',
307
                  'Used when the special class method __init__ has an explicit '
308
                  'return value.'),
309
        'E0102': ('%s already defined line %s',
310
                  'function-redefined',
311
                  'Used when a function / class / method is redefined.'),
312
        'E0103': ('%r not properly in loop',
313
                  'not-in-loop',
314
                  'Used when break or continue keywords are used outside a loop.'),
315
        'E0104': ('Return outside function',
316
                  'return-outside-function',
317
                  'Used when a "return" statement is found outside a function or '
318
                  'method.'),
319
        'E0105': ('Yield outside function',
320
                  'yield-outside-function',
321
                  'Used when a "yield" statement is found outside a function or '
322
                  'method.'),
323
        'E0106': ('Return with argument inside generator',
324
                  'return-arg-in-generator',
325
                  'Used when a "return" statement with an argument is found '
326
                  'outside in a generator function or method (e.g. with some '
327
                  '"yield" statements).',
328
                  {'maxversion': (3, 3)}),
329
        'E0107': ("Use of the non-existent %s operator",
330
                  'nonexistent-operator',
331
                  "Used when you attempt to use the C-style pre-increment or"
332
                  "pre-decrement operator -- and ++, which doesn't exist in Python."),
333
        'E0108': ('Duplicate argument name %s in function definition',
334
                  'duplicate-argument-name',
335
                  'Duplicate argument names in function definitions are syntax'
336
                  ' errors.'),
337
        'E0110': ('Abstract class %r with abstract methods instantiated',
338
                  'abstract-class-instantiated',
339
                  'Used when an abstract class with `abc.ABCMeta` as metaclass '
340
                  'has abstract methods and is instantiated.'),
341
        'W0120': ('Else clause on loop without a break statement',
342
                  'useless-else-on-loop',
343
                  'Loops should only have an else clause if they can exit early '
344
                  'with a break statement, otherwise the statements under else '
345
                  'should be on the same scope as the loop itself.'),
346
        'E0112': ('More than one starred expression in assignment',
347
                  'too-many-star-expressions',
348
                  'Emitted when there are more than one starred '
349
                  'expressions (`*x`) in an assignment. This is a SyntaxError.',
350
                  {'minversion': (3, 0)}),
351
        'E0113': ('Starred assignment target must be in a list or tuple',
352
                  'invalid-star-assignment-target',
353
                  'Emitted when a star expression is used as a starred '
354
                  'assignment target.',
355
                  {'minversion': (3, 0)}),
356
        'E0114': ('Can use starred expression only in assignment target',
357
                  'star-needs-assignment-target',
358
                  'Emitted when a star expression is not used in an '
359
                  'assignment target.',
360
                  {'minversion': (3, 0)}),
361
        'E0115': ('Name %r is nonlocal and global',
362
                  'nonlocal-and-global',
363
                  'Emitted when a name is both nonlocal and global.',
364
                  {'minversion': (3, 0)}),
365
        'E0116': ("'continue' not supported inside 'finally' clause",
366
                  'continue-in-finally',
367
                  'Emitted when the `continue` keyword is found '
368
                  'inside a finally clause, which is a SyntaxError.'),
369
        'E0117': ("nonlocal name %s found without binding",
370
                  'nonlocal-without-binding',
371
                  'Emitted when a nonlocal variable does not have an attached '
372
                  'name somewhere in the parent scopes',
373
                  {'minversion': (3, 0)}),
374
        }
375

    
376
    @check_messages('function-redefined')
377
    def visit_classdef(self, node):
378
        self._check_redefinition('class', node)
379

    
380
    @check_messages('too-many-star-expressions',
381
                    'invalid-star-assignment-target')
382
    def visit_assign(self, node):
383
        starred = list(node.targets[0].nodes_of_class(astroid.Starred))
384
        if len(starred) > 1:
385
            self.add_message('too-many-star-expressions', node=node)
386

    
387
        # Check *a = b
388
        if isinstance(node.targets[0], astroid.Starred):
389
            self.add_message('invalid-star-assignment-target', node=node)
390

    
391
    @check_messages('star-needs-assignment-target')
392
    def visit_starred(self, node):
393
        """Check that a Starred expression is used in an assignment target."""
394
        if isinstance(node.parent, astroid.Call):
395
            # f(*args) is converted to Call(args=[Starred]), so ignore
396
            # them for this check.
397
            return
398
        if PY35 and isinstance(node.parent,
399
                               (astroid.List, astroid.Tuple,
400
                                astroid.Set, astroid.Dict)):
401
            # PEP 448 unpacking.
402
            return
403

    
404
        stmt = node.statement()
405
        if not isinstance(stmt, astroid.Assign):
406
            return
407

    
408
        if stmt.value is node or stmt.value.parent_of(node):
409
            self.add_message('star-needs-assignment-target', node=node)
410

    
411
    @check_messages('init-is-generator', 'return-in-init',
412
                    'function-redefined', 'return-arg-in-generator',
413
                    'duplicate-argument-name', 'nonlocal-and-global')
414
    def visit_functiondef(self, node):
415
        self._check_nonlocal_and_global(node)
416
        if not redefined_by_decorator(node):
417
            self._check_redefinition(node.is_method() and 'method' or 'function', node)
418
        # checks for max returns, branch, return in __init__
419
        returns = node.nodes_of_class(astroid.Return,
420
                                      skip_klass=(astroid.FunctionDef,
421
                                                  astroid.ClassDef))
422
        if node.is_method() and node.name == '__init__':
423
            if node.is_generator():
424
                self.add_message('init-is-generator', node=node)
425
            else:
426
                values = [r.value for r in returns]
427
                # Are we returning anything but None from constructors
428
                if [v for v in values if not _is_none(v)]:
429
                    self.add_message('return-in-init', node=node)
430
        elif node.is_generator():
431
            # make sure we don't mix non-None returns and yields
432
            if not PY33:
433
                for retnode in returns:
434
                    if isinstance(retnode.value, astroid.Const) and \
435
                           retnode.value.value is not None:
436
                        self.add_message('return-arg-in-generator', node=node,
437
                                         line=retnode.fromlineno)
438
        # Check for duplicate names
439
        args = set()
440
        for name in node.argnames():
441
            if name in args:
442
                self.add_message('duplicate-argument-name', node=node, args=(name,))
443
            else:
444
                args.add(name)
445

    
446
    visit_asyncfunctiondef = visit_functiondef
447

    
448
    def _check_nonlocal_and_global(self, node):
449
        """Check that a name is both nonlocal and global."""
450
        def same_scope(current):
451
            return current.scope() is node
452

    
453
        from_iter = itertools.chain.from_iterable
454
        nonlocals = set(from_iter(
455
            child.names for child in node.nodes_of_class(astroid.Nonlocal)
456
            if same_scope(child)))
457
        global_vars = set(from_iter(
458
            child.names for child in node.nodes_of_class(astroid.Global)
459
            if same_scope(child)))
460
        for name in nonlocals.intersection(global_vars):
461
            self.add_message('nonlocal-and-global',
462
                             args=(name, ), node=node)
463

    
464
    @check_messages('return-outside-function')
465
    def visit_return(self, node):
466
        if not isinstance(node.frame(), astroid.FunctionDef):
467
            self.add_message('return-outside-function', node=node)
468

    
469
    @check_messages('yield-outside-function')
470
    def visit_yield(self, node):
471
        self._check_yield_outside_func(node)
472

    
473
    @check_messages('yield-outside-function')
474
    def visit_yieldfrom(self, node):
475
        self._check_yield_outside_func(node)
476

    
477
    @check_messages('not-in-loop', 'continue-in-finally')
478
    def visit_continue(self, node):
479
        self._check_in_loop(node, 'continue')
480

    
481
    @check_messages('not-in-loop')
482
    def visit_break(self, node):
483
        self._check_in_loop(node, 'break')
484

    
485
    @check_messages('useless-else-on-loop')
486
    def visit_for(self, node):
487
        self._check_else_on_loop(node)
488

    
489
    @check_messages('useless-else-on-loop')
490
    def visit_while(self, node):
491
        self._check_else_on_loop(node)
492

    
493
    @check_messages('nonexistent-operator')
494
    def visit_unaryop(self, node):
495
        """check use of the non-existent ++ and -- operator operator"""
496
        if ((node.op in '+-') and
497
                isinstance(node.operand, astroid.UnaryOp) and
498
                (node.operand.op == node.op)):
499
            self.add_message('nonexistent-operator', node=node, args=node.op*2)
500

    
501
    def _check_nonlocal_without_binding(self, node, name):
502
        current_scope = node.scope()
503
        while True:
504
            if current_scope.parent is None:
505
                break
506

    
507
            if not isinstance(current_scope, astroid.FunctionDef):
508
                self.add_message('nonlocal-without-binding', args=(name, ),
509
                                 node=node)
510
                return
511
            else:
512
                if name not in current_scope.locals:
513
                    current_scope = current_scope.parent.scope()
514
                    continue
515
                else:
516
                    # Okay, found it.
517
                    return
518

    
519
        self.add_message('nonlocal-without-binding', args=(name, ), node=node)
520

    
521
    @check_messages('nonlocal-without-binding')
522
    def visit_nonlocal(self, node):
523
        for name in node.names:
524
            self._check_nonlocal_without_binding(node, name)
525

    
526
    @check_messages('abstract-class-instantiated')
527
    def visit_call(self, node):
528
        """ Check instantiating abstract class with
529
        abc.ABCMeta as metaclass.
530
        """
531
        try:
532
            infered = next(node.func.infer())
533
        except astroid.InferenceError:
534
            return
535

    
536
        if not isinstance(infered, astroid.ClassDef):
537
            return
538

    
539
        klass = node_frame_class(node)
540
        if klass is infered:
541
            # Don't emit the warning if the class is instantiated
542
            # in its own body or if the call is not an instance
543
            # creation. If the class is instantiated into its own
544
            # body, we're expecting that it knows what it is doing.
545
            return
546

    
547
        # __init__ was called
548
        metaclass = infered.metaclass()
549
        abstract_methods = _has_abstract_methods(infered)
550
        if metaclass is None:
551
            # Python 3.4 has `abc.ABC`, which won't be detected
552
            # by ClassNode.metaclass()
553
            for ancestor in infered.ancestors():
554
                if ancestor.qname() == 'abc.ABC' and abstract_methods:
555
                    self.add_message('abstract-class-instantiated',
556
                                     args=(infered.name, ),
557
                                     node=node)
558
                    break
559
            return
560
        if metaclass.qname() == 'abc.ABCMeta' and abstract_methods:
561
            self.add_message('abstract-class-instantiated',
562
                             args=(infered.name, ),
563
                             node=node)
564

    
565
    def _check_yield_outside_func(self, node):
566
        if not isinstance(node.frame(), (astroid.FunctionDef, astroid.Lambda)):
567
            self.add_message('yield-outside-function', node=node)
568

    
569
    def _check_else_on_loop(self, node):
570
        """Check that any loop with an else clause has a break statement."""
571
        if node.orelse and not _loop_exits_early(node):
572
            self.add_message('useless-else-on-loop', node=node,
573
                             # This is not optimal, but the line previous
574
                             # to the first statement in the else clause
575
                             # will usually be the one that contains the else:.
576
                             line=node.orelse[0].lineno - 1)
577

    
578
    def _check_in_loop(self, node, node_name):
579
        """check that a node is inside a for or while loop"""
580
        _node = node.parent
581
        while _node:
582
            if isinstance(_node, (astroid.For, astroid.While)):
583
                if node not in _node.orelse:
584
                    return
585

    
586
            if isinstance(_node, (astroid.ClassDef, astroid.FunctionDef)):
587
                break
588
            if (isinstance(_node, astroid.TryFinally)
589
                    and node in _node.finalbody
590
                    and isinstance(node, astroid.Continue)):
591
                self.add_message('continue-in-finally', node=node)
592

    
593
            _node = _node.parent
594

    
595
        self.add_message('not-in-loop', node=node, args=node_name)
596

    
597
    def _check_redefinition(self, redeftype, node):
598
        """check for redefinition of a function / method / class name"""
599
        defined_self = node.parent.frame()[node.name]
600
        if defined_self is not node and not are_exclusive(node, defined_self):
601
            self.add_message('function-redefined', node=node,
602
                             args=(redeftype, defined_self.fromlineno))
603

    
604

    
605

    
606
class BasicChecker(_BasicChecker):
607
    """checks for :
608
    * doc strings
609
    * number of arguments, local variables, branches, returns and statements in
610
functions, methods
611
    * required module attributes
612
    * dangerous default values as arguments
613
    * redefinition of function / method / class
614
    * uses of the global statement
615
    """
616

    
617
    __implements__ = IAstroidChecker
618

    
619
    name = 'basic'
620
    msgs = {
621
        'W0101': ('Unreachable code',
622
                  'unreachable',
623
                  'Used when there is some code behind a "return" or "raise" '
624
                  'statement, which will never be accessed.'),
625
        'W0102': ('Dangerous default value %s as argument',
626
                  'dangerous-default-value',
627
                  'Used when a mutable value as list or dictionary is detected in '
628
                  'a default value for an argument.'),
629
        'W0104': ('Statement seems to have no effect',
630
                  'pointless-statement',
631
                  'Used when a statement doesn\'t have (or at least seems to) '
632
                  'any effect.'),
633
        'W0105': ('String statement has no effect',
634
                  'pointless-string-statement',
635
                  'Used when a string is used as a statement (which of course '
636
                  'has no effect). This is a particular case of W0104 with its '
637
                  'own message so you can easily disable it if you\'re using '
638
                  'those strings as documentation, instead of comments.'),
639
        'W0106': ('Expression "%s" is assigned to nothing',
640
                  'expression-not-assigned',
641
                  'Used when an expression that is not a function call is assigned '
642
                  'to nothing. Probably something else was intended.'),
643
        'W0108': ('Lambda may not be necessary',
644
                  'unnecessary-lambda',
645
                  'Used when the body of a lambda expression is a function call '
646
                  'on the same argument list as the lambda itself; such lambda '
647
                  'expressions are in all but a few cases replaceable with the '
648
                  'function being called in the body of the lambda.'),
649
        'W0109': ("Duplicate key %r in dictionary",
650
                  'duplicate-key',
651
                  'Used when a dictionary expression binds the same key multiple '
652
                  'times.'),
653
        'W0122': ('Use of exec',
654
                  'exec-used',
655
                  'Used when you use the "exec" statement (function for Python '
656
                  '3), to discourage its usage. That doesn\'t '
657
                  'mean you can not use it !'),
658
        'W0123': ('Use of eval',
659
                  'eval-used',
660
                  'Used when you use the "eval" function, to discourage its '
661
                  'usage. Consider using `ast.literal_eval` for safely evaluating '
662
                  'strings containing Python expressions '
663
                  'from untrusted sources. '),
664
        'W0141': ('Used builtin function %s',
665
                  'bad-builtin',
666
                  'Used when a black listed builtin function is used (see the '
667
                  'bad-function option). Usual black listed functions are the ones '
668
                  'like map, or filter , where Python offers now some cleaner '
669
                  'alternative like list comprehension.'),
670
        'W0150': ("%s statement in finally block may swallow exception",
671
                  'lost-exception',
672
                  'Used when a break or a return statement is found inside the '
673
                  'finally clause of a try...finally block: the exceptions raised '
674
                  'in the try clause will be silently swallowed instead of being '
675
                  're-raised.'),
676
        'W0199': ('Assert called on a 2-uple. Did you mean \'assert x,y\'?',
677
                  'assert-on-tuple',
678
                  'A call of assert on a tuple will always evaluate to true if '
679
                  'the tuple is not empty, and will always evaluate to false if '
680
                  'it is.'),
681
        'W0124': ('Following "as" with another context manager looks like a tuple.',
682
                  'confusing-with-statement',
683
                  'Emitted when a `with` statement component returns multiple values '
684
                  'and uses name binding with `as` only for a part of those values, '
685
                  'as in with ctx() as a, b. This can be misleading, since it\'s not '
686
                  'clear if the context manager returns a tuple or if the node without '
687
                  'a name binding is another context manager.'),
688
        'W0125': ('Using a conditional statement with a constant value',
689
                  'using-constant-test',
690
                  'Emitted when a conditional statement (If or ternary if) '
691
                  'uses a constant value for its test. This might not be what '
692
                  'the user intended to do.'),
693
        'E0111': ('The first reversed() argument is not a sequence',
694
                  'bad-reversed-sequence',
695
                  'Used when the first argument to reversed() builtin '
696
                  'isn\'t a sequence (does not implement __reversed__, '
697
                  'nor __getitem__ and __len__'),
698

    
699
    }
700

    
701
    options = (('required-attributes',
702
                deprecated_option(opt_type='csv',
703
                                  help_msg="Required attributes for module. "
704
                                           "This option is obsolete.")),
705

    
706
               ('bad-functions',
707
                {'default' : BAD_FUNCTIONS,
708
                 'type' :'csv', 'metavar' : '<builtin function names>',
709
                 'help' : 'List of builtins function names that should not be '
710
                          'used, separated by a comma'}
711
               ),
712
              )
713
    reports = (('RP0101', 'Statistics by type', report_by_type_stats),)
714

    
715
    def __init__(self, linter):
716
        _BasicChecker.__init__(self, linter)
717
        self.stats = None
718
        self._tryfinallys = None
719

    
720
    def open(self):
721
        """initialize visit variables and statistics
722
        """
723
        self._tryfinallys = []
724
        self.stats = self.linter.add_stats(module=0, function=0,
725
                                           method=0, class_=0)
726

    
727
    @check_messages('using-constant-test')
728
    def visit_if(self, node):
729
        self._check_using_constant_test(node, node.test)
730

    
731
    @check_messages('using-constant-test')
732
    def visit_ifexp(self, node):
733
        self._check_using_constant_test(node, node.test)
734

    
735
    @check_messages('using-constant-test')
736
    def visit_comprehension(self, node):
737
        if node.ifs:
738
            for if_test in node.ifs:
739
                self._check_using_constant_test(node, if_test)
740

    
741
    def _check_using_constant_test(self, node, test):
742
        const_nodes = (
743
            astroid.Module,
744
            astroid.scoped_nodes.GeneratorExp,
745
            astroid.Lambda, astroid.FunctionDef, astroid.ClassDef,
746
            astroid.bases.Generator, astroid.UnboundMethod,
747
            astroid.BoundMethod, astroid.Module)
748
        structs = (astroid.Dict, astroid.Tuple, astroid.Set)
749

    
750
        # These nodes are excepted, since they are not constant
751
        # values, requiring a computation to happen. The only type
752
        # of node in this list which doesn't have this property is
753
        # Getattr, which is excepted because the conditional statement
754
        # can be used to verify that the attribute was set inside a class,
755
        # which is definitely a valid use case.
756
        except_nodes = (astroid.Attribute, astroid.Call,
757
                        astroid.BinOp, astroid.BoolOp, astroid.UnaryOp,
758
                        astroid.Subscript)
759
        inferred = None
760
        emit = isinstance(test, (astroid.Const, ) + structs + const_nodes)
761
        if not isinstance(test, except_nodes):
762
            inferred = safe_infer(test)
763

    
764
        if emit or isinstance(inferred, const_nodes):
765
            self.add_message('using-constant-test', node=node)
766

    
767
    def visit_module(self, _):
768
        """check module name, docstring and required arguments
769
        """
770
        self.stats['module'] += 1
771

    
772
    def visit_classdef(self, node): # pylint: disable=unused-argument
773
        """check module name, docstring and redefinition
774
        increment branch counter
775
        """
776
        self.stats['class'] += 1
777

    
778
    @check_messages('pointless-statement', 'pointless-string-statement',
779
                    'expression-not-assigned')
780
    def visit_expr(self, node):
781
        """check for various kind of statements without effect"""
782
        expr = node.value
783
        if isinstance(expr, astroid.Const) and isinstance(expr.value,
784
                                                          six.string_types):
785
            # treat string statement in a separated message
786
            # Handle PEP-257 attribute docstrings.
787
            # An attribute docstring is defined as being a string right after
788
            # an assignment at the module level, class level or __init__ level.
789
            scope = expr.scope()
790
            if isinstance(scope, (astroid.ClassDef, astroid.Module, astroid.FunctionDef)):
791
                if isinstance(scope, astroid.FunctionDef) and scope.name != '__init__':
792
                    pass
793
                else:
794
                    sibling = expr.previous_sibling()
795
                    if (sibling is not None and sibling.scope() is scope and
796
                            isinstance(sibling, astroid.Assign)):
797
                        return
798
            self.add_message('pointless-string-statement', node=node)
799
            return
800
        # ignore if this is :
801
        # * a direct function call
802
        # * the unique child of a try/except body
803
        # * a yield (which are wrapped by a discard node in _ast XXX)
804
        # warn W0106 if we have any underlying function call (we can't predict
805
        # side effects), else pointless-statement
806
        if (isinstance(expr, (astroid.Yield, astroid.Await, astroid.Call)) or
807
                (isinstance(node.parent, astroid.TryExcept) and
808
                 node.parent.body == [node])):
809
            return
810
        if any(expr.nodes_of_class(astroid.Call)):
811
            self.add_message('expression-not-assigned', node=node,
812
                             args=expr.as_string())
813
        else:
814
            self.add_message('pointless-statement', node=node)
815

    
816
    @staticmethod
817
    def _filter_vararg(node, call_args):
818
        # Return the arguments for the given call which are
819
        # not passed as vararg.
820
        for arg in call_args:
821
            if isinstance(arg, astroid.Starred):
822
                if (isinstance(arg.value, astroid.Name)
823
                        and arg.value.name != node.args.vararg):
824
                    yield arg
825
            else:
826
                yield arg
827

    
828
    @staticmethod
829
    def _has_variadic_argument(args, variadic_name):
830
        if not args:
831
            return True
832
        for arg in args:
833
            if isinstance(arg.value, astroid.Name):
834
                if arg.value.name != variadic_name:
835
                    return True
836
            else:
837
                return True
838
        return False
839

    
840
    @check_messages('unnecessary-lambda')
841
    def visit_lambda(self, node):
842
        """check whether or not the lambda is suspicious
843
        """
844
        # if the body of the lambda is a call expression with the same
845
        # argument list as the lambda itself, then the lambda is
846
        # possibly unnecessary and at least suspicious.
847
        if node.args.defaults:
848
            # If the arguments of the lambda include defaults, then a
849
            # judgment cannot be made because there is no way to check
850
            # that the defaults defined by the lambda are the same as
851
            # the defaults defined by the function called in the body
852
            # of the lambda.
853
            return
854
        call = node.body
855
        if not isinstance(call, astroid.Call):
856
            # The body of the lambda must be a function call expression
857
            # for the lambda to be unnecessary.
858
            return
859
        if (isinstance(node.body.func, astroid.Attribute) and
860
                isinstance(node.body.func.expr, astroid.Call)):
861
            # Chained call, the intermediate call might
862
            # return something else (but we don't check that, yet).
863
            return
864

    
865
        ordinary_args = list(node.args.args)
866
        new_call_args = list(self._filter_vararg(node, call.args))
867
        if node.args.kwarg:
868
            if self._has_variadic_argument(call.kwargs, node.args.kwarg):
869
                return
870
        elif call.kwargs or call.keywords:
871
            return
872

    
873
        if node.args.vararg:
874
            if self._has_variadic_argument(call.starargs, node.args.vararg):
875
                return
876
        elif call.starargs:
877
            return
878

    
879
        # The "ordinary" arguments must be in a correspondence such that:
880
        # ordinary_args[i].name == call.args[i].name.
881
        if len(ordinary_args) != len(new_call_args):
882
            return
883
        for arg, passed_arg in zip(ordinary_args, new_call_args):
884
            if not isinstance(passed_arg, astroid.Name):
885
                return
886
            if arg.name != passed_arg.name:
887
                return
888

    
889
        self.add_message('unnecessary-lambda', line=node.fromlineno,
890
                         node=node)
891

    
892
    @check_messages('dangerous-default-value')
893
    def visit_functiondef(self, node):
894
        """check function name, docstring, arguments, redefinition,
895
        variable names, max locals
896
        """
897
        self.stats[node.is_method() and 'method' or 'function'] += 1
898
        self._check_dangerous_default(node)
899

    
900
    visit_asyncfunctiondef = visit_functiondef
901

    
902
    def _check_dangerous_default(self, node):
903
        # check for dangerous default values as arguments
904
        is_iterable = lambda n: isinstance(n, (astroid.List,
905
                                               astroid.Set,
906
                                               astroid.Dict))
907
        for default in node.args.defaults:
908
            try:
909
                value = next(default.infer())
910
            except astroid.InferenceError:
911
                continue
912

    
913
            if (isinstance(value, astroid.Instance) and
914
                    value.qname() in DEFAULT_ARGUMENT_SYMBOLS):
915

    
916
                if value is default:
917
                    msg = DEFAULT_ARGUMENT_SYMBOLS[value.qname()]
918
                elif isinstance(value, astroid.Instance) or is_iterable(value):
919
                    # We are here in the following situation(s):
920
                    #   * a dict/set/list/tuple call which wasn't inferred
921
                    #     to a syntax node ({}, () etc.). This can happen
922
                    #     when the arguments are invalid or unknown to
923
                    #     the inference.
924
                    #   * a variable from somewhere else, which turns out to be a list
925
                    #     or a dict.
926
                    if is_iterable(default):
927
                        msg = value.pytype()
928
                    elif isinstance(default, astroid.Call):
929
                        msg = '%s() (%s)' % (value.name, value.qname())
930
                    else:
931
                        msg = '%s (%s)' % (default.as_string(), value.qname())
932
                else:
933
                    # this argument is a name
934
                    msg = '%s (%s)' % (default.as_string(),
935
                                       DEFAULT_ARGUMENT_SYMBOLS[value.qname()])
936
                self.add_message('dangerous-default-value',
937
                                 node=node,
938
                                 args=(msg, ))
939

    
940
    @check_messages('unreachable', 'lost-exception')
941
    def visit_return(self, node):
942
        """1 - check is the node has a right sibling (if so, that's some
943
        unreachable code)
944
        2 - check is the node is inside the finally clause of a try...finally
945
        block
946
        """
947
        self._check_unreachable(node)
948
        # Is it inside final body of a try...finally bloc ?
949
        self._check_not_in_finally(node, 'return', (astroid.FunctionDef,))
950

    
951
    @check_messages('unreachable')
952
    def visit_continue(self, node):
953
        """check is the node has a right sibling (if so, that's some unreachable
954
        code)
955
        """
956
        self._check_unreachable(node)
957

    
958
    @check_messages('unreachable', 'lost-exception')
959
    def visit_break(self, node):
960
        """1 - check is the node has a right sibling (if so, that's some
961
        unreachable code)
962
        2 - check is the node is inside the finally clause of a try...finally
963
        block
964
        """
965
        # 1 - Is it right sibling ?
966
        self._check_unreachable(node)
967
        # 2 - Is it inside final body of a try...finally bloc ?
968
        self._check_not_in_finally(node, 'break', (astroid.For, astroid.While,))
969

    
970
    @check_messages('unreachable')
971
    def visit_raise(self, node):
972
        """check if the node has a right sibling (if so, that's some unreachable
973
        code)
974
        """
975
        self._check_unreachable(node)
976

    
977
    @check_messages('exec-used')
978
    def visit_exec(self, node):
979
        """just print a warning on exec statements"""
980
        self.add_message('exec-used', node=node)
981

    
982
    @check_messages('bad-builtin', 'eval-used',
983
                    'exec-used', 'bad-reversed-sequence')
984
    def visit_call(self, node):
985
        """visit a CallFunc node -> check if this is not a blacklisted builtin
986
        call and check for * or ** use
987
        """
988
        if isinstance(node.func, astroid.Name):
989
            name = node.func.name
990
            # ignore the name if it's not a builtin (i.e. not defined in the
991
            # locals nor globals scope)
992
            if not (name in node.frame() or
993
                    name in node.root()):
994
                if name == 'exec':
995
                    self.add_message('exec-used', node=node)
996
                elif name == 'reversed':
997
                    self._check_reversed(node)
998
                elif name == 'eval':
999
                    self.add_message('eval-used', node=node)
1000
                if name in self.config.bad_functions:
1001
                    hint = BUILTIN_HINTS.get(name)
1002
                    if hint:
1003
                        args = "%r. %s" % (name, hint)
1004
                    else:
1005
                        args = repr(name)
1006
                    self.add_message('bad-builtin', node=node, args=args)
1007

    
1008
    @check_messages('assert-on-tuple')
1009
    def visit_assert(self, node):
1010
        """check the use of an assert statement on a tuple."""
1011
        if node.fail is None and isinstance(node.test, astroid.Tuple) and \
1012
                len(node.test.elts) == 2:
1013
            self.add_message('assert-on-tuple', node=node)
1014

    
1015
    @check_messages('duplicate-key')
1016
    def visit_dict(self, node):
1017
        """check duplicate key in dictionary"""
1018
        keys = set()
1019
        for k, _ in node.items:
1020
            if isinstance(k, astroid.Const):
1021
                key = k.value
1022
                if key in keys:
1023
                    self.add_message('duplicate-key', node=node, args=key)
1024
                keys.add(key)
1025

    
1026
    def visit_tryfinally(self, node):
1027
        """update try...finally flag"""
1028
        self._tryfinallys.append(node)
1029

    
1030
    def leave_tryfinally(self, node): # pylint: disable=unused-argument
1031
        """update try...finally flag"""
1032
        self._tryfinallys.pop()
1033

    
1034
    def _check_unreachable(self, node):
1035
        """check unreachable code"""
1036
        unreach_stmt = node.next_sibling()
1037
        if unreach_stmt is not None:
1038
            self.add_message('unreachable', node=unreach_stmt)
1039

    
1040
    def _check_not_in_finally(self, node, node_name, breaker_classes=()):
1041
        """check that a node is not inside a finally clause of a
1042
        try...finally statement.
1043
        If we found before a try...finally bloc a parent which its type is
1044
        in breaker_classes, we skip the whole check."""
1045
        # if self._tryfinallys is empty, we're not a in try...finally bloc
1046
        if not self._tryfinallys:
1047
            return
1048
        # the node could be a grand-grand...-children of the try...finally
1049
        _parent = node.parent
1050
        _node = node
1051
        while _parent and not isinstance(_parent, breaker_classes):
1052
            if hasattr(_parent, 'finalbody') and _node in _parent.finalbody:
1053
                self.add_message('lost-exception', node=node, args=node_name)
1054
                return
1055
            _node = _parent
1056
            _parent = _node.parent
1057

    
1058
    def _check_reversed(self, node):
1059
        """ check that the argument to `reversed` is a sequence """
1060
        try:
1061
            argument = safe_infer(get_argument_from_call(node, position=0))
1062
        except NoSuchArgumentError:
1063
            pass
1064
        else:
1065
            if argument is astroid.YES:
1066
                return
1067
            if argument is None:
1068
                # Nothing was infered.
1069
                # Try to see if we have iter().
1070
                if isinstance(node.args[0], astroid.Call):
1071
                    try:
1072
                        func = next(node.args[0].func.infer())
1073
                    except InferenceError:
1074
                        return
1075
                    if (getattr(func, 'name', None) == 'iter' and
1076
                            is_builtin_object(func)):
1077
                        self.add_message('bad-reversed-sequence', node=node)
1078
                return
1079

    
1080
            if isinstance(argument, astroid.Instance):
1081
                if (argument._proxied.name == 'dict' and
1082
                        is_builtin_object(argument._proxied)):
1083
                    self.add_message('bad-reversed-sequence', node=node)
1084
                    return
1085
                elif any(ancestor.name == 'dict' and is_builtin_object(ancestor)
1086
                         for ancestor in argument._proxied.ancestors()):
1087
                    # Mappings aren't accepted by reversed(), unless
1088
                    # they provide explicitly a __reversed__ method.
1089
                    try:
1090
                        argument.locals[REVERSED_PROTOCOL_METHOD]
1091
                    except KeyError:
1092
                        self.add_message('bad-reversed-sequence', node=node)
1093
                    return
1094

    
1095
                for methods in REVERSED_METHODS:
1096
                    for meth in methods:
1097
                        try:
1098
                            argument.getattr(meth)
1099
                        except astroid.NotFoundError:
1100
                            break
1101
                    else:
1102
                        break
1103
                else:
1104
                    self.add_message('bad-reversed-sequence', node=node)
1105
            elif not isinstance(argument, (astroid.List, astroid.Tuple)):
1106
                # everything else is not a proper sequence for reversed()
1107
                self.add_message('bad-reversed-sequence', node=node)
1108

    
1109
    @check_messages('confusing-with-statement')
1110
    def visit_with(self, node):
1111
        if not PY3K:
1112
            # in Python 2 a "with" statement with multiple managers coresponds
1113
            # to multiple nested AST "With" nodes
1114
            pairs = []
1115
            parent_node = node.parent
1116
            if isinstance(parent_node, astroid.With):
1117
                # we only care about the direct parent, since this method
1118
                # gets called for each with node anyway
1119
                pairs.extend(parent_node.items)
1120
            pairs.extend(node.items)
1121
        else:
1122
            # in PY3K a "with" statement with multiple managers coresponds
1123
            # to one AST "With" node with multiple items
1124
            pairs = node.items
1125
        if pairs:
1126
            for prev_pair, pair in zip(pairs, pairs[1:]):
1127
                if (isinstance(prev_pair[1], astroid.AssignName) and
1128
                        (pair[1] is None and not isinstance(pair[0], astroid.Call))):
1129
                    # don't emit a message if the second is a function call
1130
                    # there's no way that can be mistaken for a name assignment
1131
                    if PY3K or node.lineno == node.parent.lineno:
1132
                        # if the line number doesn't match
1133
                        # we assume it's a nested "with"
1134
                        self.add_message('confusing-with-statement', node=node)
1135

    
1136

    
1137
_NAME_TYPES = {
1138
    'module': (MOD_NAME_RGX, 'module'),
1139
    'const': (CONST_NAME_RGX, 'constant'),
1140
    'class': (CLASS_NAME_RGX, 'class'),
1141
    'function': (DEFAULT_NAME_RGX, 'function'),
1142
    'method': (DEFAULT_NAME_RGX, 'method'),
1143
    'attr': (DEFAULT_NAME_RGX, 'attribute'),
1144
    'argument': (DEFAULT_NAME_RGX, 'argument'),
1145
    'variable': (DEFAULT_NAME_RGX, 'variable'),
1146
    'class_attribute': (CLASS_ATTRIBUTE_RGX, 'class attribute'),
1147
    'inlinevar': (COMP_VAR_RGX, 'inline iteration'),
1148
}
1149

    
1150
def _create_naming_options():
1151
    name_options = []
1152
    for name_type, (rgx, human_readable_name) in six.iteritems(_NAME_TYPES):
1153
        name_type = name_type.replace('_', '-')
1154
        name_options.append((
1155
            '%s-rgx' % (name_type,),
1156
            {'default': rgx, 'type': 'regexp', 'metavar': '<regexp>',
1157
             'help': 'Regular expression matching correct %s names' % (human_readable_name,)}))
1158
        name_options.append((
1159
            '%s-name-hint' % (name_type,),
1160
            {'default': rgx.pattern, 'type': 'string', 'metavar': '<string>',
1161
             'help': 'Naming hint for %s names' % (human_readable_name,)}))
1162
    return tuple(name_options)
1163

    
1164
class NameChecker(_BasicChecker):
1165
    msgs = {
1166
        'C0102': ('Black listed name "%s"',
1167
                  'blacklisted-name',
1168
                  'Used when the name is listed in the black list (unauthorized '
1169
                  'names).'),
1170
        'C0103': ('Invalid %s name "%s"%s',
1171
                  'invalid-name',
1172
                  'Used when the name doesn\'t match the regular expression '
1173
                  'associated to its type (constant, variable, class...).'),
1174
    }
1175

    
1176
    options = (('good-names',
1177
                {'default' : ('i', 'j', 'k', 'ex', 'Run', '_'),
1178
                 'type' :'csv', 'metavar' : '<names>',
1179
                 'help' : 'Good variable names which should always be accepted,'
1180
                          ' separated by a comma'}
1181
               ),
1182
               ('bad-names',
1183
                {'default' : ('foo', 'bar', 'baz', 'toto', 'tutu', 'tata'),
1184
                 'type' :'csv', 'metavar' : '<names>',
1185
                 'help' : 'Bad variable names which should always be refused, '
1186
                          'separated by a comma'}
1187
               ),
1188
               ('name-group',
1189
                {'default' : (),
1190
                 'type' :'csv', 'metavar' : '<name1:name2>',
1191
                 'help' : ('Colon-delimited sets of names that determine each'
1192
                           ' other\'s naming style when the name regexes'
1193
                           ' allow several styles.')}
1194
               ),
1195
               ('include-naming-hint',
1196
                {'default': False, 'type' : 'yn', 'metavar' : '<y_or_n>',
1197
                 'help': 'Include a hint for the correct naming format with invalid-name'}
1198
               ),
1199
              ) + _create_naming_options()
1200

    
1201

    
1202
    def __init__(self, linter):
1203
        _BasicChecker.__init__(self, linter)
1204
        self._name_category = {}
1205
        self._name_group = {}
1206
        self._bad_names = {}
1207

    
1208
    def open(self):
1209
        self.stats = self.linter.add_stats(badname_module=0,
1210
                                           badname_class=0, badname_function=0,
1211
                                           badname_method=0, badname_attr=0,
1212
                                           badname_const=0,
1213
                                           badname_variable=0,
1214
                                           badname_inlinevar=0,
1215
                                           badname_argument=0,
1216
                                           badname_class_attribute=0)
1217
        for group in self.config.name_group:
1218
            for name_type in group.split(':'):
1219
                self._name_group[name_type] = 'group_%s' % (group,)
1220

    
1221
    @check_messages('blacklisted-name', 'invalid-name')
1222
    def visit_module(self, node):
1223
        self._check_name('module', node.name.split('.')[-1], node)
1224
        self._bad_names = {}
1225

    
1226
    def leave_module(self, node): # pylint: disable=unused-argument
1227
        for all_groups in six.itervalues(self._bad_names):
1228
            if len(all_groups) < 2:
1229
                continue
1230
            groups = collections.defaultdict(list)
1231
            min_warnings = sys.maxsize
1232
            for group in six.itervalues(all_groups):
1233
                groups[len(group)].append(group)
1234
                min_warnings = min(len(group), min_warnings)
1235
            if len(groups[min_warnings]) > 1:
1236
                by_line = sorted(groups[min_warnings],
1237
                                 key=lambda group: min(warning[0].lineno for warning in group))
1238
                warnings = itertools.chain(*by_line[1:])
1239
            else:
1240
                warnings = groups[min_warnings][0]
1241
            for args in warnings:
1242
                self._raise_name_warning(*args)
1243

    
1244
    @check_messages('blacklisted-name', 'invalid-name')
1245
    def visit_classdef(self, node):
1246
        self._check_name('class', node.name, node)
1247
        for attr, anodes in six.iteritems(node.instance_attrs):
1248
            if not any(node.instance_attr_ancestors(attr)):
1249
                self._check_name('attr', attr, anodes[0])
1250

    
1251
    @check_messages('blacklisted-name', 'invalid-name')
1252
    def visit_functiondef(self, node):
1253
        # Do not emit any warnings if the method is just an implementation
1254
        # of a base class method.
1255
        confidence = HIGH
1256
        if node.is_method():
1257
            if overrides_a_method(node.parent.frame(), node.name):
1258
                return
1259
            confidence = (INFERENCE if has_known_bases(node.parent.frame())
1260
                          else INFERENCE_FAILURE)
1261

    
1262
        self._check_name(_determine_function_name_type(node),
1263
                         node.name, node, confidence)
1264
        # Check argument names
1265
        args = node.args.args
1266
        if args is not None:
1267
            self._recursive_check_names(args, node)
1268

    
1269
    visit_asyncfunctiondef = visit_functiondef
1270

    
1271
    @check_messages('blacklisted-name', 'invalid-name')
1272
    def visit_global(self, node):
1273
        for name in node.names:
1274
            self._check_name('const', name, node)
1275

    
1276
    @check_messages('blacklisted-name', 'invalid-name')
1277
    def visit_assignname(self, node):
1278
        """check module level assigned names"""
1279
        frame = node.frame()
1280
        ass_type = node.assign_type()
1281
        if isinstance(ass_type, astroid.Comprehension):
1282
            self._check_name('inlinevar', node.name, node)
1283
        elif isinstance(frame, astroid.Module):
1284
            if isinstance(ass_type, astroid.Assign) and not in_loop(ass_type):
1285
                if isinstance(safe_infer(ass_type.value), astroid.ClassDef):
1286
                    self._check_name('class', node.name, node)
1287
                else:
1288
                    if not _redefines_import(node):
1289
                        # Don't emit if the name redefines an import
1290
                        # in an ImportError except handler.
1291
                        self._check_name('const', node.name, node)
1292
            elif isinstance(ass_type, astroid.ExceptHandler):
1293
                self._check_name('variable', node.name, node)
1294
        elif isinstance(frame, astroid.FunctionDef):
1295
            # global introduced variable aren't in the function locals
1296
            if node.name in frame and node.name not in frame.argnames():
1297
                if not _redefines_import(node):
1298
                    self._check_name('variable', node.name, node)
1299
        elif isinstance(frame, astroid.ClassDef):
1300
            if not list(frame.local_attr_ancestors(node.name)):
1301
                self._check_name('class_attribute', node.name, node)
1302

    
1303
    def _recursive_check_names(self, args, node):
1304
        """check names in a possibly recursive list <arg>"""
1305
        for arg in args:
1306
            if isinstance(arg, astroid.AssignName):
1307
                self._check_name('argument', arg.name, node)
1308
            else:
1309
                self._recursive_check_names(arg.elts, node)
1310

    
1311
    def _find_name_group(self, node_type):
1312
        return self._name_group.get(node_type, node_type)
1313

    
1314
    def _raise_name_warning(self, node, node_type, name, confidence):
1315
        type_label = _NAME_TYPES[node_type][1]
1316
        hint = ''
1317
        if self.config.include_naming_hint:
1318
            hint = ' (hint: %s)' % (getattr(self.config, node_type + '_name_hint'))
1319
        self.add_message('invalid-name', node=node, args=(type_label, name, hint),
1320
                         confidence=confidence)
1321
        self.stats['badname_' + node_type] += 1
1322

    
1323
    def _check_name(self, node_type, name, node, confidence=HIGH):
1324
        """check for a name using the type's regexp"""
1325
        if is_inside_except(node):
1326
            clobbering, _ = clobber_in_except(node)
1327
            if clobbering:
1328
                return
1329
        if name in self.config.good_names:
1330
            return
1331
        if name in self.config.bad_names:
1332
            self.stats['badname_' + node_type] += 1
1333
            self.add_message('blacklisted-name', node=node, args=name)
1334
            return
1335
        regexp = getattr(self.config, node_type + '_rgx')
1336
        match = regexp.match(name)
1337

    
1338
        if _is_multi_naming_match(match, node_type, confidence):
1339
            name_group = self._find_name_group(node_type)
1340
            bad_name_group = self._bad_names.setdefault(name_group, {})
1341
            warnings = bad_name_group.setdefault(match.lastgroup, [])
1342
            warnings.append((node, node_type, name, confidence))
1343

    
1344
        if match is None:
1345
            self._raise_name_warning(node, node_type, name, confidence)
1346

    
1347

    
1348
class DocStringChecker(_BasicChecker):
1349
    msgs = {
1350
        'C0111': ('Missing %s docstring', # W0131
1351
                  'missing-docstring',
1352
                  'Used when a module, function, class or method has no docstring.'
1353
                  'Some special methods like __init__ doesn\'t necessary require a '
1354
                  'docstring.'),
1355
        'C0112': ('Empty %s docstring', # W0132
1356
                  'empty-docstring',
1357
                  'Used when a module, function, class or method has an empty '
1358
                  'docstring (it would be too easy ;).'),
1359
        }
1360
    options = (('no-docstring-rgx',
1361
                {'default' : NO_REQUIRED_DOC_RGX,
1362
                 'type' : 'regexp', 'metavar' : '<regexp>',
1363
                 'help' : 'Regular expression which should only match '
1364
                          'function or class names that do not require a '
1365
                          'docstring.'}
1366
               ),
1367
               ('docstring-min-length',
1368
                {'default' : -1,
1369
                 'type' : 'int', 'metavar' : '<int>',
1370
                 'help': ('Minimum line length for functions/classes that'
1371
                          ' require docstrings, shorter ones are exempt.')}
1372
               ),
1373
              )
1374

    
1375

    
1376
    def open(self):
1377
        self.stats = self.linter.add_stats(undocumented_module=0,
1378
                                           undocumented_function=0,
1379
                                           undocumented_method=0,
1380
                                           undocumented_class=0)
1381
    @check_messages('missing-docstring', 'empty-docstring')
1382
    def visit_module(self, node):
1383
        self._check_docstring('module', node)
1384

    
1385
    @check_messages('missing-docstring', 'empty-docstring')
1386
    def visit_classdef(self, node):
1387
        if self.config.no_docstring_rgx.match(node.name) is None:
1388
            self._check_docstring('class', node)
1389

    
1390
    @staticmethod
1391
    def _is_setter_or_deleter(node):
1392
        names = {'setter', 'deleter'}
1393
        for decorator in node.decorators.nodes:
1394
            if (isinstance(decorator, astroid.Attribute)
1395
                    and decorator.attrname in names):
1396
                return True
1397
        return False
1398

    
1399
    @check_messages('missing-docstring', 'empty-docstring')
1400
    def visit_functiondef(self, node):
1401
        if self.config.no_docstring_rgx.match(node.name) is None:
1402
            ftype = node.is_method() and 'method' or 'function'
1403
            if node.decorators and self._is_setter_or_deleter(node):
1404
                return
1405

    
1406
            if isinstance(node.parent.frame(), astroid.ClassDef):
1407
                overridden = False
1408
                confidence = (INFERENCE if has_known_bases(node.parent.frame())
1409
                              else INFERENCE_FAILURE)
1410
                # check if node is from a method overridden by its ancestor
1411
                for ancestor in node.parent.frame().ancestors():
1412
                    if node.name in ancestor and \
1413
                       isinstance(ancestor[node.name], astroid.FunctionDef):
1414
                        overridden = True
1415
                        break
1416
                self._check_docstring(ftype, node,
1417
                                      report_missing=not overridden,
1418
                                      confidence=confidence)
1419
            else:
1420
                self._check_docstring(ftype, node)
1421

    
1422
    visit_asyncfunctiondef = visit_functiondef
1423

    
1424
    def _check_docstring(self, node_type, node, report_missing=True,
1425
                         confidence=HIGH):
1426
        """check the node has a non empty docstring"""
1427
        docstring = node.doc
1428
        if docstring is None:
1429
            if not report_missing:
1430
                return
1431
            if node.body:
1432
                lines = node.body[-1].lineno - node.body[0].lineno + 1
1433
            else:
1434
                lines = 0
1435

    
1436
            if node_type == 'module' and not lines:
1437
                # If the module has no body, there's no reason
1438
                # to require a docstring.
1439
                return
1440
            max_lines = self.config.docstring_min_length
1441

    
1442
            if node_type != 'module' and max_lines > -1 and lines < max_lines:
1443
                return
1444
            self.stats['undocumented_'+node_type] += 1
1445
            if (node.body and isinstance(node.body[0], astroid.Expr) and
1446
                    isinstance(node.body[0].value, astroid.Call)):
1447
                # Most likely a string with a format call. Let's see.
1448
                func = safe_infer(node.body[0].value.func)
1449
                if (isinstance(func, astroid.BoundMethod)
1450
                        and isinstance(func.bound, astroid.Instance)):
1451
                    # Strings in Python 3, others in Python 2.
1452
                    if PY3K and func.bound.name == 'str':
1453
                        return
1454
                    elif func.bound.name in ('str', 'unicode', 'bytes'):
1455
                        return
1456
            self.add_message('missing-docstring', node=node, args=(node_type,),
1457
                             confidence=confidence)
1458
        elif not docstring.strip():
1459
            self.stats['undocumented_'+node_type] += 1
1460
            self.add_message('empty-docstring', node=node, args=(node_type,),
1461
                             confidence=confidence)
1462

    
1463

    
1464
class PassChecker(_BasicChecker):
1465
    """check if the pass statement is really necessary"""
1466
    msgs = {'W0107': ('Unnecessary pass statement',
1467
                      'unnecessary-pass',
1468
                      'Used when a "pass" statement that can be avoided is '
1469
                      'encountered.'),
1470
           }
1471
    @check_messages('unnecessary-pass')
1472
    def visit_pass(self, node):
1473
        if len(node.parent.child_sequence(node)) > 1:
1474
            self.add_message('unnecessary-pass', node=node)
1475

    
1476

    
1477
class LambdaForComprehensionChecker(_BasicChecker):
1478
    """check for using a lambda where a comprehension would do.
1479

1480
    See <http://www.artima.com/weblogs/viewpost.jsp?thread=98196>
1481
    where GvR says comprehensions would be clearer.
1482
    """
1483

    
1484
    msgs = {'W0110': ('map/filter on lambda could be replaced by comprehension',
1485
                      'deprecated-lambda',
1486
                      'Used when a lambda is the first argument to "map" or '
1487
                      '"filter". It could be clearer as a list '
1488
                      'comprehension or generator expression.',
1489
                      {'maxversion': (3, 0)}),
1490
           }
1491

    
1492
    @check_messages('deprecated-lambda')
1493
    def visit_call(self, node):
1494
        """visit a CallFunc node, check if map or filter are called with a
1495
        lambda
1496
        """
1497
        if not node.args:
1498
            return
1499
        if not isinstance(node.args[0], astroid.Lambda):
1500
            return
1501
        infered = safe_infer(node.func)
1502
        if (is_builtin_object(infered)
1503
                and infered.name in ['map', 'filter']):
1504
            self.add_message('deprecated-lambda', node=node)
1505

    
1506

    
1507
class RecommandationChecker(_BasicChecker):
1508
    msgs = {'C0200': ('Consider using enumerate instead of iterating with range and len',
1509
                      'consider-using-enumerate',
1510
                      'Emitted when code that iterates with range and len is '
1511
                      'encountered. Such code can be simplified by using the '
1512
                      'enumerate builtin.'),
1513
           }
1514

    
1515
    @staticmethod
1516
    def _is_builtin(node, function):
1517
        inferred = safe_infer(node)
1518
        if not inferred:
1519
            return False
1520
        return is_builtin_object(inferred) and inferred.name == function
1521

    
1522
    @check_messages('consider-using-enumerate')
1523
    def visit_for(self, node):
1524
        """Emit a convention whenever range and len are used for indexing."""
1525
        # Verify that we have a `range(len(...))` call and that the object
1526
        # which is iterated is used as a subscript in the body of the for.
1527

    
1528
        # Is it a proper range call?
1529
        if not isinstance(node.iter, astroid.Call):
1530
            return
1531
        if not self._is_builtin(node.iter.func, 'range'):
1532
            return
1533
        if len(node.iter.args) != 1:
1534
            return
1535

    
1536
        # Is it a proper len call?
1537
        if not isinstance(node.iter.args[0], astroid.Call):
1538
            return
1539
        second_func = node.iter.args[0].func
1540
        if not self._is_builtin(second_func, 'len'):
1541
            return
1542
        len_args = node.iter.args[0].args
1543
        if not len_args or len(len_args) != 1:
1544
            return
1545
        iterating_object = len_args[0]
1546
        if not isinstance(iterating_object, astroid.Name):
1547
            return
1548

    
1549
        # Verify that the body of the for loop uses a subscript
1550
        # with the object that was iterated. This uses some heuristics
1551
        # in order to make sure that the same object is used in the
1552
        # for body.
1553
        for child in node.body:
1554
            for subscript in child.nodes_of_class(astroid.Subscript):
1555
                if not isinstance(subscript.value, astroid.Name):
1556
                    continue
1557
                if not isinstance(subscript.slice, astroid.Index):
1558
                    continue
1559
                if not isinstance(subscript.slice.value, astroid.Name):
1560
                    continue
1561
                if subscript.slice.value.name != node.target.name:
1562
                    continue
1563
                if iterating_object.name != subscript.value.name:
1564
                    continue
1565
                if subscript.value.scope() != node.scope():
1566
                    # Ignore this subscript if it's not in the same
1567
                    # scope. This means that in the body of the for
1568
                    # loop, another scope was created, where the same
1569
                    # name for the iterating object was used.
1570
                    continue
1571
                self.add_message('consider-using-enumerate', node=node)
1572
                return
1573

    
1574

    
1575
def _is_one_arg_pos_call(call):
1576
    """Is this a call with exactly 1 argument,
1577
    where that argument is positional?
1578
    """
1579
    return (isinstance(call, astroid.Call)
1580
            and len(call.args) == 1 and not call.keywords)
1581

    
1582

    
1583
class ComparisonChecker(_BasicChecker):
1584
    """Checks for comparisons
1585

1586
    - singleton comparison: 'expr == True', 'expr == False' and 'expr == None'
1587
    - yoda condition: 'const "comp" right' where comp can be '==', '!=', '<',
1588
      '<=', '>' or '>=', and right can be a variable, an attribute, a method or
1589
      a function
1590
    """
1591
    msgs = {'C0121': ('Comparison to %s should be %s',
1592
                      'singleton-comparison',
1593
                      'Used when an expression is compared to singleton '
1594
                      'values like True, False or None.'),
1595
            'C0122': ('Comparison should be %s',
1596
                      'misplaced-comparison-constant',
1597
                      'Used when the constant is placed on the left side'
1598
                      'of a comparison. It is usually clearer in intent to '
1599
                      'place it in the right hand side of the comparison.'),
1600
            'C0123': ('Using type() instead of isinstance() for a typecheck.',
1601
                      'unidiomatic-typecheck',
1602
                      'The idiomatic way to perform an explicit typecheck in '
1603
                      'Python is to use isinstance(x, Y) rather than '
1604
                      'type(x) == Y, type(x) is Y. Though there are unusual '
1605
                      'situations where these give different results.',
1606
                      {'old_names': [('W0154', 'unidiomatic-typecheck')]}),
1607
           }
1608

    
1609
    def _check_singleton_comparison(self, singleton, root_node):
1610
        if singleton.value is True:
1611
            suggestion = "just 'expr' or 'expr is True'"
1612
            self.add_message('singleton-comparison',
1613
                             node=root_node,
1614
                             args=(True, suggestion))
1615
        elif singleton.value is False:
1616
            suggestion = "'not expr' or 'expr is False'"
1617
            self.add_message('singleton-comparison',
1618
                             node=root_node,
1619
                             args=(False, suggestion))
1620
        elif singleton.value is None:
1621
            self.add_message('singleton-comparison',
1622
                             node=root_node,
1623
                             args=(None, "'expr is None'"))
1624

    
1625
    def _check_misplaced_constant(self, node, left, right, operator):
1626
        if isinstance(right, astroid.Const):
1627
            return
1628
        operator = REVERSED_COMPS.get(operator, operator)
1629
        suggestion = '%s %s %r' % (right.as_string(), operator, left.value)
1630
        self.add_message('misplaced-comparison-constant', node=node,
1631
                         args=(suggestion,))
1632

    
1633
    @check_messages('singleton-comparison', 'misplaced-comparison-constant',
1634
                    'unidiomatic-typecheck')
1635
    def visit_compare(self, node):
1636
        self._check_unidiomatic_typecheck(node)
1637
        # NOTE: this checker only works with binary comparisons like 'x == 42'
1638
        # but not 'x == y == 42'
1639
        if len(node.ops) != 1:
1640
            return
1641
        left = node.left
1642
        operator, right = node.ops[0]
1643
        if (operator in ('<', '<=', '>', '>=', '!=', '==')
1644
                and isinstance(left, astroid.Const)):
1645
            self._check_misplaced_constant(node, left, right, operator)
1646

    
1647
        if operator == '==':
1648
            if isinstance(left, astroid.Const):
1649
                self._check_singleton_comparison(left, node)
1650
            elif isinstance(right, astroid.Const):
1651
                self._check_singleton_comparison(right, node)
1652

    
1653
    def _check_unidiomatic_typecheck(self, node):
1654
        operator, right = node.ops[0]
1655
        if operator in TYPECHECK_COMPARISON_OPERATORS:
1656
            left = node.left
1657
            if _is_one_arg_pos_call(left):
1658
                self._check_type_x_is_y(node, left, operator, right)
1659

    
1660
    def _check_type_x_is_y(self, node, left, operator, right):
1661
        """Check for expressions like type(x) == Y."""
1662
        left_func = safe_infer(left.func)
1663
        if not (isinstance(left_func, astroid.ClassDef)
1664
                and left_func.qname() == TYPE_QNAME):
1665
            return
1666

    
1667
        if operator in ('is', 'is not') and _is_one_arg_pos_call(right):
1668
            right_func = safe_infer(right.func)
1669
            if (isinstance(right_func, astroid.ClassDef)
1670
                    and right_func.qname() == TYPE_QNAME):
1671
                # type(x) == type(a)
1672
                right_arg = safe_infer(right.args[0])
1673
                if not isinstance(right_arg, LITERAL_NODE_TYPES):
1674
                    # not e.g. type(x) == type([])
1675
                    return
1676
        self.add_message('unidiomatic-typecheck', node=node)
1677

    
1678

    
1679
class ElifChecker(BaseTokenChecker):
1680
    """Checks needing to distinguish "else if" from "elif"
1681

1682
    This checker mixes the astroid and the token approaches in order to create
1683
    knowledge about whether a "else if" node is a true "else if" node, or a
1684
    "elif" node.
1685

1686
    The following checks depend on this implementation:
1687
        - check for too many nested blocks (if/elif structures aren't considered
1688
          as nested)
1689
        - to be continued
1690
    """
1691
    __implements__ = (ITokenChecker, IAstroidChecker)
1692
    name = 'elif'
1693
    msgs = {'R0101': ('Too many nested blocks (%s/%s)',
1694
                      'too-many-nested-blocks',
1695
                      'Used when a function or a method has too many nested '
1696
                      'blocks. This makes the code less understandable and '
1697
                      'maintainable.'),
1698
            'R0102': ('The if statement can be reduced by %s',
1699
                      'simplifiable-if-statement',
1700
                      'Used when an if statement can be reduced to a boolean '
1701
                      'conversion of the statement\'s test. '),
1702
           }
1703
    options = (('max-nested-blocks',
1704
                {'default' : 5, 'type' : 'int', 'metavar' : '<int>',
1705
                 'help': 'Maximum number of nested blocks for function / '
1706
                         'method body'}
1707
               ),)
1708

    
1709
    def __init__(self, linter=None):
1710
        BaseTokenChecker.__init__(self, linter)
1711
        self._init()
1712

    
1713
    def _init(self):
1714
        self._nested_blocks = []
1715
        self._elifs = []
1716
        self._if_counter = 0
1717
        self._nested_blocks_msg = None
1718

    
1719
    @staticmethod
1720
    def _is_bool_const(node):
1721
        return (isinstance(node.value, astroid.Const)
1722
                and isinstance(node.value.value, bool))
1723

    
1724
    def _is_actual_elif(self, node):
1725
        """Check if the given node is an actual elif
1726

1727
        This is a problem we're having with the builtin ast module,
1728
        which splits `elif` branches into a separate if statement.
1729
        Unfortunately we need to know the exact type in certain
1730
        cases.
1731
        """
1732

    
1733
        if isinstance(node.parent, astroid.If):
1734
            orelse = node.parent.orelse
1735
            # current if node must directly follow a "else"
1736
            if orelse and orelse == [node]:
1737
                if self._elifs[self._if_counter]:
1738
                    return True
1739
        return False
1740

    
1741
    def _check_simplifiable_if(self, node):
1742
        """Check if the given if node can be simplified.
1743

1744
        The if statement can be reduced to a boolean expression
1745
        in some cases. For instance, if there are two branches
1746
        and both of them return a boolean value that depends on
1747
        the result of the statement's test, then this can be reduced
1748
        to `bool(test)` without losing any functionality.
1749
        """
1750

    
1751
        if self._is_actual_elif(node):
1752
            # Not interested in if statements with multiple branches.
1753
            return
1754
        if len(node.orelse) != 1 or len(node.body) != 1:
1755
            return
1756

    
1757
        # Check if both branches can be reduced.
1758
        first_branch = node.body[0]
1759
        else_branch = node.orelse[0]
1760
        if isinstance(first_branch, astroid.Return):
1761
            if not isinstance(else_branch, astroid.Return):
1762
                return
1763
            first_branch_is_bool = self._is_bool_const(first_branch)
1764
            else_branch_is_bool = self._is_bool_const(else_branch)
1765
            reduced_to = "returning bool of test"
1766
        elif isinstance(first_branch, astroid.Assign):
1767
            if not isinstance(else_branch, astroid.Assign):
1768
                return
1769
            first_branch_is_bool = self._is_bool_const(first_branch)
1770
            else_branch_is_bool = self._is_bool_const(else_branch)
1771
            reduced_to = "assigning bool of test"
1772
        else:
1773
            return
1774

    
1775
        if not first_branch_is_bool or not else_branch_is_bool:
1776
            return
1777
        if not first_branch.value.value:
1778
            # This is a case that can't be easily simplified and
1779
            # if it can be simplified, it will usually result in a
1780
            # code that's harder to understand and comprehend.
1781
            # Let's take for instance `arg and arg <= 3`. This could theoretically be
1782
            # reduced to `not arg or arg > 3`, but the net result is that now the
1783
            # condition is harder to understand, because it requires understanding of
1784
            # an extra clause:
1785
            #   * first, there is the negation of truthness with `not arg`
1786
            #   * the second clause is `arg > 3`, which occurs when arg has a
1787
            #     a truth value, but it implies that `arg > 3` is equivalent
1788
            #     with `arg and arg > 3`, which means that the user must
1789
            #     think about this assumption when evaluating `arg > 3`.
1790
            #     The original form is easier to grasp.
1791
            return
1792

    
1793
        self.add_message('simplifiable-if-statement', node=node,
1794
                         args=(reduced_to, ))
1795

    
1796
    def process_tokens(self, tokens):
1797
        # Process tokens and look for 'if' or 'elif'
1798
        for _, token, _, _, _ in tokens:
1799
            if token == 'elif':
1800
                self._elifs.append(True)
1801
            elif token == 'if':
1802
                self._elifs.append(False)
1803

    
1804
    def leave_module(self, _):
1805
        self._init()
1806

    
1807
    @check_messages('too-many-nested-blocks')
1808
    def visit_tryexcept(self, node):
1809
        self._check_nested_blocks(node)
1810

    
1811
    visit_tryfinally = visit_tryexcept
1812
    visit_while = visit_tryexcept
1813
    visit_for = visit_while
1814

    
1815
    def visit_ifexp(self, _):
1816
        self._if_counter += 1
1817

    
1818
    def visit_comprehension(self, node):
1819
        self._if_counter += len(node.ifs)
1820

    
1821
    @check_messages('too-many-nested-blocks', 'simplifiable-if-statement')
1822
    def visit_if(self, node):
1823
        self._check_simplifiable_if(node)
1824
        self._check_nested_blocks(node)
1825
        self._if_counter += 1
1826

    
1827
    @check_messages('too-many-nested-blocks')
1828
    def leave_functiondef(self, _):
1829
        # new scope = reinitialize the stack of nested blocks
1830
        self._nested_blocks = []
1831
        # if there is a waiting message left, send it
1832
        if self._nested_blocks_msg:
1833
            self.add_message('too-many-nested-blocks',
1834
                             node=self._nested_blocks_msg[0],
1835
                             args=self._nested_blocks_msg[1])
1836
            self._nested_blocks_msg = None
1837

    
1838
    def _check_nested_blocks(self, node):
1839
        """Update and check the number of nested blocks
1840
        """
1841
        # only check block levels inside functions or methods
1842
        if not isinstance(node.scope(), astroid.FunctionDef):
1843
            return
1844
        # messages are triggered on leaving the nested block. Here we save the
1845
        # stack in case the current node isn't nested in the previous one
1846
        nested_blocks = self._nested_blocks[:]
1847
        if node.parent == node.scope():
1848
            self._nested_blocks = [node]
1849
        else:
1850
            # go through ancestors from the most nested to the less
1851
            for ancestor_node in reversed(self._nested_blocks):
1852
                if ancestor_node == node.parent:
1853
                    break
1854
                self._nested_blocks.pop()
1855
            # if the node is a elif, this should not be another nesting level
1856
            if isinstance(node, astroid.If) and self._elifs[self._if_counter]:
1857
                if self._nested_blocks:
1858
                    self._nested_blocks.pop()
1859
            self._nested_blocks.append(node)
1860
        # send message only once per group of nested blocks
1861
        if len(nested_blocks) > self.config.max_nested_blocks:
1862
            if len(nested_blocks) > len(self._nested_blocks):
1863
                self.add_message('too-many-nested-blocks', node=nested_blocks[0],
1864
                                 args=(len(nested_blocks),
1865
                                       self.config.max_nested_blocks))
1866
                self._nested_blocks_msg = None
1867
            else:
1868
                # if time has not come yet to send the message (ie the stack of
1869
                # nested nodes is still increasing), save it in case the
1870
                # current node is the last one of the function
1871
                self._nested_blocks_msg = (self._nested_blocks[0],
1872
                                           (len(self._nested_blocks),
1873
                                            self.config.max_nested_blocks))
1874

    
1875
class NotChecker(_BasicChecker):
1876
    """checks for too many not in comparison expressions
1877

1878
    - "not not" should trigger a warning
1879
    - "not" followed by a comparison should trigger a warning
1880
    """
1881
    msgs = {'C0113': ('Consider changing "%s" to "%s"',
1882
                      'unneeded-not',
1883
                      'Used when a boolean expression contains an unneeded '
1884
                      'negation.'),
1885
           }
1886

    
1887
    reverse_op = {'<': '>=', '<=': '>', '>': '<=', '>=': '<', '==': '!=',
1888
                  '!=': '==', 'in': 'not in', 'is': 'is not'}
1889
    # sets are not ordered, so for example "not set(LEFT_VALS) <= set(RIGHT_VALS)" is
1890
    # not equivalent to "set(LEFT_VALS) > set(RIGHT_VALS)"
1891
    skipped_nodes = (astroid.Set, )
1892
    # 'builtins' py3, '__builtin__' py2
1893
    skipped_classnames = ['%s.%s' % (six.moves.builtins.__name__, qname)
1894
                          for qname in ('set', 'frozenset')]
1895

    
1896
    @check_messages('unneeded-not')
1897
    def visit_unaryop(self, node):
1898
        if node.op != 'not':
1899
            return
1900
        operand = node.operand
1901

    
1902
        if isinstance(operand, astroid.UnaryOp) and operand.op == 'not':
1903
            self.add_message('unneeded-not', node=node,
1904
                             args=(node.as_string(),
1905
                                   operand.operand.as_string()))
1906
        elif isinstance(operand, astroid.Compare):
1907
            left = operand.left
1908
            # ignore multiple comparisons
1909
            if len(operand.ops) > 1:
1910
                return
1911
            operator, right = operand.ops[0]
1912
            if operator not in self.reverse_op:
1913
                return
1914
            # Ignore __ne__ as function of __eq__
1915
            frame = node.frame()
1916
            if frame.name == '__ne__' and operator == '==':
1917
                return
1918
            for _type in (_node_type(left), _node_type(right)):
1919
                if not _type:
1920
                    return
1921
                if isinstance(_type, self.skipped_nodes):
1922
                    return
1923
                if (isinstance(_type, astroid.Instance) and
1924
                        _type.qname() in self.skipped_classnames):
1925
                    return
1926
            suggestion = '%s %s %s' % (left.as_string(),
1927
                                       self.reverse_op[operator],
1928
                                       right.as_string())
1929
            self.add_message('unneeded-not', node=node,
1930
                             args=(node.as_string(), suggestion))
1931

    
1932

    
1933
class MultipleTypesChecker(BaseChecker):
1934
    """Checks for variable type redefinitions (NoneType excepted)
1935

1936
    At a function, method, class or module scope
1937

1938
    This rule could be improved:
1939
    - Currently, if an attribute is set to different types in 2 methods of a
1940
      same class, it won't be detected (see functional test)
1941
    - One could improve the support for inference on assignment with tuples,
1942
      ifexpr, etc. Also it would be great to have support for inference on
1943
      str.split()
1944
    """
1945
    __implements__ = IAstroidChecker
1946

    
1947
    name = 'multiple_types'
1948
    msgs = {'R0204': ('Redefinition of %s type from %s to %s',
1949
                      'redefined-variable-type',
1950
                      'Used when the type of a variable changes inside a '
1951
                      'method or a function.'
1952
                     ),
1953
           }
1954

    
1955
    def visit_classdef(self, _):
1956
        self._assigns.append({})
1957

    
1958
    @check_messages('redefined-variable-type')
1959
    def leave_classdef(self, _):
1960
        self._check_and_add_messages()
1961

    
1962
    visit_functiondef = visit_classdef
1963
    leave_functiondef = leave_module = leave_classdef
1964

    
1965
    def visit_module(self, _):
1966
        self._assigns = [{}]
1967

    
1968
    def _check_and_add_messages(self):
1969
        assigns = self._assigns.pop()
1970
        for name, args in assigns.items():
1971
            if len(args) <= 1:
1972
                continue
1973
            _, orig_type = args[0]
1974
            # Check if there is a type in the following nodes that would be
1975
            # different from orig_type.
1976
            for redef_node, redef_type in args[1:]:
1977
                if redef_type != orig_type:
1978
                    orig_type = orig_type.replace(BUILTINS + ".", '')
1979
                    redef_type = redef_type.replace(BUILTINS + ".", '')
1980
                    self.add_message('redefined-variable-type', node=redef_node,
1981
                                     args=(name, orig_type, redef_type))
1982
                    break
1983

    
1984
    def visit_assign(self, node):
1985
        # we don't handle multiple assignment nor slice assignment
1986
        target = node.targets[0]
1987
        if isinstance(target, (astroid.Tuple, astroid.Subscript)):
1988
            return
1989
        # ignore NoneType
1990
        if _is_none(node):
1991
            return
1992
        _type = _node_type(node.value)
1993
        if _type:
1994
            self._assigns[-1].setdefault(target.as_string(), []).append(
1995
                (node, _type.pytype()))
1996

    
1997

    
1998
def register(linter):
1999
    """required method to auto register this checker"""
2000
    linter.register_checker(BasicErrorChecker(linter))
2001
    linter.register_checker(BasicChecker(linter))
2002
    linter.register_checker(NameChecker(linter))
2003
    linter.register_checker(DocStringChecker(linter))
2004
    linter.register_checker(PassChecker(linter))
2005
    linter.register_checker(LambdaForComprehensionChecker(linter))
2006
    linter.register_checker(ComparisonChecker(linter))
2007
    linter.register_checker(NotChecker(linter))
2008
    linter.register_checker(RecommandationChecker(linter))
2009
    linter.register_checker(ElifChecker(linter))
2010
    linter.register_checker(MultipleTypesChecker(linter))