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