1# Copyright (c) 2006-2016 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
2# Copyright (c) 2010 Daniel Harding <dharding@gmail.com>
3# Copyright (c) 2012-2014 Google, Inc.
4# Copyright (c) 2013-2020 Claudiu Popa <pcmanticore@gmail.com>
5# Copyright (c) 2014 Brett Cannon <brett@python.org>
6# Copyright (c) 2014 Arun Persaud <arun@nubati.net>
7# Copyright (c) 2015 Nick Bastin <nick.bastin@gmail.com>
8# Copyright (c) 2015 Michael Kefeder <oss@multiwave.ch>
9# Copyright (c) 2015 Dmitry Pribysh <dmand@yandex.ru>
10# Copyright (c) 2015 Stephane Wirtel <stephane@wirtel.be>
11# Copyright (c) 2015 Cosmin Poieana <cmin@ropython.org>
12# Copyright (c) 2015 Florian Bruhin <me@the-compiler.org>
13# Copyright (c) 2015 Radu Ciorba <radu@devrandom.ro>
14# Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
15# Copyright (c) 2016, 2019 Ashley Whetter <ashley@awhetter.co.uk>
16# Copyright (c) 2016, 2018 Jakub Wilk <jwilk@jwilk.net>
17# Copyright (c) 2016-2017 Łukasz Rogalski <rogalski.91@gmail.com>
18# Copyright (c) 2016 Glenn Matthews <glenn@e-dad.net>
19# Copyright (c) 2016 Elias Dorneles <eliasdorneles@gmail.com>
20# Copyright (c) 2016 Yannack <yannack@users.noreply.github.com>
21# Copyright (c) 2016 Alex Jurkiewicz <alex@jurkiewi.cz>
22# Copyright (c) 2017, 2019-2021 Pierre Sassoulas <pierre.sassoulas@gmail.com>
23# Copyright (c) 2017, 2019-2021 hippo91 <guillaume.peillex@gmail.com>
24# Copyright (c) 2017 danields <danields761@gmail.com>
25# Copyright (c) 2017 Jacques Kvam <jwkvam@gmail.com>
26# Copyright (c) 2017 ttenhoeve-aa <ttenhoeve@appannie.com>
27# Copyright (c) 2018-2019, 2021 Nick Drozd <nicholasdrozd@gmail.com>
28# Copyright (c) 2018-2019, 2021 Ville Skyttä <ville.skytta@iki.fi>
29# Copyright (c) 2018 Sergei Lebedev <185856+superbobry@users.noreply.github.com>
30# Copyright (c) 2018 Lucas Cimon <lucas.cimon@gmail.com>
31# Copyright (c) 2018 ssolanki <sushobhitsolanki@gmail.com>
32# Copyright (c) 2018 Natalie Serebryakova <natalie.serebryakova@Natalies-MacBook-Pro.local>
33# Copyright (c) 2018 Sushobhit <31987769+sushobhit27@users.noreply.github.com>
34# Copyright (c) 2018 SergeyKosarchuk <sergeykosarchuk@gmail.com>
35# Copyright (c) 2018 Steven M. Vascellaro <svascellaro@gmail.com>
36# Copyright (c) 2018 Mike Frysinger <vapier@gmail.com>
37# Copyright (c) 2018 Chris Lamb <chris@chris-lamb.co.uk>
38# Copyright (c) 2018 glmdgrielson <32415403+glmdgrielson@users.noreply.github.com>
39# Copyright (c) 2019 Daniel Draper <Germandrummer92@users.noreply.github.com>
40# Copyright (c) 2019 Hugo van Kemenade <hugovk@users.noreply.github.com>
41# Copyright (c) 2019 Niko Wenselowski <niko@nerdno.de>
42# Copyright (c) 2019 Nikita Sobolev <mail@sobolevn.me>
43# Copyright (c) 2019 Oisín Moran <OisinMoran@users.noreply.github.com>
44# Copyright (c) 2019 Fantix King <fantix@uchicago.edu>
45# Copyright (c) 2020 Peter Kolbus <peter.kolbus@gmail.com>
46# Copyright (c) 2020 ethan-leba <ethanleba5@gmail.com>
47# Copyright (c) 2020 へーさん <hira9603859504@gmail.com>
48# Copyright (c) 2020 Damien Baty <damien.baty@polyconseil.fr>
49# Copyright (c) 2020 Ram Rachum <ram@rachum.com>
50# Copyright (c) 2020 Anthony Sottile <asottile@umich.edu>
51# Copyright (c) 2020 bernie gray <bfgray3@users.noreply.github.com>
52# Copyright (c) 2020 Gabriel R Sezefredo <g@briel.dev>
53# Copyright (c) 2020 Benny <benny.mueller91@gmail.com>
54# Copyright (c) 2020 Anubhav <35621759+anubh-v@users.noreply.github.com>
55# Copyright (c) 2021 Daniël van Noord <13665637+DanielNoord@users.noreply.github.com>
56# Copyright (c) 2021 Tushar Sadhwani <tushar.sadhwani000@gmail.com>
57# Copyright (c) 2021 Tim Martin <tim@asymptotic.co.uk>
58# Copyright (c) 2021 Jaehoon Hwang <jaehoonhwang@users.noreply.github.com>
59# Copyright (c) 2021 jaydesl <35102795+jaydesl@users.noreply.github.com>
60# Copyright (c) 2021 Marc Mueller <30130371+cdce8p@users.noreply.github.com>
61# Copyright (c) 2021 bot <bot@noreply.github.com>
62# Copyright (c) 2021 Yilei "Dolee" Yang <yileiyang@google.com>
63# Copyright (c) 2021 Lorena B <46202743+lorena-b@users.noreply.github.com>
64# Copyright (c) 2021 David Liu <david@cs.toronto.edu>
65# Copyright (c) 2021 Andreas Finkler <andi.finkler@gmail.com>
66# Copyright (c) 2021 Or Bahari <orbahari@mail.tau.ac.il>
67
68# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
69# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
70
71"""basic checker for Python code"""
72import collections
73import itertools
74import re
75import sys
76from typing import Any, Dict, Iterator, Optional, Pattern, cast
77
78import astroid
79from astroid import nodes
80
81from pylint import checkers, interfaces
82from pylint import utils as lint_utils
83from pylint.checkers import utils
84from pylint.checkers.utils import (
85    infer_all,
86    is_overload_stub,
87    is_property_deleter,
88    is_property_setter,
89)
90from pylint.reporters.ureports import nodes as reporter_nodes
91from pylint.utils import LinterStats
92from pylint.utils.utils import get_global_option
93
94if sys.version_info >= (3, 8):
95    from typing import Literal
96else:
97    from typing_extensions import Literal
98
99
100class NamingStyle:
101    """It may seem counterintuitive that single naming style has multiple "accepted"
102    forms of regular expressions, but we need to special-case stuff like dunder names
103    in method names."""
104
105    ANY: Pattern[str] = re.compile(".*")
106    CLASS_NAME_RGX: Pattern[str] = ANY
107    MOD_NAME_RGX: Pattern[str] = ANY
108    CONST_NAME_RGX: Pattern[str] = ANY
109    COMP_VAR_RGX: Pattern[str] = ANY
110    DEFAULT_NAME_RGX: Pattern[str] = ANY
111    CLASS_ATTRIBUTE_RGX: Pattern[str] = ANY
112
113    @classmethod
114    def get_regex(cls, name_type):
115        return {
116            "module": cls.MOD_NAME_RGX,
117            "const": cls.CONST_NAME_RGX,
118            "class": cls.CLASS_NAME_RGX,
119            "function": cls.DEFAULT_NAME_RGX,
120            "method": cls.DEFAULT_NAME_RGX,
121            "attr": cls.DEFAULT_NAME_RGX,
122            "argument": cls.DEFAULT_NAME_RGX,
123            "variable": cls.DEFAULT_NAME_RGX,
124            "class_attribute": cls.CLASS_ATTRIBUTE_RGX,
125            "class_const": cls.CONST_NAME_RGX,
126            "inlinevar": cls.COMP_VAR_RGX,
127        }[name_type]
128
129
130class SnakeCaseStyle(NamingStyle):
131    """Regex rules for snake_case naming style."""
132
133    CLASS_NAME_RGX = re.compile(r"[^\W\dA-Z][^\WA-Z]+$")
134    MOD_NAME_RGX = re.compile(r"[^\W\dA-Z][^\WA-Z]*$")
135    CONST_NAME_RGX = re.compile(r"([^\W\dA-Z][^\WA-Z]*|__.*__)$")
136    COMP_VAR_RGX = re.compile(r"[^\W\dA-Z][^\WA-Z]*$")
137    DEFAULT_NAME_RGX = re.compile(
138        r"([^\W\dA-Z][^\WA-Z]{2,}|_[^\WA-Z]*|__[^\WA-Z\d_][^\WA-Z]+__)$"
139    )
140    CLASS_ATTRIBUTE_RGX = re.compile(r"([^\W\dA-Z][^\WA-Z]{2,}|__.*__)$")
141
142
143class CamelCaseStyle(NamingStyle):
144    """Regex rules for camelCase naming style."""
145
146    CLASS_NAME_RGX = re.compile(r"[^\W\dA-Z][^\W_]+$")
147    MOD_NAME_RGX = re.compile(r"[^\W\dA-Z][^\W_]*$")
148    CONST_NAME_RGX = re.compile(r"([^\W\dA-Z][^\W_]*|__.*__)$")
149    COMP_VAR_RGX = re.compile(r"[^\W\dA-Z][^\W_]*$")
150    DEFAULT_NAME_RGX = re.compile(r"([^\W\dA-Z][^\W_]{2,}|__[^\W\dA-Z_]\w+__)$")
151    CLASS_ATTRIBUTE_RGX = re.compile(r"([^\W\dA-Z][^\W_]{2,}|__.*__)$")
152
153
154class PascalCaseStyle(NamingStyle):
155    """Regex rules for PascalCase naming style."""
156
157    CLASS_NAME_RGX = re.compile(r"[^\W\da-z][^\W_]+$")
158    MOD_NAME_RGX = re.compile(r"[^\W\da-z][^\W_]+$")
159    CONST_NAME_RGX = re.compile(r"([^\W\da-z][^\W_]*|__.*__)$")
160    COMP_VAR_RGX = re.compile(r"[^\W\da-z][^\W_]+$")
161    DEFAULT_NAME_RGX = re.compile(r"([^\W\da-z][^\W_]{2,}|__[^\W\dA-Z_]\w+__)$")
162    CLASS_ATTRIBUTE_RGX = re.compile(r"[^\W\da-z][^\W_]{2,}$")
163
164
165class UpperCaseStyle(NamingStyle):
166    """Regex rules for UPPER_CASE naming style."""
167
168    CLASS_NAME_RGX = re.compile(r"[^\W\da-z][^\Wa-z]+$")
169    MOD_NAME_RGX = re.compile(r"[^\W\da-z][^\Wa-z]+$")
170    CONST_NAME_RGX = re.compile(r"([^\W\da-z][^\Wa-z]*|__.*__)$")
171    COMP_VAR_RGX = re.compile(r"[^\W\da-z][^\Wa-z]+$")
172    DEFAULT_NAME_RGX = re.compile(r"([^\W\da-z][^\Wa-z]{2,}|__[^\W\dA-Z_]\w+__)$")
173    CLASS_ATTRIBUTE_RGX = re.compile(r"[^\W\da-z][^\Wa-z]{2,}$")
174
175
176class AnyStyle(NamingStyle):
177    pass
178
179
180NAMING_STYLES = {
181    "snake_case": SnakeCaseStyle,
182    "camelCase": CamelCaseStyle,
183    "PascalCase": PascalCaseStyle,
184    "UPPER_CASE": UpperCaseStyle,
185    "any": AnyStyle,
186}
187
188# do not require a doc string on private/system methods
189NO_REQUIRED_DOC_RGX = re.compile("^_")
190REVERSED_PROTOCOL_METHOD = "__reversed__"
191SEQUENCE_PROTOCOL_METHODS = ("__getitem__", "__len__")
192REVERSED_METHODS = (SEQUENCE_PROTOCOL_METHODS, (REVERSED_PROTOCOL_METHOD,))
193TYPECHECK_COMPARISON_OPERATORS = frozenset(("is", "is not", "==", "!="))
194LITERAL_NODE_TYPES = (nodes.Const, nodes.Dict, nodes.List, nodes.Set)
195UNITTEST_CASE = "unittest.case"
196TYPE_QNAME = "builtins.type"
197ABC_METACLASSES = {"_py_abc.ABCMeta", "abc.ABCMeta"}  # Python 3.7+,
198
199# Name categories that are always consistent with all naming conventions.
200EXEMPT_NAME_CATEGORIES = {"exempt", "ignore"}
201
202# A mapping from qname -> symbol, to be used when generating messages
203# about dangerous default values as arguments
204DEFAULT_ARGUMENT_SYMBOLS = dict(
205    zip(
206        [".".join(["builtins", x]) for x in ("set", "dict", "list")],
207        ["set()", "{}", "[]"],
208    ),
209    **{
210        x: f"{x}()"
211        for x in (
212            "collections.deque",
213            "collections.ChainMap",
214            "collections.Counter",
215            "collections.OrderedDict",
216            "collections.defaultdict",
217            "collections.UserDict",
218            "collections.UserList",
219        )
220    },
221)
222
223COMPARISON_OPERATORS = frozenset(("==", "!=", "<", ">", "<=", ">="))
224# List of methods which can be redefined
225REDEFINABLE_METHODS = frozenset(("__module__",))
226TYPING_FORWARD_REF_QNAME = "typing.ForwardRef"
227
228
229def _redefines_import(node):
230    """Detect that the given node (AssignName) is inside an
231    exception handler and redefines an import from the tryexcept body.
232    Returns True if the node redefines an import, False otherwise.
233    """
234    current = node
235    while current and not isinstance(current.parent, nodes.ExceptHandler):
236        current = current.parent
237    if not current or not utils.error_of_type(current.parent, ImportError):
238        return False
239    try_block = current.parent.parent
240    for import_node in try_block.nodes_of_class((nodes.ImportFrom, nodes.Import)):
241        for name, alias in import_node.names:
242            if alias:
243                if alias == node.name:
244                    return True
245            elif name == node.name:
246                return True
247    return False
248
249
250LOOPLIKE_NODES = (
251    nodes.For,
252    nodes.ListComp,
253    nodes.SetComp,
254    nodes.DictComp,
255    nodes.GeneratorExp,
256)
257
258
259def in_loop(node: nodes.NodeNG) -> bool:
260    """Return whether the node is inside a kind of for loop"""
261    return any(isinstance(parent, LOOPLIKE_NODES) for parent in node.node_ancestors())
262
263
264def in_nested_list(nested_list, obj):
265    """return true if the object is an element of <nested_list> or of a nested
266    list
267    """
268    for elmt in nested_list:
269        if isinstance(elmt, (list, tuple)):
270            if in_nested_list(elmt, obj):
271                return True
272        elif elmt == obj:
273            return True
274    return False
275
276
277def _get_break_loop_node(break_node):
278    """
279    Returns the loop node that holds the break node in arguments.
280
281    Args:
282        break_node (astroid.Break): the break node of interest.
283
284    Returns:
285        astroid.For or astroid.While: the loop node holding the break node.
286    """
287    loop_nodes = (nodes.For, nodes.While)
288    parent = break_node.parent
289    while not isinstance(parent, loop_nodes) or break_node in getattr(
290        parent, "orelse", []
291    ):
292        break_node = parent
293        parent = parent.parent
294        if parent is None:
295            break
296    return parent
297
298
299def _loop_exits_early(loop):
300    """
301    Returns true if a loop may ends up in a break statement.
302
303    Args:
304        loop (astroid.For, astroid.While): the loop node inspected.
305
306    Returns:
307        bool: True if the loop may ends up in a break statement, False otherwise.
308    """
309    loop_nodes = (nodes.For, nodes.While)
310    definition_nodes = (nodes.FunctionDef, nodes.ClassDef)
311    inner_loop_nodes = [
312        _node
313        for _node in loop.nodes_of_class(loop_nodes, skip_klass=definition_nodes)
314        if _node != loop
315    ]
316    return any(
317        _node
318        for _node in loop.nodes_of_class(nodes.Break, skip_klass=definition_nodes)
319        if _get_break_loop_node(_node) not in inner_loop_nodes
320    )
321
322
323def _is_multi_naming_match(match, node_type, confidence):
324    return (
325        match is not None
326        and match.lastgroup is not None
327        and match.lastgroup not in EXEMPT_NAME_CATEGORIES
328        and (node_type != "method" or confidence != interfaces.INFERENCE_FAILURE)
329    )
330
331
332BUILTIN_PROPERTY = "builtins.property"
333
334
335def _get_properties(config):
336    """Returns a tuple of property classes and names.
337
338    Property classes are fully qualified, such as 'abc.abstractproperty' and
339    property names are the actual names, such as 'abstract_property'.
340    """
341    property_classes = {BUILTIN_PROPERTY}
342    property_names = set()  # Not returning 'property', it has its own check.
343    if config is not None:
344        property_classes.update(config.property_classes)
345        property_names.update(
346            prop.rsplit(".", 1)[-1] for prop in config.property_classes
347        )
348    return property_classes, property_names
349
350
351def _determine_function_name_type(node: nodes.FunctionDef, config=None):
352    """Determine the name type whose regex the a function's name should match.
353
354    :param node: A function node.
355    :param config: Configuration from which to pull additional property classes.
356    :type config: :class:`optparse.Values`
357
358    :returns: One of ('function', 'method', 'attr')
359    :rtype: str
360    """
361    property_classes, property_names = _get_properties(config)
362    if not node.is_method():
363        return "function"
364
365    if is_property_setter(node) or is_property_deleter(node):
366        # If the function is decorated using the prop_method.{setter,getter}
367        # form, treat it like an attribute as well.
368        return "attr"
369
370    decorators = node.decorators.nodes if node.decorators else []
371    for decorator in decorators:
372        # If the function is a property (decorated with @property
373        # or @abc.abstractproperty), the name type is 'attr'.
374        if isinstance(decorator, nodes.Name) or (
375            isinstance(decorator, nodes.Attribute)
376            and decorator.attrname in property_names
377        ):
378            inferred = utils.safe_infer(decorator)
379            if (
380                inferred
381                and hasattr(inferred, "qname")
382                and inferred.qname() in property_classes
383            ):
384                return "attr"
385    return "method"
386
387
388def _has_abstract_methods(node):
389    """
390    Determine if the given `node` has abstract methods.
391
392    The methods should be made abstract by decorating them
393    with `abc` decorators.
394    """
395    return len(utils.unimplemented_abstract_methods(node)) > 0
396
397
398def report_by_type_stats(
399    sect,
400    stats: LinterStats,
401    old_stats: Optional[LinterStats],
402):
403    """make a report of
404
405    * percentage of different types documented
406    * percentage of different types with a bad name
407    """
408    # percentage of different types documented and/or with a bad name
409    nice_stats: Dict[str, Dict[str, str]] = {}
410    for node_type in ("module", "class", "method", "function"):
411        node_type = cast(Literal["function", "class", "method", "module"], node_type)
412        total = stats.get_node_count(node_type)
413        nice_stats[node_type] = {}
414        if total != 0:
415            undocumented_node = stats.get_undocumented(node_type)
416            documented = total - undocumented_node
417            percent = (documented * 100.0) / total
418            nice_stats[node_type]["percent_documented"] = f"{percent:.2f}"
419            badname_node = stats.get_bad_names(node_type)
420            percent = (badname_node * 100.0) / total
421            nice_stats[node_type]["percent_badname"] = f"{percent:.2f}"
422    lines = ["type", "number", "old number", "difference", "%documented", "%badname"]
423    for node_type in ("module", "class", "method", "function"):
424        node_type = cast(Literal["function", "class", "method", "module"], node_type)
425        new = stats.get_node_count(node_type)
426        old = old_stats.get_node_count(node_type) if old_stats else None
427        diff_str = lint_utils.diff_string(old, new) if old else None
428        lines += [
429            node_type,
430            str(new),
431            str(old) if old else "NC",
432            diff_str if diff_str else "NC",
433            nice_stats[node_type].get("percent_documented", "0"),
434            nice_stats[node_type].get("percent_badname", "0"),
435        ]
436    sect.append(reporter_nodes.Table(children=lines, cols=6, rheaders=1))
437
438
439def redefined_by_decorator(node):
440    """return True if the object is a method redefined via decorator.
441
442    For example:
443        @property
444        def x(self): return self._x
445        @x.setter
446        def x(self, value): self._x = value
447    """
448    if node.decorators:
449        for decorator in node.decorators.nodes:
450            if (
451                isinstance(decorator, nodes.Attribute)
452                and getattr(decorator.expr, "name", None) == node.name
453            ):
454                return True
455    return False
456
457
458class _BasicChecker(checkers.BaseChecker):
459    __implements__ = interfaces.IAstroidChecker
460    name = "basic"
461
462
463class BasicErrorChecker(_BasicChecker):
464    msgs = {
465        "E0100": (
466            "__init__ method is a generator",
467            "init-is-generator",
468            "Used when the special class method __init__ is turned into a "
469            "generator by a yield in its body.",
470        ),
471        "E0101": (
472            "Explicit return in __init__",
473            "return-in-init",
474            "Used when the special class method __init__ has an explicit "
475            "return value.",
476        ),
477        "E0102": (
478            "%s already defined line %s",
479            "function-redefined",
480            "Used when a function / class / method is redefined.",
481        ),
482        "E0103": (
483            "%r not properly in loop",
484            "not-in-loop",
485            "Used when break or continue keywords are used outside a loop.",
486        ),
487        "E0104": (
488            "Return outside function",
489            "return-outside-function",
490            'Used when a "return" statement is found outside a function or method.',
491        ),
492        "E0105": (
493            "Yield outside function",
494            "yield-outside-function",
495            'Used when a "yield" statement is found outside a function or method.',
496        ),
497        "E0106": (
498            "Return with argument inside generator",
499            "return-arg-in-generator",
500            'Used when a "return" statement with an argument is found '
501            "outside in a generator function or method (e.g. with some "
502            '"yield" statements).',
503            {"maxversion": (3, 3)},
504        ),
505        "E0107": (
506            "Use of the non-existent %s operator",
507            "nonexistent-operator",
508            "Used when you attempt to use the C-style pre-increment or "
509            "pre-decrement operator -- and ++, which doesn't exist in Python.",
510        ),
511        "E0108": (
512            "Duplicate argument name %s in function definition",
513            "duplicate-argument-name",
514            "Duplicate argument names in function definitions are syntax errors.",
515        ),
516        "E0110": (
517            "Abstract class %r with abstract methods instantiated",
518            "abstract-class-instantiated",
519            "Used when an abstract class with `abc.ABCMeta` as metaclass "
520            "has abstract methods and is instantiated.",
521        ),
522        "W0120": (
523            "Else clause on loop without a break statement",
524            "useless-else-on-loop",
525            "Loops should only have an else clause if they can exit early "
526            "with a break statement, otherwise the statements under else "
527            "should be on the same scope as the loop itself.",
528        ),
529        "E0112": (
530            "More than one starred expression in assignment",
531            "too-many-star-expressions",
532            "Emitted when there are more than one starred "
533            "expressions (`*x`) in an assignment. This is a SyntaxError.",
534        ),
535        "E0113": (
536            "Starred assignment target must be in a list or tuple",
537            "invalid-star-assignment-target",
538            "Emitted when a star expression is used as a starred assignment target.",
539        ),
540        "E0114": (
541            "Can use starred expression only in assignment target",
542            "star-needs-assignment-target",
543            "Emitted when a star expression is not used in an assignment target.",
544        ),
545        "E0115": (
546            "Name %r is nonlocal and global",
547            "nonlocal-and-global",
548            "Emitted when a name is both nonlocal and global.",
549        ),
550        "E0116": (
551            "'continue' not supported inside 'finally' clause",
552            "continue-in-finally",
553            "Emitted when the `continue` keyword is found "
554            "inside a finally clause, which is a SyntaxError.",
555            {"maxversion": (3, 8)},
556        ),
557        "E0117": (
558            "nonlocal name %s found without binding",
559            "nonlocal-without-binding",
560            "Emitted when a nonlocal variable does not have an attached "
561            "name somewhere in the parent scopes",
562        ),
563        "E0118": (
564            "Name %r is used prior to global declaration",
565            "used-prior-global-declaration",
566            "Emitted when a name is used prior a global declaration, "
567            "which results in an error since Python 3.6.",
568            {"minversion": (3, 6)},
569        ),
570    }
571
572    @utils.check_messages("function-redefined")
573    def visit_classdef(self, node: nodes.ClassDef) -> None:
574        self._check_redefinition("class", node)
575
576    def _too_many_starred_for_tuple(self, assign_tuple):
577        starred_count = 0
578        for elem in assign_tuple.itered():
579            if isinstance(elem, nodes.Tuple):
580                return self._too_many_starred_for_tuple(elem)
581            if isinstance(elem, nodes.Starred):
582                starred_count += 1
583        return starred_count > 1
584
585    @utils.check_messages("too-many-star-expressions", "invalid-star-assignment-target")
586    def visit_assign(self, node: nodes.Assign) -> None:
587        # Check *a, *b = ...
588        assign_target = node.targets[0]
589        # Check *a = b
590        if isinstance(node.targets[0], nodes.Starred):
591            self.add_message("invalid-star-assignment-target", node=node)
592
593        if not isinstance(assign_target, nodes.Tuple):
594            return
595        if self._too_many_starred_for_tuple(assign_target):
596            self.add_message("too-many-star-expressions", node=node)
597
598    @utils.check_messages("star-needs-assignment-target")
599    def visit_starred(self, node: nodes.Starred) -> None:
600        """Check that a Starred expression is used in an assignment target."""
601        if isinstance(node.parent, nodes.Call):
602            # f(*args) is converted to Call(args=[Starred]), so ignore
603            # them for this check.
604            return
605        if isinstance(node.parent, (nodes.List, nodes.Tuple, nodes.Set, nodes.Dict)):
606            # PEP 448 unpacking.
607            return
608
609        stmt = node.statement()
610        if not isinstance(stmt, nodes.Assign):
611            return
612
613        if stmt.value is node or stmt.value.parent_of(node):
614            self.add_message("star-needs-assignment-target", node=node)
615
616    @utils.check_messages(
617        "init-is-generator",
618        "return-in-init",
619        "function-redefined",
620        "return-arg-in-generator",
621        "duplicate-argument-name",
622        "nonlocal-and-global",
623        "used-prior-global-declaration",
624    )
625    def visit_functiondef(self, node: nodes.FunctionDef) -> None:
626        self._check_nonlocal_and_global(node)
627        self._check_name_used_prior_global(node)
628        if not redefined_by_decorator(
629            node
630        ) and not utils.is_registered_in_singledispatch_function(node):
631            self._check_redefinition(node.is_method() and "method" or "function", node)
632        # checks for max returns, branch, return in __init__
633        returns = node.nodes_of_class(
634            nodes.Return, skip_klass=(nodes.FunctionDef, nodes.ClassDef)
635        )
636        if node.is_method() and node.name == "__init__":
637            if node.is_generator():
638                self.add_message("init-is-generator", node=node)
639            else:
640                values = [r.value for r in returns]
641                # Are we returning anything but None from constructors
642                if any(v for v in values if not utils.is_none(v)):
643                    self.add_message("return-in-init", node=node)
644        # Check for duplicate names by clustering args with same name for detailed report
645        arg_clusters = collections.defaultdict(list)
646        arguments: Iterator[Any] = filter(None, [node.args.args, node.args.kwonlyargs])
647
648        for arg in itertools.chain.from_iterable(arguments):
649            arg_clusters[arg.name].append(arg)
650
651        # provide detailed report about each repeated argument
652        for argument_duplicates in arg_clusters.values():
653            if len(argument_duplicates) != 1:
654                for argument in argument_duplicates:
655                    self.add_message(
656                        "duplicate-argument-name",
657                        line=argument.lineno,
658                        node=argument,
659                        args=(argument.name,),
660                    )
661
662    visit_asyncfunctiondef = visit_functiondef
663
664    def _check_name_used_prior_global(self, node):
665
666        scope_globals = {
667            name: child
668            for child in node.nodes_of_class(nodes.Global)
669            for name in child.names
670            if child.scope() is node
671        }
672
673        if not scope_globals:
674            return
675
676        for node_name in node.nodes_of_class(nodes.Name):
677            if node_name.scope() is not node:
678                continue
679
680            name = node_name.name
681            corresponding_global = scope_globals.get(name)
682            if not corresponding_global:
683                continue
684
685            global_lineno = corresponding_global.fromlineno
686            if global_lineno and global_lineno > node_name.fromlineno:
687                self.add_message(
688                    "used-prior-global-declaration", node=node_name, args=(name,)
689                )
690
691    def _check_nonlocal_and_global(self, node):
692        """Check that a name is both nonlocal and global."""
693
694        def same_scope(current):
695            return current.scope() is node
696
697        from_iter = itertools.chain.from_iterable
698        nonlocals = set(
699            from_iter(
700                child.names
701                for child in node.nodes_of_class(nodes.Nonlocal)
702                if same_scope(child)
703            )
704        )
705
706        if not nonlocals:
707            return
708
709        global_vars = set(
710            from_iter(
711                child.names
712                for child in node.nodes_of_class(nodes.Global)
713                if same_scope(child)
714            )
715        )
716        for name in nonlocals.intersection(global_vars):
717            self.add_message("nonlocal-and-global", args=(name,), node=node)
718
719    @utils.check_messages("return-outside-function")
720    def visit_return(self, node: nodes.Return) -> None:
721        if not isinstance(node.frame(), nodes.FunctionDef):
722            self.add_message("return-outside-function", node=node)
723
724    @utils.check_messages("yield-outside-function")
725    def visit_yield(self, node: nodes.Yield) -> None:
726        self._check_yield_outside_func(node)
727
728    @utils.check_messages("yield-outside-function")
729    def visit_yieldfrom(self, node: nodes.YieldFrom) -> None:
730        self._check_yield_outside_func(node)
731
732    @utils.check_messages("not-in-loop", "continue-in-finally")
733    def visit_continue(self, node: nodes.Continue) -> None:
734        self._check_in_loop(node, "continue")
735
736    @utils.check_messages("not-in-loop")
737    def visit_break(self, node: nodes.Break) -> None:
738        self._check_in_loop(node, "break")
739
740    @utils.check_messages("useless-else-on-loop")
741    def visit_for(self, node: nodes.For) -> None:
742        self._check_else_on_loop(node)
743
744    @utils.check_messages("useless-else-on-loop")
745    def visit_while(self, node: nodes.While) -> None:
746        self._check_else_on_loop(node)
747
748    @utils.check_messages("nonexistent-operator")
749    def visit_unaryop(self, node: nodes.UnaryOp) -> None:
750        """check use of the non-existent ++ and -- operator operator"""
751        if (
752            (node.op in "+-")
753            and isinstance(node.operand, nodes.UnaryOp)
754            and (node.operand.op == node.op)
755        ):
756            self.add_message("nonexistent-operator", node=node, args=node.op * 2)
757
758    def _check_nonlocal_without_binding(self, node, name):
759        current_scope = node.scope()
760        while True:
761            if current_scope.parent is None:
762                break
763
764            if not isinstance(current_scope, (nodes.ClassDef, nodes.FunctionDef)):
765                self.add_message("nonlocal-without-binding", args=(name,), node=node)
766                return
767
768            if name not in current_scope.locals:
769                current_scope = current_scope.parent.scope()
770                continue
771
772            # Okay, found it.
773            return
774
775        if not isinstance(current_scope, nodes.FunctionDef):
776            self.add_message("nonlocal-without-binding", args=(name,), node=node)
777
778    @utils.check_messages("nonlocal-without-binding")
779    def visit_nonlocal(self, node: nodes.Nonlocal) -> None:
780        for name in node.names:
781            self._check_nonlocal_without_binding(node, name)
782
783    @utils.check_messages("abstract-class-instantiated")
784    def visit_call(self, node: nodes.Call) -> None:
785        """Check instantiating abstract class with
786        abc.ABCMeta as metaclass.
787        """
788        for inferred in infer_all(node.func):
789            self._check_inferred_class_is_abstract(inferred, node)
790
791    def _check_inferred_class_is_abstract(self, inferred, node):
792        if not isinstance(inferred, nodes.ClassDef):
793            return
794
795        klass = utils.node_frame_class(node)
796        if klass is inferred:
797            # Don't emit the warning if the class is instantiated
798            # in its own body or if the call is not an instance
799            # creation. If the class is instantiated into its own
800            # body, we're expecting that it knows what it is doing.
801            return
802
803        # __init__ was called
804        abstract_methods = _has_abstract_methods(inferred)
805
806        if not abstract_methods:
807            return
808
809        metaclass = inferred.metaclass()
810
811        if metaclass is None:
812            # Python 3.4 has `abc.ABC`, which won't be detected
813            # by ClassNode.metaclass()
814            for ancestor in inferred.ancestors():
815                if ancestor.qname() == "abc.ABC":
816                    self.add_message(
817                        "abstract-class-instantiated", args=(inferred.name,), node=node
818                    )
819                    break
820
821            return
822
823        if metaclass.qname() in ABC_METACLASSES:
824            self.add_message(
825                "abstract-class-instantiated", args=(inferred.name,), node=node
826            )
827
828    def _check_yield_outside_func(self, node):
829        if not isinstance(node.frame(), (nodes.FunctionDef, nodes.Lambda)):
830            self.add_message("yield-outside-function", node=node)
831
832    def _check_else_on_loop(self, node):
833        """Check that any loop with an else clause has a break statement."""
834        if node.orelse and not _loop_exits_early(node):
835            self.add_message(
836                "useless-else-on-loop",
837                node=node,
838                # This is not optimal, but the line previous
839                # to the first statement in the else clause
840                # will usually be the one that contains the else:.
841                line=node.orelse[0].lineno - 1,
842            )
843
844    def _check_in_loop(self, node, node_name):
845        """check that a node is inside a for or while loop"""
846        for parent in node.node_ancestors():
847            if isinstance(parent, (nodes.For, nodes.While)):
848                if node not in parent.orelse:
849                    return
850
851            if isinstance(parent, (nodes.ClassDef, nodes.FunctionDef)):
852                break
853            if (
854                isinstance(parent, nodes.TryFinally)
855                and node in parent.finalbody
856                and isinstance(node, nodes.Continue)
857            ):
858                self.add_message("continue-in-finally", node=node)
859
860        self.add_message("not-in-loop", node=node, args=node_name)
861
862    def _check_redefinition(self, redeftype, node):
863        """check for redefinition of a function / method / class name"""
864        parent_frame = node.parent.frame()
865
866        # Ignore function stubs created for type information
867        redefinitions = [
868            i
869            for i in parent_frame.locals[node.name]
870            if not (isinstance(i.parent, nodes.AnnAssign) and i.parent.simple)
871        ]
872        defined_self = next(
873            (local for local in redefinitions if not utils.is_overload_stub(local)),
874            node,
875        )
876        if defined_self is not node and not astroid.are_exclusive(node, defined_self):
877            # Additional checks for methods which are not considered
878            # redefined, since they are already part of the base API.
879            if (
880                isinstance(parent_frame, nodes.ClassDef)
881                and node.name in REDEFINABLE_METHODS
882            ):
883                return
884
885            # Skip typing.overload() functions.
886            if utils.is_overload_stub(node):
887                return
888
889            # Exempt functions redefined on a condition.
890            if isinstance(node.parent, nodes.If):
891                # Exempt "if not <func>" cases
892                if (
893                    isinstance(node.parent.test, nodes.UnaryOp)
894                    and node.parent.test.op == "not"
895                    and isinstance(node.parent.test.operand, nodes.Name)
896                    and node.parent.test.operand.name == node.name
897                ):
898                    return
899
900                # Exempt "if <func> is not None" cases
901                # pylint: disable=too-many-boolean-expressions
902                if (
903                    isinstance(node.parent.test, nodes.Compare)
904                    and isinstance(node.parent.test.left, nodes.Name)
905                    and node.parent.test.left.name == node.name
906                    and node.parent.test.ops[0][0] == "is"
907                    and isinstance(node.parent.test.ops[0][1], nodes.Const)
908                    and node.parent.test.ops[0][1].value is None
909                ):
910                    return
911
912            # Check if we have forward references for this node.
913            try:
914                redefinition_index = redefinitions.index(node)
915            except ValueError:
916                pass
917            else:
918                for redefinition in redefinitions[:redefinition_index]:
919                    inferred = utils.safe_infer(redefinition)
920                    if (
921                        inferred
922                        and isinstance(inferred, astroid.Instance)
923                        and inferred.qname() == TYPING_FORWARD_REF_QNAME
924                    ):
925                        return
926
927            dummy_variables_rgx = lint_utils.get_global_option(
928                self, "dummy-variables-rgx", default=None
929            )
930            if dummy_variables_rgx and dummy_variables_rgx.match(node.name):
931                return
932            self.add_message(
933                "function-redefined",
934                node=node,
935                args=(redeftype, defined_self.fromlineno),
936            )
937
938
939class BasicChecker(_BasicChecker):
940    """checks for :
941    * doc strings
942    * number of arguments, local variables, branches, returns and statements in
943    functions, methods
944    * required module attributes
945    * dangerous default values as arguments
946    * redefinition of function / method / class
947    * uses of the global statement
948    """
949
950    __implements__ = interfaces.IAstroidChecker
951
952    name = "basic"
953    msgs = {
954        "W0101": (
955            "Unreachable code",
956            "unreachable",
957            'Used when there is some code behind a "return" or "raise" '
958            "statement, which will never be accessed.",
959        ),
960        "W0102": (
961            "Dangerous default value %s as argument",
962            "dangerous-default-value",
963            "Used when a mutable value as list or dictionary is detected in "
964            "a default value for an argument.",
965        ),
966        "W0104": (
967            "Statement seems to have no effect",
968            "pointless-statement",
969            "Used when a statement doesn't have (or at least seems to) any effect.",
970        ),
971        "W0105": (
972            "String statement has no effect",
973            "pointless-string-statement",
974            "Used when a string is used as a statement (which of course "
975            "has no effect). This is a particular case of W0104 with its "
976            "own message so you can easily disable it if you're using "
977            "those strings as documentation, instead of comments.",
978        ),
979        "W0106": (
980            'Expression "%s" is assigned to nothing',
981            "expression-not-assigned",
982            "Used when an expression that is not a function call is assigned "
983            "to nothing. Probably something else was intended.",
984        ),
985        "W0108": (
986            "Lambda may not be necessary",
987            "unnecessary-lambda",
988            "Used when the body of a lambda expression is a function call "
989            "on the same argument list as the lambda itself; such lambda "
990            "expressions are in all but a few cases replaceable with the "
991            "function being called in the body of the lambda.",
992        ),
993        "W0109": (
994            "Duplicate key %r in dictionary",
995            "duplicate-key",
996            "Used when a dictionary expression binds the same key multiple times.",
997        ),
998        "W0122": (
999            "Use of exec",
1000            "exec-used",
1001            'Used when you use the "exec" statement (function for Python '
1002            "3), to discourage its usage. That doesn't "
1003            "mean you cannot use it !",
1004        ),
1005        "W0123": (
1006            "Use of eval",
1007            "eval-used",
1008            'Used when you use the "eval" function, to discourage its '
1009            "usage. Consider using `ast.literal_eval` for safely evaluating "
1010            "strings containing Python expressions "
1011            "from untrusted sources. ",
1012        ),
1013        "W0150": (
1014            "%s statement in finally block may swallow exception",
1015            "lost-exception",
1016            "Used when a break or a return statement is found inside the "
1017            "finally clause of a try...finally block: the exceptions raised "
1018            "in the try clause will be silently swallowed instead of being "
1019            "re-raised.",
1020        ),
1021        "W0199": (
1022            "Assert called on a 2-item-tuple. Did you mean 'assert x,y'?",
1023            "assert-on-tuple",
1024            "A call of assert on a tuple will always evaluate to true if "
1025            "the tuple is not empty, and will always evaluate to false if "
1026            "it is.",
1027        ),
1028        "W0124": (
1029            'Following "as" with another context manager looks like a tuple.',
1030            "confusing-with-statement",
1031            "Emitted when a `with` statement component returns multiple values "
1032            "and uses name binding with `as` only for a part of those values, "
1033            "as in with ctx() as a, b. This can be misleading, since it's not "
1034            "clear if the context manager returns a tuple or if the node without "
1035            "a name binding is another context manager.",
1036        ),
1037        "W0125": (
1038            "Using a conditional statement with a constant value",
1039            "using-constant-test",
1040            "Emitted when a conditional statement (If or ternary if) "
1041            "uses a constant value for its test. This might not be what "
1042            "the user intended to do.",
1043        ),
1044        "W0126": (
1045            "Using a conditional statement with potentially wrong function or method call due to missing parentheses",
1046            "missing-parentheses-for-call-in-test",
1047            "Emitted when a conditional statement (If or ternary if) "
1048            "seems to wrongly call a function due to missing parentheses",
1049        ),
1050        "W0127": (
1051            "Assigning the same variable %r to itself",
1052            "self-assigning-variable",
1053            "Emitted when we detect that a variable is assigned to itself",
1054        ),
1055        "W0128": (
1056            "Redeclared variable %r in assignment",
1057            "redeclared-assigned-name",
1058            "Emitted when we detect that a variable was redeclared in the same assignment.",
1059        ),
1060        "E0111": (
1061            "The first reversed() argument is not a sequence",
1062            "bad-reversed-sequence",
1063            "Used when the first argument to reversed() builtin "
1064            "isn't a sequence (does not implement __reversed__, "
1065            "nor __getitem__ and __len__",
1066        ),
1067        "E0119": (
1068            "format function is not called on str",
1069            "misplaced-format-function",
1070            "Emitted when format function is not called on str object. "
1071            'e.g doing print("value: {}").format(123) instead of '
1072            'print("value: {}".format(123)). This might not be what the user '
1073            "intended to do.",
1074        ),
1075        "W0129": (
1076            "Assert statement has a string literal as its first argument. The assert will %s fail.",
1077            "assert-on-string-literal",
1078            "Used when an assert statement has a string literal as its first argument, which will "
1079            "cause the assert to always pass.",
1080        ),
1081    }
1082
1083    reports = (("RP0101", "Statistics by type", report_by_type_stats),)
1084
1085    def __init__(self, linter):
1086        super().__init__(linter)
1087        self._tryfinallys = None
1088
1089    def open(self):
1090        """initialize visit variables and statistics"""
1091        py_version = get_global_option(self, "py-version")
1092        self._py38_plus = py_version >= (3, 8)
1093        self._tryfinallys = []
1094        self.linter.stats.reset_node_count()
1095
1096    @utils.check_messages("using-constant-test", "missing-parentheses-for-call-in-test")
1097    def visit_if(self, node: nodes.If) -> None:
1098        self._check_using_constant_test(node, node.test)
1099
1100    @utils.check_messages("using-constant-test", "missing-parentheses-for-call-in-test")
1101    def visit_ifexp(self, node: nodes.IfExp) -> None:
1102        self._check_using_constant_test(node, node.test)
1103
1104    @utils.check_messages("using-constant-test", "missing-parentheses-for-call-in-test")
1105    def visit_comprehension(self, node: nodes.Comprehension) -> None:
1106        if node.ifs:
1107            for if_test in node.ifs:
1108                self._check_using_constant_test(node, if_test)
1109
1110    def _check_using_constant_test(self, node, test):
1111        const_nodes = (
1112            nodes.Module,
1113            nodes.GeneratorExp,
1114            nodes.Lambda,
1115            nodes.FunctionDef,
1116            nodes.ClassDef,
1117            astroid.bases.Generator,
1118            astroid.UnboundMethod,
1119            astroid.BoundMethod,
1120            nodes.Module,
1121        )
1122        structs = (nodes.Dict, nodes.Tuple, nodes.Set, nodes.List)
1123
1124        # These nodes are excepted, since they are not constant
1125        # values, requiring a computation to happen.
1126        except_nodes = (
1127            nodes.Call,
1128            nodes.BinOp,
1129            nodes.BoolOp,
1130            nodes.UnaryOp,
1131            nodes.Subscript,
1132        )
1133        inferred = None
1134        emit = isinstance(test, (nodes.Const,) + structs + const_nodes)
1135        if not isinstance(test, except_nodes):
1136            inferred = utils.safe_infer(test)
1137
1138        if emit:
1139            self.add_message("using-constant-test", node=node)
1140        elif isinstance(inferred, const_nodes):
1141            # If the constant node is a FunctionDef or Lambda then
1142            # it may be an illicit function call due to missing parentheses
1143            call_inferred = None
1144            try:
1145                if isinstance(inferred, nodes.FunctionDef):
1146                    call_inferred = inferred.infer_call_result()
1147                elif isinstance(inferred, nodes.Lambda):
1148                    call_inferred = inferred.infer_call_result(node)
1149            except astroid.InferenceError:
1150                call_inferred = None
1151            if call_inferred:
1152                try:
1153                    for inf_call in call_inferred:
1154                        if inf_call != astroid.Uninferable:
1155                            self.add_message(
1156                                "missing-parentheses-for-call-in-test", node=node
1157                            )
1158                            break
1159                except astroid.InferenceError:
1160                    pass
1161            self.add_message("using-constant-test", node=node)
1162
1163    def visit_module(self, _: nodes.Module) -> None:
1164        """check module name, docstring and required arguments"""
1165        self.linter.stats.node_count["module"] += 1
1166
1167    def visit_classdef(self, _: nodes.ClassDef) -> None:
1168        """check module name, docstring and redefinition
1169        increment branch counter
1170        """
1171        self.linter.stats.node_count["klass"] += 1
1172
1173    @utils.check_messages(
1174        "pointless-statement", "pointless-string-statement", "expression-not-assigned"
1175    )
1176    def visit_expr(self, node: nodes.Expr) -> None:
1177        """Check for various kind of statements without effect"""
1178        expr = node.value
1179        if isinstance(expr, nodes.Const) and isinstance(expr.value, str):
1180            # treat string statement in a separated message
1181            # Handle PEP-257 attribute docstrings.
1182            # An attribute docstring is defined as being a string right after
1183            # an assignment at the module level, class level or __init__ level.
1184            scope = expr.scope()
1185            if isinstance(scope, (nodes.ClassDef, nodes.Module, nodes.FunctionDef)):
1186                if isinstance(scope, nodes.FunctionDef) and scope.name != "__init__":
1187                    pass
1188                else:
1189                    sibling = expr.previous_sibling()
1190                    if (
1191                        sibling is not None
1192                        and sibling.scope() is scope
1193                        and isinstance(sibling, (nodes.Assign, nodes.AnnAssign))
1194                    ):
1195                        return
1196            self.add_message("pointless-string-statement", node=node)
1197            return
1198
1199        # Ignore if this is :
1200        # * a direct function call
1201        # * the unique child of a try/except body
1202        # * a yield statement
1203        # * an ellipsis (which can be used on Python 3 instead of pass)
1204        # warn W0106 if we have any underlying function call (we can't predict
1205        # side effects), else pointless-statement
1206        if (
1207            isinstance(expr, (nodes.Yield, nodes.Await, nodes.Call))
1208            or (isinstance(node.parent, nodes.TryExcept) and node.parent.body == [node])
1209            or (isinstance(expr, nodes.Const) and expr.value is Ellipsis)
1210        ):
1211            return
1212        if any(expr.nodes_of_class(nodes.Call)):
1213            self.add_message(
1214                "expression-not-assigned", node=node, args=expr.as_string()
1215            )
1216        else:
1217            self.add_message("pointless-statement", node=node)
1218
1219    @staticmethod
1220    def _filter_vararg(node, call_args):
1221        # Return the arguments for the given call which are
1222        # not passed as vararg.
1223        for arg in call_args:
1224            if isinstance(arg, nodes.Starred):
1225                if (
1226                    isinstance(arg.value, nodes.Name)
1227                    and arg.value.name != node.args.vararg
1228                ):
1229                    yield arg
1230            else:
1231                yield arg
1232
1233    @staticmethod
1234    def _has_variadic_argument(args, variadic_name):
1235        if not args:
1236            return True
1237        for arg in args:
1238            if isinstance(arg.value, nodes.Name):
1239                if arg.value.name != variadic_name:
1240                    return True
1241            else:
1242                return True
1243        return False
1244
1245    @utils.check_messages("unnecessary-lambda")
1246    def visit_lambda(self, node: nodes.Lambda) -> None:
1247        """check whether or not the lambda is suspicious"""
1248        # if the body of the lambda is a call expression with the same
1249        # argument list as the lambda itself, then the lambda is
1250        # possibly unnecessary and at least suspicious.
1251        if node.args.defaults:
1252            # If the arguments of the lambda include defaults, then a
1253            # judgment cannot be made because there is no way to check
1254            # that the defaults defined by the lambda are the same as
1255            # the defaults defined by the function called in the body
1256            # of the lambda.
1257            return
1258        call = node.body
1259        if not isinstance(call, nodes.Call):
1260            # The body of the lambda must be a function call expression
1261            # for the lambda to be unnecessary.
1262            return
1263        if isinstance(node.body.func, nodes.Attribute) and isinstance(
1264            node.body.func.expr, nodes.Call
1265        ):
1266            # Chained call, the intermediate call might
1267            # return something else (but we don't check that, yet).
1268            return
1269
1270        call_site = astroid.arguments.CallSite.from_call(call)
1271        ordinary_args = list(node.args.args)
1272        new_call_args = list(self._filter_vararg(node, call.args))
1273        if node.args.kwarg:
1274            if self._has_variadic_argument(call.kwargs, node.args.kwarg):
1275                return
1276
1277        if node.args.vararg:
1278            if self._has_variadic_argument(call.starargs, node.args.vararg):
1279                return
1280        elif call.starargs:
1281            return
1282
1283        if call.keywords:
1284            # Look for additional keyword arguments that are not part
1285            # of the lambda's signature
1286            lambda_kwargs = {keyword.name for keyword in node.args.defaults}
1287            if len(lambda_kwargs) != len(call_site.keyword_arguments):
1288                # Different lengths, so probably not identical
1289                return
1290            if set(call_site.keyword_arguments).difference(lambda_kwargs):
1291                return
1292
1293        # The "ordinary" arguments must be in a correspondence such that:
1294        # ordinary_args[i].name == call.args[i].name.
1295        if len(ordinary_args) != len(new_call_args):
1296            return
1297        for arg, passed_arg in zip(ordinary_args, new_call_args):
1298            if not isinstance(passed_arg, nodes.Name):
1299                return
1300            if arg.name != passed_arg.name:
1301                return
1302
1303        self.add_message("unnecessary-lambda", line=node.fromlineno, node=node)
1304
1305    @utils.check_messages("dangerous-default-value")
1306    def visit_functiondef(self, node: nodes.FunctionDef) -> None:
1307        """check function name, docstring, arguments, redefinition,
1308        variable names, max locals
1309        """
1310        if node.is_method():
1311            self.linter.stats.node_count["method"] += 1
1312        else:
1313            self.linter.stats.node_count["function"] += 1
1314        self._check_dangerous_default(node)
1315
1316    visit_asyncfunctiondef = visit_functiondef
1317
1318    def _check_dangerous_default(self, node):
1319        """Check for dangerous default values as arguments."""
1320
1321        def is_iterable(internal_node):
1322            return isinstance(internal_node, (nodes.List, nodes.Set, nodes.Dict))
1323
1324        defaults = node.args.defaults or [] + node.args.kw_defaults or []
1325        for default in defaults:
1326            if not default:
1327                continue
1328            try:
1329                value = next(default.infer())
1330            except astroid.InferenceError:
1331                continue
1332
1333            if (
1334                isinstance(value, astroid.Instance)
1335                and value.qname() in DEFAULT_ARGUMENT_SYMBOLS
1336            ):
1337                if value is default:
1338                    msg = DEFAULT_ARGUMENT_SYMBOLS[value.qname()]
1339                elif isinstance(value, astroid.Instance) or is_iterable(value):
1340                    # We are here in the following situation(s):
1341                    #   * a dict/set/list/tuple call which wasn't inferred
1342                    #     to a syntax node ({}, () etc.). This can happen
1343                    #     when the arguments are invalid or unknown to
1344                    #     the inference.
1345                    #   * a variable from somewhere else, which turns out to be a list
1346                    #     or a dict.
1347                    if is_iterable(default):
1348                        msg = value.pytype()
1349                    elif isinstance(default, nodes.Call):
1350                        msg = f"{value.name}() ({value.qname()})"
1351                    else:
1352                        msg = f"{default.as_string()} ({value.qname()})"
1353                else:
1354                    # this argument is a name
1355                    msg = f"{default.as_string()} ({DEFAULT_ARGUMENT_SYMBOLS[value.qname()]})"
1356                self.add_message("dangerous-default-value", node=node, args=(msg,))
1357
1358    @utils.check_messages("unreachable", "lost-exception")
1359    def visit_return(self, node: nodes.Return) -> None:
1360        """1 - check is the node has a right sibling (if so, that's some
1361        unreachable code)
1362        2 - check is the node is inside the finally clause of a try...finally
1363        block
1364        """
1365        self._check_unreachable(node)
1366        # Is it inside final body of a try...finally block ?
1367        self._check_not_in_finally(node, "return", (nodes.FunctionDef,))
1368
1369    @utils.check_messages("unreachable")
1370    def visit_continue(self, node: nodes.Continue) -> None:
1371        """check is the node has a right sibling (if so, that's some unreachable
1372        code)
1373        """
1374        self._check_unreachable(node)
1375
1376    @utils.check_messages("unreachable", "lost-exception")
1377    def visit_break(self, node: nodes.Break) -> None:
1378        """1 - check is the node has a right sibling (if so, that's some
1379        unreachable code)
1380        2 - check is the node is inside the finally clause of a try...finally
1381        block
1382        """
1383        # 1 - Is it right sibling ?
1384        self._check_unreachable(node)
1385        # 2 - Is it inside final body of a try...finally block ?
1386        self._check_not_in_finally(node, "break", (nodes.For, nodes.While))
1387
1388    @utils.check_messages("unreachable")
1389    def visit_raise(self, node: nodes.Raise) -> None:
1390        """check if the node has a right sibling (if so, that's some unreachable
1391        code)
1392        """
1393        self._check_unreachable(node)
1394
1395    def _check_misplaced_format_function(self, call_node):
1396        if not isinstance(call_node.func, nodes.Attribute):
1397            return
1398        if call_node.func.attrname != "format":
1399            return
1400
1401        expr = utils.safe_infer(call_node.func.expr)
1402        if expr is astroid.Uninferable:
1403            return
1404        if not expr:
1405            # we are doubtful on inferred type of node, so here just check if format
1406            # was called on print()
1407            call_expr = call_node.func.expr
1408            if not isinstance(call_expr, nodes.Call):
1409                return
1410            if (
1411                isinstance(call_expr.func, nodes.Name)
1412                and call_expr.func.name == "print"
1413            ):
1414                self.add_message("misplaced-format-function", node=call_node)
1415
1416    @utils.check_messages(
1417        "eval-used", "exec-used", "bad-reversed-sequence", "misplaced-format-function"
1418    )
1419    def visit_call(self, node: nodes.Call) -> None:
1420        """visit a Call node -> check if this is not a disallowed builtin
1421        call and check for * or ** use
1422        """
1423        self._check_misplaced_format_function(node)
1424        if isinstance(node.func, nodes.Name):
1425            name = node.func.name
1426            # ignore the name if it's not a builtin (i.e. not defined in the
1427            # locals nor globals scope)
1428            if not (name in node.frame() or name in node.root()):
1429                if name == "exec":
1430                    self.add_message("exec-used", node=node)
1431                elif name == "reversed":
1432                    self._check_reversed(node)
1433                elif name == "eval":
1434                    self.add_message("eval-used", node=node)
1435
1436    @utils.check_messages("assert-on-tuple", "assert-on-string-literal")
1437    def visit_assert(self, node: nodes.Assert) -> None:
1438        """check whether assert is used on a tuple or string literal."""
1439        if (
1440            node.fail is None
1441            and isinstance(node.test, nodes.Tuple)
1442            and len(node.test.elts) == 2
1443        ):
1444            self.add_message("assert-on-tuple", node=node)
1445
1446        if isinstance(node.test, nodes.Const) and isinstance(node.test.value, str):
1447            if node.test.value:
1448                when = "never"
1449            else:
1450                when = "always"
1451            self.add_message("assert-on-string-literal", node=node, args=(when,))
1452
1453    @utils.check_messages("duplicate-key")
1454    def visit_dict(self, node: nodes.Dict) -> None:
1455        """check duplicate key in dictionary"""
1456        keys = set()
1457        for k, _ in node.items:
1458            if isinstance(k, nodes.Const):
1459                key = k.value
1460            elif isinstance(k, nodes.Attribute):
1461                key = k.as_string()
1462            else:
1463                continue
1464            if key in keys:
1465                self.add_message("duplicate-key", node=node, args=key)
1466            keys.add(key)
1467
1468    def visit_tryfinally(self, node: nodes.TryFinally) -> None:
1469        """update try...finally flag"""
1470        self._tryfinallys.append(node)
1471
1472    def leave_tryfinally(self, _: nodes.TryFinally) -> None:
1473        """update try...finally flag"""
1474        self._tryfinallys.pop()
1475
1476    def _check_unreachable(self, node):
1477        """check unreachable code"""
1478        unreach_stmt = node.next_sibling()
1479        if unreach_stmt is not None:
1480            if (
1481                isinstance(node, nodes.Return)
1482                and isinstance(unreach_stmt, nodes.Expr)
1483                and isinstance(unreach_stmt.value, nodes.Yield)
1484            ):
1485                # Don't add 'unreachable' for empty generators.
1486                # Only add warning if 'yield' is followed by another node.
1487                unreach_stmt = unreach_stmt.next_sibling()
1488                if unreach_stmt is None:
1489                    return
1490            self.add_message("unreachable", node=unreach_stmt)
1491
1492    def _check_not_in_finally(self, node, node_name, breaker_classes=()):
1493        """check that a node is not inside a finally clause of a
1494        try...finally statement.
1495        If we found before a try...finally block a parent which its type is
1496        in breaker_classes, we skip the whole check."""
1497        # if self._tryfinallys is empty, we're not an in try...finally block
1498        if not self._tryfinallys:
1499            return
1500        # the node could be a grand-grand...-children of the try...finally
1501        _parent = node.parent
1502        _node = node
1503        while _parent and not isinstance(_parent, breaker_classes):
1504            if hasattr(_parent, "finalbody") and _node in _parent.finalbody:
1505                self.add_message("lost-exception", node=node, args=node_name)
1506                return
1507            _node = _parent
1508            _parent = _node.parent
1509
1510    def _check_reversed(self, node):
1511        """check that the argument to `reversed` is a sequence"""
1512        try:
1513            argument = utils.safe_infer(utils.get_argument_from_call(node, position=0))
1514        except utils.NoSuchArgumentError:
1515            pass
1516        else:
1517            if argument is astroid.Uninferable:
1518                return
1519            if argument is None:
1520                # Nothing was inferred.
1521                # Try to see if we have iter().
1522                if isinstance(node.args[0], nodes.Call):
1523                    try:
1524                        func = next(node.args[0].func.infer())
1525                    except astroid.InferenceError:
1526                        return
1527                    if getattr(
1528                        func, "name", None
1529                    ) == "iter" and utils.is_builtin_object(func):
1530                        self.add_message("bad-reversed-sequence", node=node)
1531                return
1532
1533            if isinstance(argument, (nodes.List, nodes.Tuple)):
1534                return
1535
1536            # dicts are reversible, but only from Python 3.8 onwards. Prior to
1537            # that, any class based on dict must explicitly provide a
1538            # __reversed__ method
1539            if not self._py38_plus and isinstance(argument, astroid.Instance):
1540                if any(
1541                    ancestor.name == "dict" and utils.is_builtin_object(ancestor)
1542                    for ancestor in itertools.chain(
1543                        (argument._proxied,), argument._proxied.ancestors()
1544                    )
1545                ):
1546                    try:
1547                        argument.locals[REVERSED_PROTOCOL_METHOD]
1548                    except KeyError:
1549                        self.add_message("bad-reversed-sequence", node=node)
1550                    return
1551
1552            if hasattr(argument, "getattr"):
1553                # everything else is not a proper sequence for reversed()
1554                for methods in REVERSED_METHODS:
1555                    for meth in methods:
1556                        try:
1557                            argument.getattr(meth)
1558                        except astroid.NotFoundError:
1559                            break
1560                    else:
1561                        break
1562                else:
1563                    self.add_message("bad-reversed-sequence", node=node)
1564            else:
1565                self.add_message("bad-reversed-sequence", node=node)
1566
1567    @utils.check_messages("confusing-with-statement")
1568    def visit_with(self, node: nodes.With) -> None:
1569        # a "with" statement with multiple managers corresponds
1570        # to one AST "With" node with multiple items
1571        pairs = node.items
1572        if pairs:
1573            for prev_pair, pair in zip(pairs, pairs[1:]):
1574                if isinstance(prev_pair[1], nodes.AssignName) and (
1575                    pair[1] is None and not isinstance(pair[0], nodes.Call)
1576                ):
1577                    # Don't emit a message if the second is a function call
1578                    # there's no way that can be mistaken for a name assignment.
1579                    # If the line number doesn't match
1580                    # we assume it's a nested "with".
1581                    self.add_message("confusing-with-statement", node=node)
1582
1583    def _check_self_assigning_variable(self, node):
1584        # Detect assigning to the same variable.
1585
1586        scope = node.scope()
1587        scope_locals = scope.locals
1588
1589        rhs_names = []
1590        targets = node.targets
1591        if isinstance(targets[0], nodes.Tuple):
1592            if len(targets) != 1:
1593                # A complex assignment, so bail out early.
1594                return
1595            targets = targets[0].elts
1596            if len(targets) == 1:
1597                # Unpacking a variable into the same name.
1598                return
1599
1600        if isinstance(node.value, nodes.Name):
1601            if len(targets) != 1:
1602                return
1603            rhs_names = [node.value]
1604        elif isinstance(node.value, nodes.Tuple):
1605            rhs_count = len(node.value.elts)
1606            if len(targets) != rhs_count or rhs_count == 1:
1607                return
1608            rhs_names = node.value.elts
1609
1610        for target, lhs_name in zip(targets, rhs_names):
1611            if not isinstance(lhs_name, nodes.Name):
1612                continue
1613            if not isinstance(target, nodes.AssignName):
1614                continue
1615            if isinstance(scope, nodes.ClassDef) and target.name in scope_locals:
1616                # Check that the scope is different than a class level, which is usually
1617                # a pattern to expose module level attributes as class level ones.
1618                continue
1619            if target.name == lhs_name.name:
1620                self.add_message(
1621                    "self-assigning-variable", args=(target.name,), node=target
1622                )
1623
1624    def _check_redeclared_assign_name(self, targets):
1625        dummy_variables_rgx = lint_utils.get_global_option(
1626            self, "dummy-variables-rgx", default=None
1627        )
1628
1629        for target in targets:
1630            if not isinstance(target, nodes.Tuple):
1631                continue
1632
1633            found_names = []
1634            for element in target.elts:
1635                if isinstance(element, nodes.Tuple):
1636                    self._check_redeclared_assign_name([element])
1637                elif isinstance(element, nodes.AssignName) and element.name != "_":
1638                    if dummy_variables_rgx and dummy_variables_rgx.match(element.name):
1639                        return
1640                    found_names.append(element.name)
1641
1642            names = collections.Counter(found_names)
1643            for name, count in names.most_common():
1644                if count > 1:
1645                    self.add_message(
1646                        "redeclared-assigned-name", args=(name,), node=target
1647                    )
1648
1649    @utils.check_messages("self-assigning-variable", "redeclared-assigned-name")
1650    def visit_assign(self, node: nodes.Assign) -> None:
1651        self._check_self_assigning_variable(node)
1652        self._check_redeclared_assign_name(node.targets)
1653
1654    @utils.check_messages("redeclared-assigned-name")
1655    def visit_for(self, node: nodes.For) -> None:
1656        self._check_redeclared_assign_name([node.target])
1657
1658
1659KNOWN_NAME_TYPES = {
1660    "module",
1661    "const",
1662    "class",
1663    "function",
1664    "method",
1665    "attr",
1666    "argument",
1667    "variable",
1668    "class_attribute",
1669    "class_const",
1670    "inlinevar",
1671}
1672
1673HUMAN_READABLE_TYPES = {
1674    "module": "module",
1675    "const": "constant",
1676    "class": "class",
1677    "function": "function",
1678    "method": "method",
1679    "attr": "attribute",
1680    "argument": "argument",
1681    "variable": "variable",
1682    "class_attribute": "class attribute",
1683    "class_const": "class constant",
1684    "inlinevar": "inline iteration",
1685}
1686
1687DEFAULT_NAMING_STYLES = {
1688    "module": "snake_case",
1689    "const": "UPPER_CASE",
1690    "class": "PascalCase",
1691    "function": "snake_case",
1692    "method": "snake_case",
1693    "attr": "snake_case",
1694    "argument": "snake_case",
1695    "variable": "snake_case",
1696    "class_attribute": "any",
1697    "class_const": "UPPER_CASE",
1698    "inlinevar": "any",
1699}
1700
1701
1702def _create_naming_options():
1703    name_options = []
1704    for name_type in sorted(KNOWN_NAME_TYPES):
1705        human_readable_name = HUMAN_READABLE_TYPES[name_type]
1706        default_style = DEFAULT_NAMING_STYLES[name_type]
1707        name_type = name_type.replace("_", "-")
1708        name_options.append(
1709            (
1710                f"{name_type}-naming-style",
1711                {
1712                    "default": default_style,
1713                    "type": "choice",
1714                    "choices": list(NAMING_STYLES.keys()),
1715                    "metavar": "<style>",
1716                    "help": f"Naming style matching correct {human_readable_name} names.",
1717                },
1718            )
1719        )
1720        name_options.append(
1721            (
1722                f"{name_type}-rgx",
1723                {
1724                    "default": None,
1725                    "type": "regexp",
1726                    "metavar": "<regexp>",
1727                    "help": f"Regular expression matching correct {human_readable_name} names. Overrides {name_type}-naming-style.",
1728                },
1729            )
1730        )
1731    return tuple(name_options)
1732
1733
1734class NameChecker(_BasicChecker):
1735    msgs = {
1736        "C0103": (
1737            '%s name "%s" doesn\'t conform to %s',
1738            "invalid-name",
1739            "Used when the name doesn't conform to naming rules "
1740            "associated to its type (constant, variable, class...).",
1741        ),
1742        "C0104": (
1743            'Disallowed name "%s"',
1744            "disallowed-name",
1745            "Used when the name matches bad-names or bad-names-rgxs- (unauthorized names).",
1746            {
1747                "old_names": [
1748                    ("C0102", "blacklisted-name"),
1749                ]
1750            },
1751        ),
1752        "C0144": (
1753            '%s name "%s" contains a non-ASCII unicode character',
1754            "non-ascii-name",
1755            "Used when the name contains at least one non-ASCII unicode character.",
1756        ),
1757        "W0111": (
1758            "Name %s will become a keyword in Python %s",
1759            "assign-to-new-keyword",
1760            "Used when assignment will become invalid in future "
1761            "Python release due to introducing new keyword.",
1762        ),
1763    }
1764
1765    options = (
1766        (
1767            "good-names",
1768            {
1769                "default": ("i", "j", "k", "ex", "Run", "_"),
1770                "type": "csv",
1771                "metavar": "<names>",
1772                "help": "Good variable names which should always be accepted,"
1773                " separated by a comma.",
1774            },
1775        ),
1776        (
1777            "good-names-rgxs",
1778            {
1779                "default": "",
1780                "type": "regexp_csv",
1781                "metavar": "<names>",
1782                "help": "Good variable names regexes, separated by a comma. If names match any regex,"
1783                " they will always be accepted",
1784            },
1785        ),
1786        (
1787            "bad-names",
1788            {
1789                "default": ("foo", "bar", "baz", "toto", "tutu", "tata"),
1790                "type": "csv",
1791                "metavar": "<names>",
1792                "help": "Bad variable names which should always be refused, "
1793                "separated by a comma.",
1794            },
1795        ),
1796        (
1797            "bad-names-rgxs",
1798            {
1799                "default": "",
1800                "type": "regexp_csv",
1801                "metavar": "<names>",
1802                "help": "Bad variable names regexes, separated by a comma. If names match any regex,"
1803                " they will always be refused",
1804            },
1805        ),
1806        (
1807            "name-group",
1808            {
1809                "default": (),
1810                "type": "csv",
1811                "metavar": "<name1:name2>",
1812                "help": (
1813                    "Colon-delimited sets of names that determine each"
1814                    " other's naming style when the name regexes"
1815                    " allow several styles."
1816                ),
1817            },
1818        ),
1819        (
1820            "include-naming-hint",
1821            {
1822                "default": False,
1823                "type": "yn",
1824                "metavar": "<y or n>",
1825                "help": "Include a hint for the correct naming format with invalid-name.",
1826            },
1827        ),
1828        (
1829            "property-classes",
1830            {
1831                "default": ("abc.abstractproperty",),
1832                "type": "csv",
1833                "metavar": "<decorator names>",
1834                "help": "List of decorators that produce properties, such as "
1835                "abc.abstractproperty. Add to this list to register "
1836                "other decorators that produce valid properties. "
1837                "These decorators are taken in consideration only for invalid-name.",
1838            },
1839        ),
1840    ) + _create_naming_options()
1841
1842    KEYWORD_ONSET = {(3, 7): {"async", "await"}}
1843
1844    def __init__(self, linter):
1845        super().__init__(linter)
1846        self._name_category = {}
1847        self._name_group = {}
1848        self._bad_names = {}
1849        self._name_regexps = {}
1850        self._name_hints = {}
1851        self._good_names_rgxs_compiled = []
1852        self._bad_names_rgxs_compiled = []
1853        self._non_ascii_rgx_compiled = re.compile("[^\u0000-\u007F]")
1854
1855    def open(self):
1856        self.linter.stats.reset_bad_names()
1857        for group in self.config.name_group:
1858            for name_type in group.split(":"):
1859                self._name_group[name_type] = f"group_{group}"
1860
1861        regexps, hints = self._create_naming_rules()
1862        self._name_regexps = regexps
1863        self._name_hints = hints
1864        self._good_names_rgxs_compiled = [
1865            re.compile(rgxp) for rgxp in self.config.good_names_rgxs
1866        ]
1867        self._bad_names_rgxs_compiled = [
1868            re.compile(rgxp) for rgxp in self.config.bad_names_rgxs
1869        ]
1870
1871    def _create_naming_rules(self):
1872        regexps = {}
1873        hints = {}
1874
1875        for name_type in KNOWN_NAME_TYPES:
1876            naming_style_option_name = f"{name_type}_naming_style"
1877            naming_style_name = getattr(self.config, naming_style_option_name)
1878
1879            regexps[name_type] = NAMING_STYLES[naming_style_name].get_regex(name_type)
1880
1881            custom_regex_setting_name = f"{name_type}_rgx"
1882            custom_regex = getattr(self.config, custom_regex_setting_name, None)
1883            if custom_regex is not None:
1884                regexps[name_type] = custom_regex
1885
1886            if custom_regex is not None:
1887                hints[name_type] = f"{custom_regex.pattern!r} pattern"
1888            else:
1889                hints[name_type] = f"{naming_style_name} naming style"
1890
1891        return regexps, hints
1892
1893    @utils.check_messages("disallowed-name", "invalid-name", "non-ascii-name")
1894    def visit_module(self, node: nodes.Module) -> None:
1895        self._check_name("module", node.name.split(".")[-1], node)
1896        self._bad_names = {}
1897
1898    def leave_module(self, _: nodes.Module) -> None:
1899        for all_groups in self._bad_names.values():
1900            if len(all_groups) < 2:
1901                continue
1902            groups = collections.defaultdict(list)
1903            min_warnings = sys.maxsize
1904            prevalent_group, _ = max(all_groups.items(), key=lambda item: len(item[1]))
1905            for group in all_groups.values():
1906                groups[len(group)].append(group)
1907                min_warnings = min(len(group), min_warnings)
1908            if len(groups[min_warnings]) > 1:
1909                by_line = sorted(
1910                    groups[min_warnings],
1911                    key=lambda group: min(warning[0].lineno for warning in group),
1912                )
1913                warnings = itertools.chain(*by_line[1:])
1914            else:
1915                warnings = groups[min_warnings][0]
1916            for args in warnings:
1917                self._raise_name_warning(prevalent_group, *args)
1918
1919    @utils.check_messages(
1920        "disallowed-name", "invalid-name", "assign-to-new-keyword", "non-ascii-name"
1921    )
1922    def visit_classdef(self, node: nodes.ClassDef) -> None:
1923        self._check_assign_to_new_keyword_violation(node.name, node)
1924        self._check_name("class", node.name, node)
1925        for attr, anodes in node.instance_attrs.items():
1926            if not any(node.instance_attr_ancestors(attr)):
1927                self._check_name("attr", attr, anodes[0])
1928
1929    @utils.check_messages(
1930        "disallowed-name", "invalid-name", "assign-to-new-keyword", "non-ascii-name"
1931    )
1932    def visit_functiondef(self, node: nodes.FunctionDef) -> None:
1933        # Do not emit any warnings if the method is just an implementation
1934        # of a base class method.
1935        self._check_assign_to_new_keyword_violation(node.name, node)
1936        confidence = interfaces.HIGH
1937        if node.is_method():
1938            if utils.overrides_a_method(node.parent.frame(), node.name):
1939                return
1940            confidence = (
1941                interfaces.INFERENCE
1942                if utils.has_known_bases(node.parent.frame())
1943                else interfaces.INFERENCE_FAILURE
1944            )
1945
1946        self._check_name(
1947            _determine_function_name_type(node, config=self.config),
1948            node.name,
1949            node,
1950            confidence,
1951        )
1952        # Check argument names
1953        args = node.args.args
1954        if args is not None:
1955            self._recursive_check_names(args)
1956
1957    visit_asyncfunctiondef = visit_functiondef
1958
1959    @utils.check_messages("disallowed-name", "invalid-name", "non-ascii-name")
1960    def visit_global(self, node: nodes.Global) -> None:
1961        for name in node.names:
1962            self._check_name("const", name, node)
1963
1964    @utils.check_messages(
1965        "disallowed-name", "invalid-name", "assign-to-new-keyword", "non-ascii-name"
1966    )
1967    def visit_assignname(self, node: nodes.AssignName) -> None:
1968        """check module level assigned names"""
1969        self._check_assign_to_new_keyword_violation(node.name, node)
1970        frame = node.frame()
1971        assign_type = node.assign_type()
1972        if isinstance(assign_type, nodes.Comprehension):
1973            self._check_name("inlinevar", node.name, node)
1974        elif isinstance(frame, nodes.Module):
1975            if isinstance(assign_type, nodes.Assign):
1976                if isinstance(utils.safe_infer(assign_type.value), nodes.ClassDef):
1977                    self._check_name("class", node.name, node)
1978                # Don't emit if the name redefines an import
1979                # in an ImportError except handler.
1980                elif not _redefines_import(node) and isinstance(
1981                    utils.safe_infer(assign_type.value), nodes.Const
1982                ):
1983                    self._check_name("const", node.name, node)
1984            elif isinstance(
1985                assign_type, nodes.AnnAssign
1986            ) and utils.is_assign_name_annotated_with(node, "Final"):
1987                self._check_name("const", node.name, node)
1988        elif isinstance(frame, nodes.FunctionDef):
1989            # global introduced variable aren't in the function locals
1990            if node.name in frame and node.name not in frame.argnames():
1991                if not _redefines_import(node):
1992                    self._check_name("variable", node.name, node)
1993        elif isinstance(frame, nodes.ClassDef):
1994            if not list(frame.local_attr_ancestors(node.name)):
1995                for ancestor in frame.ancestors():
1996                    if (
1997                        ancestor.name == "Enum"
1998                        and ancestor.root().name == "enum"
1999                        or utils.is_assign_name_annotated_with(node, "Final")
2000                    ):
2001                        self._check_name("class_const", node.name, node)
2002                        break
2003                else:
2004                    self._check_name("class_attribute", node.name, node)
2005
2006    def _recursive_check_names(self, args):
2007        """check names in a possibly recursive list <arg>"""
2008        for arg in args:
2009            if isinstance(arg, nodes.AssignName):
2010                self._check_name("argument", arg.name, arg)
2011            else:
2012                self._recursive_check_names(arg.elts)
2013
2014    def _find_name_group(self, node_type):
2015        return self._name_group.get(node_type, node_type)
2016
2017    def _raise_name_warning(
2018        self,
2019        prevalent_group: Optional[str],
2020        node: nodes.NodeNG,
2021        node_type: str,
2022        name: str,
2023        confidence,
2024        warning: str = "invalid-name",
2025    ) -> None:
2026        type_label = HUMAN_READABLE_TYPES[node_type]
2027        hint = self._name_hints[node_type]
2028        if prevalent_group:
2029            # This happens in the multi naming match case. The expected
2030            # prevalent group needs to be spelled out to make the message
2031            # correct.
2032            hint = f"the `{prevalent_group}` group in the {hint}"
2033        if self.config.include_naming_hint:
2034            hint += f" ({self._name_regexps[node_type].pattern!r} pattern)"
2035        args = (
2036            (type_label.capitalize(), name, hint)
2037            if warning == "invalid-name"
2038            else (type_label.capitalize(), name)
2039        )
2040
2041        self.add_message(warning, node=node, args=args, confidence=confidence)
2042        self.linter.stats.increase_bad_name(node_type, 1)
2043
2044    def _name_allowed_by_regex(self, name: str) -> bool:
2045        return name in self.config.good_names or any(
2046            pattern.match(name) for pattern in self._good_names_rgxs_compiled
2047        )
2048
2049    def _name_disallowed_by_regex(self, name: str) -> bool:
2050        return name in self.config.bad_names or any(
2051            pattern.match(name) for pattern in self._bad_names_rgxs_compiled
2052        )
2053
2054    def _check_name(self, node_type, name, node, confidence=interfaces.HIGH):
2055        """check for a name using the type's regexp"""
2056        non_ascii_match = self._non_ascii_rgx_compiled.match(name)
2057        if non_ascii_match is not None:
2058            self._raise_name_warning(
2059                None, node, node_type, name, confidence, warning="non-ascii-name"
2060            )
2061
2062        def _should_exempt_from_invalid_name(node):
2063            if node_type == "variable":
2064                inferred = utils.safe_infer(node)
2065                if isinstance(inferred, nodes.ClassDef):
2066                    return True
2067            return False
2068
2069        if self._name_allowed_by_regex(name=name):
2070            return
2071        if self._name_disallowed_by_regex(name=name):
2072            self.linter.stats.increase_bad_name(node_type, 1)
2073            self.add_message("disallowed-name", node=node, args=name)
2074            return
2075        regexp = self._name_regexps[node_type]
2076        match = regexp.match(name)
2077
2078        if _is_multi_naming_match(match, node_type, confidence):
2079            name_group = self._find_name_group(node_type)
2080            bad_name_group = self._bad_names.setdefault(name_group, {})
2081            warnings = bad_name_group.setdefault(match.lastgroup, [])
2082            warnings.append((node, node_type, name, confidence))
2083
2084        if match is None and not _should_exempt_from_invalid_name(node):
2085            self._raise_name_warning(None, node, node_type, name, confidence)
2086
2087    def _check_assign_to_new_keyword_violation(self, name, node):
2088        keyword_first_version = self._name_became_keyword_in_version(
2089            name, self.KEYWORD_ONSET
2090        )
2091        if keyword_first_version is not None:
2092            self.add_message(
2093                "assign-to-new-keyword",
2094                node=node,
2095                args=(name, keyword_first_version),
2096                confidence=interfaces.HIGH,
2097            )
2098
2099    @staticmethod
2100    def _name_became_keyword_in_version(name, rules):
2101        for version, keywords in rules.items():
2102            if name in keywords and sys.version_info < version:
2103                return ".".join(str(v) for v in version)
2104        return None
2105
2106
2107class DocStringChecker(_BasicChecker):
2108    msgs = {
2109        "C0112": (
2110            "Empty %s docstring",
2111            "empty-docstring",
2112            "Used when a module, function, class or method has an empty "
2113            "docstring (it would be too easy ;).",
2114            {"old_names": [("W0132", "old-empty-docstring")]},
2115        ),
2116        "C0114": (
2117            "Missing module docstring",
2118            "missing-module-docstring",
2119            "Used when a module has no docstring."
2120            "Empty modules do not require a docstring.",
2121            {"old_names": [("C0111", "missing-docstring")]},
2122        ),
2123        "C0115": (
2124            "Missing class docstring",
2125            "missing-class-docstring",
2126            "Used when a class has no docstring."
2127            "Even an empty class must have a docstring.",
2128            {"old_names": [("C0111", "missing-docstring")]},
2129        ),
2130        "C0116": (
2131            "Missing function or method docstring",
2132            "missing-function-docstring",
2133            "Used when a function or method has no docstring."
2134            "Some special methods like __init__ do not require a "
2135            "docstring.",
2136            {"old_names": [("C0111", "missing-docstring")]},
2137        ),
2138    }
2139    options = (
2140        (
2141            "no-docstring-rgx",
2142            {
2143                "default": NO_REQUIRED_DOC_RGX,
2144                "type": "regexp",
2145                "metavar": "<regexp>",
2146                "help": "Regular expression which should only match "
2147                "function or class names that do not require a "
2148                "docstring.",
2149            },
2150        ),
2151        (
2152            "docstring-min-length",
2153            {
2154                "default": -1,
2155                "type": "int",
2156                "metavar": "<int>",
2157                "help": (
2158                    "Minimum line length for functions/classes that"
2159                    " require docstrings, shorter ones are exempt."
2160                ),
2161            },
2162        ),
2163    )
2164
2165    def open(self):
2166        self.linter.stats.reset_undocumented()
2167
2168    @utils.check_messages("missing-docstring", "empty-docstring")
2169    def visit_module(self, node: nodes.Module) -> None:
2170        self._check_docstring("module", node)
2171
2172    @utils.check_messages("missing-docstring", "empty-docstring")
2173    def visit_classdef(self, node: nodes.ClassDef) -> None:
2174        if self.config.no_docstring_rgx.match(node.name) is None:
2175            self._check_docstring("class", node)
2176
2177    @utils.check_messages("missing-docstring", "empty-docstring")
2178    def visit_functiondef(self, node: nodes.FunctionDef) -> None:
2179        if self.config.no_docstring_rgx.match(node.name) is None:
2180            ftype = "method" if node.is_method() else "function"
2181            if (
2182                is_property_setter(node)
2183                or is_property_deleter(node)
2184                or is_overload_stub(node)
2185            ):
2186                return
2187
2188            if isinstance(node.parent.frame(), nodes.ClassDef):
2189                overridden = False
2190                confidence = (
2191                    interfaces.INFERENCE
2192                    if utils.has_known_bases(node.parent.frame())
2193                    else interfaces.INFERENCE_FAILURE
2194                )
2195                # check if node is from a method overridden by its ancestor
2196                for ancestor in node.parent.frame().ancestors():
2197                    if ancestor.qname() == "builtins.object":
2198                        continue
2199                    if node.name in ancestor and isinstance(
2200                        ancestor[node.name], nodes.FunctionDef
2201                    ):
2202                        overridden = True
2203                        break
2204                self._check_docstring(
2205                    ftype, node, report_missing=not overridden, confidence=confidence  # type: ignore[arg-type]
2206                )
2207            elif isinstance(node.parent.frame(), nodes.Module):
2208                self._check_docstring(ftype, node)  # type: ignore[arg-type]
2209            else:
2210                return
2211
2212    visit_asyncfunctiondef = visit_functiondef
2213
2214    def _check_docstring(
2215        self,
2216        node_type: Literal["class", "function", "method", "module"],
2217        node,
2218        report_missing=True,
2219        confidence=interfaces.HIGH,
2220    ):
2221        """check the node has a non empty docstring"""
2222        docstring = node.doc
2223        if docstring is None:
2224            docstring = _infer_dunder_doc_attribute(node)
2225
2226        if docstring is None:
2227            if not report_missing:
2228                return
2229            lines = utils.get_node_last_lineno(node) - node.lineno
2230
2231            if node_type == "module" and not lines:
2232                # If the module has no body, there's no reason
2233                # to require a docstring.
2234                return
2235            max_lines = self.config.docstring_min_length
2236
2237            if node_type != "module" and max_lines > -1 and lines < max_lines:
2238                return
2239            if node_type == "class":
2240                self.linter.stats.undocumented["klass"] += 1
2241            else:
2242                self.linter.stats.undocumented[node_type] += 1
2243            if (
2244                node.body
2245                and isinstance(node.body[0], nodes.Expr)
2246                and isinstance(node.body[0].value, nodes.Call)
2247            ):
2248                # Most likely a string with a format call. Let's see.
2249                func = utils.safe_infer(node.body[0].value.func)
2250                if isinstance(func, astroid.BoundMethod) and isinstance(
2251                    func.bound, astroid.Instance
2252                ):
2253                    # Strings.
2254                    if func.bound.name in {"str", "unicode", "bytes"}:
2255                        return
2256            if node_type == "module":
2257                message = "missing-module-docstring"
2258            elif node_type == "class":
2259                message = "missing-class-docstring"
2260            else:
2261                message = "missing-function-docstring"
2262            self.add_message(message, node=node, confidence=confidence)
2263        elif not docstring.strip():
2264            if node_type == "class":
2265                self.linter.stats.undocumented["klass"] += 1
2266            else:
2267                self.linter.stats.undocumented[node_type] += 1
2268            self.add_message(
2269                "empty-docstring", node=node, args=(node_type,), confidence=confidence
2270            )
2271
2272
2273class PassChecker(_BasicChecker):
2274    """check if the pass statement is really necessary"""
2275
2276    msgs = {
2277        "W0107": (
2278            "Unnecessary pass statement",
2279            "unnecessary-pass",
2280            'Used when a "pass" statement that can be avoided is encountered.',
2281        )
2282    }
2283
2284    @utils.check_messages("unnecessary-pass")
2285    def visit_pass(self, node: nodes.Pass) -> None:
2286        if len(node.parent.child_sequence(node)) > 1 or (
2287            isinstance(node.parent, (nodes.ClassDef, nodes.FunctionDef))
2288            and (node.parent.doc is not None)
2289        ):
2290            self.add_message("unnecessary-pass", node=node)
2291
2292
2293def _is_one_arg_pos_call(call):
2294    """Is this a call with exactly 1 argument,
2295    where that argument is positional?
2296    """
2297    return isinstance(call, nodes.Call) and len(call.args) == 1 and not call.keywords
2298
2299
2300def _infer_dunder_doc_attribute(node):
2301    # Try to see if we have a `__doc__` attribute.
2302    try:
2303        docstring = node["__doc__"]
2304    except KeyError:
2305        return None
2306
2307    docstring = utils.safe_infer(docstring)
2308    if not docstring:
2309        return None
2310    if not isinstance(docstring, nodes.Const):
2311        return None
2312    return docstring.value
2313
2314
2315class ComparisonChecker(_BasicChecker):
2316    """Checks for comparisons
2317
2318    - singleton comparison: 'expr == True', 'expr == False' and 'expr == None'
2319    - yoda condition: 'const "comp" right' where comp can be '==', '!=', '<',
2320      '<=', '>' or '>=', and right can be a variable, an attribute, a method or
2321      a function
2322    """
2323
2324    msgs = {
2325        "C0121": (
2326            "Comparison %s should be %s",
2327            "singleton-comparison",
2328            "Used when an expression is compared to singleton "
2329            "values like True, False or None.",
2330        ),
2331        "C0123": (
2332            "Use isinstance() rather than type() for a typecheck.",
2333            "unidiomatic-typecheck",
2334            "The idiomatic way to perform an explicit typecheck in "
2335            "Python is to use isinstance(x, Y) rather than "
2336            "type(x) == Y, type(x) is Y. Though there are unusual "
2337            "situations where these give different results.",
2338            {"old_names": [("W0154", "old-unidiomatic-typecheck")]},
2339        ),
2340        "R0123": (
2341            "Comparison to literal",
2342            "literal-comparison",
2343            "Used when comparing an object to a literal, which is usually "
2344            "what you do not want to do, since you can compare to a different "
2345            "literal than what was expected altogether.",
2346        ),
2347        "R0124": (
2348            "Redundant comparison - %s",
2349            "comparison-with-itself",
2350            "Used when something is compared against itself.",
2351        ),
2352        "W0143": (
2353            "Comparing against a callable, did you omit the parenthesis?",
2354            "comparison-with-callable",
2355            "This message is emitted when pylint detects that a comparison with a "
2356            "callable was made, which might suggest that some parenthesis were omitted, "
2357            "resulting in potential unwanted behaviour.",
2358        ),
2359        "W0177": (
2360            "Comparison %s should be %s",
2361            "nan-comparison",
2362            "Used when an expression is compared to NaN"
2363            "values like numpy.NaN and float('nan')",
2364        ),
2365    }
2366
2367    def _check_singleton_comparison(
2368        self, left_value, right_value, root_node, checking_for_absence: bool = False
2369    ):
2370        """Check if == or != is being used to compare a singleton value"""
2371        singleton_values = (True, False, None)
2372
2373        def _is_singleton_const(node) -> bool:
2374            return isinstance(node, nodes.Const) and any(
2375                node.value is value for value in singleton_values
2376            )
2377
2378        if _is_singleton_const(left_value):
2379            singleton, other_value = left_value.value, right_value
2380        elif _is_singleton_const(right_value):
2381            singleton, other_value = right_value.value, left_value
2382        else:
2383            return
2384
2385        singleton_comparison_example = {False: "'{} is {}'", True: "'{} is not {}'"}
2386
2387        # True/False singletons have a special-cased message in case the user is
2388        # mistakenly using == or != to check for truthiness
2389        if singleton in {True, False}:
2390            suggestion_template = (
2391                "{} if checking for the singleton value {}, or {} if testing for {}"
2392            )
2393            truthiness_example = {False: "not {}", True: "{}"}
2394            truthiness_phrase = {True: "truthiness", False: "falsiness"}
2395
2396            # Looks for comparisons like x == True or x != False
2397            checking_truthiness = singleton is not checking_for_absence
2398
2399            suggestion = suggestion_template.format(
2400                singleton_comparison_example[checking_for_absence].format(
2401                    left_value.as_string(), right_value.as_string()
2402                ),
2403                singleton,
2404                (
2405                    "'bool({})'"
2406                    if not utils.is_test_condition(root_node) and checking_truthiness
2407                    else "'{}'"
2408                ).format(
2409                    truthiness_example[checking_truthiness].format(
2410                        other_value.as_string()
2411                    )
2412                ),
2413                truthiness_phrase[checking_truthiness],
2414            )
2415        else:
2416            suggestion = singleton_comparison_example[checking_for_absence].format(
2417                left_value.as_string(), right_value.as_string()
2418            )
2419        self.add_message(
2420            "singleton-comparison",
2421            node=root_node,
2422            args=(f"'{root_node.as_string()}'", suggestion),
2423        )
2424
2425    def _check_nan_comparison(
2426        self, left_value, right_value, root_node, checking_for_absence: bool = False
2427    ):
2428        def _is_float_nan(node):
2429            try:
2430                if isinstance(node, nodes.Call) and len(node.args) == 1:
2431                    if (
2432                        node.args[0].value.lower() == "nan"
2433                        and node.inferred()[0].pytype() == "builtins.float"
2434                    ):
2435                        return True
2436                return False
2437            except AttributeError:
2438                return False
2439
2440        def _is_numpy_nan(node):
2441            if isinstance(node, nodes.Attribute) and node.attrname == "NaN":
2442                if isinstance(node.expr, nodes.Name):
2443                    return node.expr.name in {"numpy", "nmp", "np"}
2444            return False
2445
2446        def _is_nan(node) -> bool:
2447            return _is_float_nan(node) or _is_numpy_nan(node)
2448
2449        nan_left = _is_nan(left_value)
2450        if not nan_left and not _is_nan(right_value):
2451            return
2452
2453        absence_text = ""
2454        if checking_for_absence:
2455            absence_text = "not "
2456        if nan_left:
2457            suggestion = f"'{absence_text}math.isnan({right_value.as_string()})'"
2458        else:
2459            suggestion = f"'{absence_text}math.isnan({left_value.as_string()})'"
2460        self.add_message(
2461            "nan-comparison",
2462            node=root_node,
2463            args=(f"'{root_node.as_string()}'", suggestion),
2464        )
2465
2466    def _check_literal_comparison(self, literal, node: nodes.Compare):
2467        """Check if we compare to a literal, which is usually what we do not want to do."""
2468        is_other_literal = isinstance(literal, (nodes.List, nodes.Dict, nodes.Set))
2469        is_const = False
2470        if isinstance(literal, nodes.Const):
2471            if isinstance(literal.value, bool) or literal.value is None:
2472                # Not interested in this values.
2473                return
2474            is_const = isinstance(literal.value, (bytes, str, int, float))
2475
2476        if is_const or is_other_literal:
2477            self.add_message("literal-comparison", node=node)
2478
2479    def _check_logical_tautology(self, node: nodes.Compare):
2480        """Check if identifier is compared against itself.
2481        :param node: Compare node
2482        :Example:
2483        val = 786
2484        if val == val:  # [comparison-with-itself]
2485            pass
2486        """
2487        left_operand = node.left
2488        right_operand = node.ops[0][1]
2489        operator = node.ops[0][0]
2490        if isinstance(left_operand, nodes.Const) and isinstance(
2491            right_operand, nodes.Const
2492        ):
2493            left_operand = left_operand.value
2494            right_operand = right_operand.value
2495        elif isinstance(left_operand, nodes.Name) and isinstance(
2496            right_operand, nodes.Name
2497        ):
2498            left_operand = left_operand.name
2499            right_operand = right_operand.name
2500
2501        if left_operand == right_operand:
2502            suggestion = f"{left_operand} {operator} {right_operand}"
2503            self.add_message("comparison-with-itself", node=node, args=(suggestion,))
2504
2505    def _check_callable_comparison(self, node):
2506        operator = node.ops[0][0]
2507        if operator not in COMPARISON_OPERATORS:
2508            return
2509
2510        bare_callables = (nodes.FunctionDef, astroid.BoundMethod)
2511        left_operand, right_operand = node.left, node.ops[0][1]
2512        # this message should be emitted only when there is comparison of bare callable
2513        # with non bare callable.
2514        if (
2515            sum(
2516                1
2517                for operand in (left_operand, right_operand)
2518                if isinstance(utils.safe_infer(operand), bare_callables)
2519            )
2520            == 1
2521        ):
2522            self.add_message("comparison-with-callable", node=node)
2523
2524    @utils.check_messages(
2525        "singleton-comparison",
2526        "unidiomatic-typecheck",
2527        "literal-comparison",
2528        "comparison-with-itself",
2529        "comparison-with-callable",
2530    )
2531    def visit_compare(self, node: nodes.Compare) -> None:
2532        self._check_callable_comparison(node)
2533        self._check_logical_tautology(node)
2534        self._check_unidiomatic_typecheck(node)
2535        # NOTE: this checker only works with binary comparisons like 'x == 42'
2536        # but not 'x == y == 42'
2537        if len(node.ops) != 1:
2538            return
2539
2540        left = node.left
2541        operator, right = node.ops[0]
2542
2543        if operator in {"==", "!="}:
2544            self._check_singleton_comparison(
2545                left, right, node, checking_for_absence=operator == "!="
2546            )
2547
2548        if operator in {"==", "!=", "is", "is not"}:
2549            self._check_nan_comparison(
2550                left, right, node, checking_for_absence=operator in {"!=", "is not"}
2551            )
2552        if operator in {"is", "is not"}:
2553            self._check_literal_comparison(right, node)
2554
2555    def _check_unidiomatic_typecheck(self, node):
2556        operator, right = node.ops[0]
2557        if operator in TYPECHECK_COMPARISON_OPERATORS:
2558            left = node.left
2559            if _is_one_arg_pos_call(left):
2560                self._check_type_x_is_y(node, left, operator, right)
2561
2562    def _check_type_x_is_y(self, node, left, operator, right):
2563        """Check for expressions like type(x) == Y."""
2564        left_func = utils.safe_infer(left.func)
2565        if not (
2566            isinstance(left_func, nodes.ClassDef) and left_func.qname() == TYPE_QNAME
2567        ):
2568            return
2569
2570        if operator in {"is", "is not"} and _is_one_arg_pos_call(right):
2571            right_func = utils.safe_infer(right.func)
2572            if (
2573                isinstance(right_func, nodes.ClassDef)
2574                and right_func.qname() == TYPE_QNAME
2575            ):
2576                # type(x) == type(a)
2577                right_arg = utils.safe_infer(right.args[0])
2578                if not isinstance(right_arg, LITERAL_NODE_TYPES):
2579                    # not e.g. type(x) == type([])
2580                    return
2581        self.add_message("unidiomatic-typecheck", node=node)
2582
2583
2584def register(linter):
2585    """required method to auto register this checker"""
2586    linter.register_checker(BasicErrorChecker(linter))
2587    linter.register_checker(BasicChecker(linter))
2588    linter.register_checker(NameChecker(linter))
2589    linter.register_checker(DocStringChecker(linter))
2590    linter.register_checker(PassChecker(linter))
2591    linter.register_checker(ComparisonChecker(linter))
2592