basic_checker.py 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974
  1. # Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  2. # For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
  3. # Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
  4. """Basic checker for Python code."""
  5. from __future__ import annotations
  6. import collections
  7. import itertools
  8. import sys
  9. from collections.abc import Iterator
  10. from typing import TYPE_CHECKING, cast
  11. import astroid
  12. from astroid import nodes, objects, util
  13. from pylint import utils as lint_utils
  14. from pylint.checkers import BaseChecker, utils
  15. from pylint.interfaces import HIGH, INFERENCE, Confidence
  16. from pylint.reporters.ureports import nodes as reporter_nodes
  17. from pylint.utils import LinterStats
  18. if TYPE_CHECKING:
  19. from pylint.lint.pylinter import PyLinter
  20. if sys.version_info >= (3, 8):
  21. from typing import Literal
  22. else:
  23. from typing_extensions import Literal
  24. class _BasicChecker(BaseChecker):
  25. """Permits separating multiple checks with the same checker name into
  26. classes/file.
  27. """
  28. name = "basic"
  29. REVERSED_PROTOCOL_METHOD = "__reversed__"
  30. SEQUENCE_PROTOCOL_METHODS = ("__getitem__", "__len__")
  31. REVERSED_METHODS = (SEQUENCE_PROTOCOL_METHODS, (REVERSED_PROTOCOL_METHOD,))
  32. # A mapping from qname -> symbol, to be used when generating messages
  33. # about dangerous default values as arguments
  34. DEFAULT_ARGUMENT_SYMBOLS = dict(
  35. zip(
  36. [".".join(["builtins", x]) for x in ("set", "dict", "list")],
  37. ["set()", "{}", "[]"],
  38. ),
  39. **{
  40. x: f"{x}()"
  41. for x in (
  42. "collections.deque",
  43. "collections.ChainMap",
  44. "collections.Counter",
  45. "collections.OrderedDict",
  46. "collections.defaultdict",
  47. "collections.UserDict",
  48. "collections.UserList",
  49. )
  50. },
  51. )
  52. def report_by_type_stats(
  53. sect: reporter_nodes.Section,
  54. stats: LinterStats,
  55. old_stats: LinterStats | None,
  56. ) -> None:
  57. """Make a report of.
  58. * percentage of different types documented
  59. * percentage of different types with a bad name
  60. """
  61. # percentage of different types documented and/or with a bad name
  62. nice_stats: dict[str, dict[str, str]] = {}
  63. for node_type in ("module", "class", "method", "function"):
  64. node_type = cast(Literal["function", "class", "method", "module"], node_type)
  65. total = stats.get_node_count(node_type)
  66. nice_stats[node_type] = {}
  67. if total != 0:
  68. undocumented_node = stats.get_undocumented(node_type)
  69. documented = total - undocumented_node
  70. percent = (documented * 100.0) / total
  71. nice_stats[node_type]["percent_documented"] = f"{percent:.2f}"
  72. badname_node = stats.get_bad_names(node_type)
  73. percent = (badname_node * 100.0) / total
  74. nice_stats[node_type]["percent_badname"] = f"{percent:.2f}"
  75. lines = ["type", "number", "old number", "difference", "%documented", "%badname"]
  76. for node_type in ("module", "class", "method", "function"):
  77. node_type = cast(Literal["function", "class", "method", "module"], node_type)
  78. new = stats.get_node_count(node_type)
  79. old = old_stats.get_node_count(node_type) if old_stats else None
  80. diff_str = lint_utils.diff_string(old, new) if old else None
  81. lines += [
  82. node_type,
  83. str(new),
  84. str(old) if old else "NC",
  85. diff_str if diff_str else "NC",
  86. nice_stats[node_type].get("percent_documented", "0"),
  87. nice_stats[node_type].get("percent_badname", "0"),
  88. ]
  89. sect.append(reporter_nodes.Table(children=lines, cols=6, rheaders=1))
  90. # pylint: disable-next = too-many-public-methods
  91. class BasicChecker(_BasicChecker):
  92. """Basic checker.
  93. Checks for :
  94. * doc strings
  95. * number of arguments, local variables, branches, returns and statements in
  96. functions, methods
  97. * required module attributes
  98. * dangerous default values as arguments
  99. * redefinition of function / method / class
  100. * uses of the global statement
  101. """
  102. name = "basic"
  103. msgs = {
  104. "W0101": (
  105. "Unreachable code",
  106. "unreachable",
  107. 'Used when there is some code behind a "return" or "raise" '
  108. "statement, which will never be accessed.",
  109. ),
  110. "W0102": (
  111. "Dangerous default value %s as argument",
  112. "dangerous-default-value",
  113. "Used when a mutable value as list or dictionary is detected in "
  114. "a default value for an argument.",
  115. ),
  116. "W0104": (
  117. "Statement seems to have no effect",
  118. "pointless-statement",
  119. "Used when a statement doesn't have (or at least seems to) any effect.",
  120. ),
  121. "W0105": (
  122. "String statement has no effect",
  123. "pointless-string-statement",
  124. "Used when a string is used as a statement (which of course "
  125. "has no effect). This is a particular case of W0104 with its "
  126. "own message so you can easily disable it if you're using "
  127. "those strings as documentation, instead of comments.",
  128. ),
  129. "W0106": (
  130. 'Expression "%s" is assigned to nothing',
  131. "expression-not-assigned",
  132. "Used when an expression that is not a function call is assigned "
  133. "to nothing. Probably something else was intended.",
  134. ),
  135. "W0108": (
  136. "Lambda may not be necessary",
  137. "unnecessary-lambda",
  138. "Used when the body of a lambda expression is a function call "
  139. "on the same argument list as the lambda itself; such lambda "
  140. "expressions are in all but a few cases replaceable with the "
  141. "function being called in the body of the lambda.",
  142. ),
  143. "W0109": (
  144. "Duplicate key %r in dictionary",
  145. "duplicate-key",
  146. "Used when a dictionary expression binds the same key multiple times.",
  147. ),
  148. "W0122": (
  149. "Use of exec",
  150. "exec-used",
  151. "Raised when the 'exec' statement is used. It's dangerous to use this "
  152. "function for a user input, and it's also slower than actual code in "
  153. "general. This doesn't mean you should never use it, but you should "
  154. "consider alternatives first and restrict the functions available.",
  155. ),
  156. "W0123": (
  157. "Use of eval",
  158. "eval-used",
  159. 'Used when you use the "eval" function, to discourage its '
  160. "usage. Consider using `ast.literal_eval` for safely evaluating "
  161. "strings containing Python expressions "
  162. "from untrusted sources.",
  163. ),
  164. "W0150": (
  165. "%s statement in finally block may swallow exception",
  166. "lost-exception",
  167. "Used when a break or a return statement is found inside the "
  168. "finally clause of a try...finally block: the exceptions raised "
  169. "in the try clause will be silently swallowed instead of being "
  170. "re-raised.",
  171. ),
  172. "W0199": (
  173. "Assert called on a populated tuple. Did you mean 'assert x,y'?",
  174. "assert-on-tuple",
  175. "A call of assert on a tuple will always evaluate to true if "
  176. "the tuple is not empty, and will always evaluate to false if "
  177. "it is.",
  178. ),
  179. "W0124": (
  180. 'Following "as" with another context manager looks like a tuple.',
  181. "confusing-with-statement",
  182. "Emitted when a `with` statement component returns multiple values "
  183. "and uses name binding with `as` only for a part of those values, "
  184. "as in with ctx() as a, b. This can be misleading, since it's not "
  185. "clear if the context manager returns a tuple or if the node without "
  186. "a name binding is another context manager.",
  187. ),
  188. "W0125": (
  189. "Using a conditional statement with a constant value",
  190. "using-constant-test",
  191. "Emitted when a conditional statement (If or ternary if) "
  192. "uses a constant value for its test. This might not be what "
  193. "the user intended to do.",
  194. ),
  195. "W0126": (
  196. "Using a conditional statement with potentially wrong function or method call due to "
  197. "missing parentheses",
  198. "missing-parentheses-for-call-in-test",
  199. "Emitted when a conditional statement (If or ternary if) "
  200. "seems to wrongly call a function due to missing parentheses",
  201. ),
  202. "W0127": (
  203. "Assigning the same variable %r to itself",
  204. "self-assigning-variable",
  205. "Emitted when we detect that a variable is assigned to itself",
  206. ),
  207. "W0128": (
  208. "Redeclared variable %r in assignment",
  209. "redeclared-assigned-name",
  210. "Emitted when we detect that a variable was redeclared in the same assignment.",
  211. ),
  212. "E0111": (
  213. "The first reversed() argument is not a sequence",
  214. "bad-reversed-sequence",
  215. "Used when the first argument to reversed() builtin "
  216. "isn't a sequence (does not implement __reversed__, "
  217. "nor __getitem__ and __len__",
  218. ),
  219. "E0119": (
  220. "format function is not called on str",
  221. "misplaced-format-function",
  222. "Emitted when format function is not called on str object. "
  223. 'e.g doing print("value: {}").format(123) instead of '
  224. 'print("value: {}".format(123)). This might not be what the user '
  225. "intended to do.",
  226. ),
  227. "W0129": (
  228. "Assert statement has a string literal as its first argument. The assert will %s fail.",
  229. "assert-on-string-literal",
  230. "Used when an assert statement has a string literal as its first argument, which will "
  231. "cause the assert to always pass.",
  232. ),
  233. "W0130": (
  234. "Duplicate value %r in set",
  235. "duplicate-value",
  236. "This message is emitted when a set contains the same value two or more times.",
  237. ),
  238. "W0131": (
  239. "Named expression used without context",
  240. "named-expr-without-context",
  241. "Emitted if named expression is used to do a regular assignment "
  242. "outside a context like if, for, while, or a comprehension.",
  243. ),
  244. "W0133": (
  245. "Exception statement has no effect",
  246. "pointless-exception-statement",
  247. "Used when an exception is created without being assigned, raised or returned "
  248. "for subsequent use elsewhere.",
  249. ),
  250. }
  251. reports = (("RP0101", "Statistics by type", report_by_type_stats),)
  252. def __init__(self, linter: PyLinter) -> None:
  253. super().__init__(linter)
  254. self._tryfinallys: list[nodes.TryFinally] | None = None
  255. def open(self) -> None:
  256. """Initialize visit variables and statistics."""
  257. py_version = self.linter.config.py_version
  258. self._py38_plus = py_version >= (3, 8)
  259. self._tryfinallys = []
  260. self.linter.stats.reset_node_count()
  261. @utils.only_required_for_messages(
  262. "using-constant-test", "missing-parentheses-for-call-in-test"
  263. )
  264. def visit_if(self, node: nodes.If) -> None:
  265. self._check_using_constant_test(node, node.test)
  266. @utils.only_required_for_messages(
  267. "using-constant-test", "missing-parentheses-for-call-in-test"
  268. )
  269. def visit_ifexp(self, node: nodes.IfExp) -> None:
  270. self._check_using_constant_test(node, node.test)
  271. @utils.only_required_for_messages(
  272. "using-constant-test", "missing-parentheses-for-call-in-test"
  273. )
  274. def visit_comprehension(self, node: nodes.Comprehension) -> None:
  275. if node.ifs:
  276. for if_test in node.ifs:
  277. self._check_using_constant_test(node, if_test)
  278. def _check_using_constant_test(
  279. self,
  280. node: nodes.If | nodes.IfExp | nodes.Comprehension,
  281. test: nodes.NodeNG | None,
  282. ) -> None:
  283. const_nodes = (
  284. nodes.Module,
  285. nodes.GeneratorExp,
  286. nodes.Lambda,
  287. nodes.FunctionDef,
  288. nodes.ClassDef,
  289. astroid.bases.Generator,
  290. astroid.UnboundMethod,
  291. astroid.BoundMethod,
  292. nodes.Module,
  293. )
  294. structs = (nodes.Dict, nodes.Tuple, nodes.Set, nodes.List)
  295. # These nodes are excepted, since they are not constant
  296. # values, requiring a computation to happen.
  297. except_nodes = (
  298. nodes.Call,
  299. nodes.BinOp,
  300. nodes.BoolOp,
  301. nodes.UnaryOp,
  302. nodes.Subscript,
  303. )
  304. inferred = None
  305. emit = isinstance(test, (nodes.Const,) + structs + const_nodes)
  306. maybe_generator_call = None
  307. if not isinstance(test, except_nodes):
  308. inferred = utils.safe_infer(test)
  309. if isinstance(inferred, util.UninferableBase) and isinstance(
  310. test, nodes.Name
  311. ):
  312. emit, maybe_generator_call = BasicChecker._name_holds_generator(test)
  313. # Emit if calling a function that only returns GeneratorExp (always tests True)
  314. elif isinstance(test, nodes.Call):
  315. maybe_generator_call = test
  316. if maybe_generator_call:
  317. inferred_call = utils.safe_infer(maybe_generator_call.func)
  318. if isinstance(inferred_call, nodes.FunctionDef):
  319. # Can't use all(x) or not any(not x) for this condition, because it
  320. # will return True for empty generators, which is not what we want.
  321. all_returns_were_generator = None
  322. for return_node in inferred_call._get_return_nodes_skip_functions():
  323. if not isinstance(return_node.value, nodes.GeneratorExp):
  324. all_returns_were_generator = False
  325. break
  326. all_returns_were_generator = True
  327. if all_returns_were_generator:
  328. self.add_message(
  329. "using-constant-test", node=node, confidence=INFERENCE
  330. )
  331. return
  332. if emit:
  333. self.add_message("using-constant-test", node=test, confidence=INFERENCE)
  334. elif isinstance(inferred, const_nodes):
  335. # If the constant node is a FunctionDef or Lambda then
  336. # it may be an illicit function call due to missing parentheses
  337. call_inferred = None
  338. try:
  339. # Just forcing the generator to infer all elements.
  340. # astroid.exceptions.InferenceError are false positives
  341. # see https://github.com/pylint-dev/pylint/pull/8185
  342. if isinstance(inferred, nodes.FunctionDef):
  343. call_inferred = list(inferred.infer_call_result())
  344. elif isinstance(inferred, nodes.Lambda):
  345. call_inferred = list(inferred.infer_call_result(node))
  346. except astroid.InferenceError:
  347. call_inferred = None
  348. if call_inferred:
  349. self.add_message(
  350. "missing-parentheses-for-call-in-test",
  351. node=test,
  352. confidence=INFERENCE,
  353. )
  354. self.add_message("using-constant-test", node=test, confidence=INFERENCE)
  355. @staticmethod
  356. def _name_holds_generator(test: nodes.Name) -> tuple[bool, nodes.Call | None]:
  357. """Return whether `test` tests a name certain to hold a generator, or optionally
  358. a call that should be then tested to see if *it* returns only generators.
  359. """
  360. assert isinstance(test, nodes.Name)
  361. emit = False
  362. maybe_generator_call = None
  363. lookup_result = test.frame(future=True).lookup(test.name)
  364. if not lookup_result:
  365. return emit, maybe_generator_call
  366. maybe_generator_assigned = (
  367. isinstance(assign_name.parent.value, nodes.GeneratorExp)
  368. for assign_name in lookup_result[1]
  369. if isinstance(assign_name.parent, nodes.Assign)
  370. )
  371. first_item = next(maybe_generator_assigned, None)
  372. if first_item is not None:
  373. # Emit if this variable is certain to hold a generator
  374. if all(itertools.chain((first_item,), maybe_generator_assigned)):
  375. emit = True
  376. # If this variable holds the result of a call, save it for next test
  377. elif (
  378. len(lookup_result[1]) == 1
  379. and isinstance(lookup_result[1][0].parent, nodes.Assign)
  380. and isinstance(lookup_result[1][0].parent.value, nodes.Call)
  381. ):
  382. maybe_generator_call = lookup_result[1][0].parent.value
  383. return emit, maybe_generator_call
  384. def visit_module(self, _: nodes.Module) -> None:
  385. """Check module name, docstring and required arguments."""
  386. self.linter.stats.node_count["module"] += 1
  387. def visit_classdef(self, _: nodes.ClassDef) -> None:
  388. """Check module name, docstring and redefinition
  389. increment branch counter.
  390. """
  391. self.linter.stats.node_count["klass"] += 1
  392. @utils.only_required_for_messages(
  393. "pointless-statement",
  394. "pointless-exception-statement",
  395. "pointless-string-statement",
  396. "expression-not-assigned",
  397. "named-expr-without-context",
  398. )
  399. def visit_expr(self, node: nodes.Expr) -> None:
  400. """Check for various kind of statements without effect."""
  401. expr = node.value
  402. if isinstance(expr, nodes.Const) and isinstance(expr.value, str):
  403. # treat string statement in a separated message
  404. # Handle PEP-257 attribute docstrings.
  405. # An attribute docstring is defined as being a string right after
  406. # an assignment at the module level, class level or __init__ level.
  407. scope = expr.scope()
  408. if isinstance(scope, (nodes.ClassDef, nodes.Module, nodes.FunctionDef)):
  409. if isinstance(scope, nodes.FunctionDef) and scope.name != "__init__":
  410. pass
  411. else:
  412. sibling = expr.previous_sibling()
  413. if (
  414. sibling is not None
  415. and sibling.scope() is scope
  416. and isinstance(sibling, (nodes.Assign, nodes.AnnAssign))
  417. ):
  418. return
  419. self.add_message("pointless-string-statement", node=node)
  420. return
  421. # Warn W0133 for exceptions that are used as statements
  422. if isinstance(expr, nodes.Call):
  423. name = ""
  424. if isinstance(expr.func, nodes.Name):
  425. name = expr.func.name
  426. elif isinstance(expr.func, nodes.Attribute):
  427. name = expr.func.attrname
  428. # Heuristic: only run inference for names that begin with an uppercase char
  429. # This reduces W0133's coverage, but retains acceptable runtime performance
  430. # For more details, see: https://github.com/pylint-dev/pylint/issues/8073
  431. inferred = utils.safe_infer(expr) if name[:1].isupper() else None
  432. if isinstance(inferred, objects.ExceptionInstance):
  433. self.add_message(
  434. "pointless-exception-statement", node=node, confidence=INFERENCE
  435. )
  436. return
  437. # Ignore if this is :
  438. # * the unique child of a try/except body
  439. # * a yield statement
  440. # * an ellipsis (which can be used on Python 3 instead of pass)
  441. # warn W0106 if we have any underlying function call (we can't predict
  442. # side effects), else pointless-statement
  443. if (
  444. isinstance(expr, (nodes.Yield, nodes.Await))
  445. or (isinstance(node.parent, nodes.TryExcept) and node.parent.body == [node])
  446. or (isinstance(expr, nodes.Const) and expr.value is Ellipsis)
  447. ):
  448. return
  449. if isinstance(expr, nodes.NamedExpr):
  450. self.add_message("named-expr-without-context", node=node, confidence=HIGH)
  451. elif any(expr.nodes_of_class(nodes.Call)):
  452. self.add_message(
  453. "expression-not-assigned", node=node, args=expr.as_string()
  454. )
  455. else:
  456. self.add_message("pointless-statement", node=node)
  457. @staticmethod
  458. def _filter_vararg(
  459. node: nodes.Lambda, call_args: list[nodes.NodeNG]
  460. ) -> Iterator[nodes.NodeNG]:
  461. # Return the arguments for the given call which are
  462. # not passed as vararg.
  463. for arg in call_args:
  464. if isinstance(arg, nodes.Starred):
  465. if (
  466. isinstance(arg.value, nodes.Name)
  467. and arg.value.name != node.args.vararg
  468. ):
  469. yield arg
  470. else:
  471. yield arg
  472. @staticmethod
  473. def _has_variadic_argument(
  474. args: list[nodes.Starred | nodes.Keyword], variadic_name: str
  475. ) -> bool:
  476. return not args or any(
  477. isinstance(a.value, nodes.Name)
  478. and a.value.name != variadic_name
  479. or not isinstance(a.value, nodes.Name)
  480. for a in args
  481. )
  482. @utils.only_required_for_messages("unnecessary-lambda")
  483. # pylint: disable-next=too-many-return-statements
  484. def visit_lambda(self, node: nodes.Lambda) -> None:
  485. """Check whether the lambda is suspicious."""
  486. # if the body of the lambda is a call expression with the same
  487. # argument list as the lambda itself, then the lambda is
  488. # possibly unnecessary and at least suspicious.
  489. if node.args.defaults:
  490. # If the arguments of the lambda include defaults, then a
  491. # judgment cannot be made because there is no way to check
  492. # that the defaults defined by the lambda are the same as
  493. # the defaults defined by the function called in the body
  494. # of the lambda.
  495. return
  496. call = node.body
  497. if not isinstance(call, nodes.Call):
  498. # The body of the lambda must be a function call expression
  499. # for the lambda to be unnecessary.
  500. return
  501. if isinstance(node.body.func, nodes.Attribute) and isinstance(
  502. node.body.func.expr, nodes.Call
  503. ):
  504. # Chained call, the intermediate call might
  505. # return something else (but we don't check that, yet).
  506. return
  507. call_site = astroid.arguments.CallSite.from_call(call)
  508. ordinary_args = list(node.args.args)
  509. new_call_args = list(self._filter_vararg(node, call.args))
  510. if node.args.kwarg:
  511. if self._has_variadic_argument(call.kwargs, node.args.kwarg):
  512. return
  513. if node.args.vararg:
  514. if self._has_variadic_argument(call.starargs, node.args.vararg):
  515. return
  516. elif call.starargs:
  517. return
  518. if call.keywords:
  519. # Look for additional keyword arguments that are not part
  520. # of the lambda's signature
  521. lambda_kwargs = {keyword.name for keyword in node.args.defaults}
  522. if len(lambda_kwargs) != len(call_site.keyword_arguments):
  523. # Different lengths, so probably not identical
  524. return
  525. if set(call_site.keyword_arguments).difference(lambda_kwargs):
  526. return
  527. # The "ordinary" arguments must be in a correspondence such that:
  528. # ordinary_args[i].name == call.args[i].name.
  529. if len(ordinary_args) != len(new_call_args):
  530. return
  531. for arg, passed_arg in zip(ordinary_args, new_call_args):
  532. if not isinstance(passed_arg, nodes.Name):
  533. return
  534. if arg.name != passed_arg.name:
  535. return
  536. # The lambda is necessary if it uses its parameter in the function it is
  537. # calling in the lambda's body
  538. # e.g. lambda foo: (func1 if foo else func2)(foo)
  539. for name in call.func.nodes_of_class(nodes.Name):
  540. if name.lookup(name.name)[0] is node:
  541. return
  542. self.add_message("unnecessary-lambda", line=node.fromlineno, node=node)
  543. @utils.only_required_for_messages("dangerous-default-value")
  544. def visit_functiondef(self, node: nodes.FunctionDef) -> None:
  545. """Check function name, docstring, arguments, redefinition,
  546. variable names, max locals.
  547. """
  548. if node.is_method():
  549. self.linter.stats.node_count["method"] += 1
  550. else:
  551. self.linter.stats.node_count["function"] += 1
  552. self._check_dangerous_default(node)
  553. visit_asyncfunctiondef = visit_functiondef
  554. def _check_dangerous_default(self, node: nodes.FunctionDef) -> None:
  555. """Check for dangerous default values as arguments."""
  556. def is_iterable(internal_node: nodes.NodeNG) -> bool:
  557. return isinstance(internal_node, (nodes.List, nodes.Set, nodes.Dict))
  558. defaults = (node.args.defaults or []) + (node.args.kw_defaults or [])
  559. for default in defaults:
  560. if not default:
  561. continue
  562. try:
  563. value = next(default.infer())
  564. except astroid.InferenceError:
  565. continue
  566. if (
  567. isinstance(value, astroid.Instance)
  568. and value.qname() in DEFAULT_ARGUMENT_SYMBOLS
  569. ):
  570. if value is default:
  571. msg = DEFAULT_ARGUMENT_SYMBOLS[value.qname()]
  572. elif isinstance(value, astroid.Instance) or is_iterable(value):
  573. # We are here in the following situation(s):
  574. # * a dict/set/list/tuple call which wasn't inferred
  575. # to a syntax node ({}, () etc.). This can happen
  576. # when the arguments are invalid or unknown to
  577. # the inference.
  578. # * a variable from somewhere else, which turns out to be a list
  579. # or a dict.
  580. if is_iterable(default):
  581. msg = value.pytype()
  582. elif isinstance(default, nodes.Call):
  583. msg = f"{value.name}() ({value.qname()})"
  584. else:
  585. msg = f"{default.as_string()} ({value.qname()})"
  586. else:
  587. # this argument is a name
  588. msg = f"{default.as_string()} ({DEFAULT_ARGUMENT_SYMBOLS[value.qname()]})"
  589. self.add_message("dangerous-default-value", node=node, args=(msg,))
  590. @utils.only_required_for_messages("unreachable", "lost-exception")
  591. def visit_return(self, node: nodes.Return) -> None:
  592. """Return node visitor.
  593. 1 - check if the node has a right sibling (if so, that's some
  594. unreachable code)
  595. 2 - check if the node is inside the 'finally' clause of a 'try...finally'
  596. block
  597. """
  598. self._check_unreachable(node)
  599. # Is it inside final body of a try...finally block ?
  600. self._check_not_in_finally(node, "return", (nodes.FunctionDef,))
  601. @utils.only_required_for_messages("unreachable")
  602. def visit_continue(self, node: nodes.Continue) -> None:
  603. """Check is the node has a right sibling (if so, that's some unreachable
  604. code).
  605. """
  606. self._check_unreachable(node)
  607. @utils.only_required_for_messages("unreachable", "lost-exception")
  608. def visit_break(self, node: nodes.Break) -> None:
  609. """Break node visitor.
  610. 1 - check if the node has a right sibling (if so, that's some
  611. unreachable code)
  612. 2 - check if the node is inside the 'finally' clause of a 'try...finally'
  613. block
  614. """
  615. # 1 - Is it right sibling ?
  616. self._check_unreachable(node)
  617. # 2 - Is it inside final body of a try...finally block ?
  618. self._check_not_in_finally(node, "break", (nodes.For, nodes.While))
  619. @utils.only_required_for_messages("unreachable")
  620. def visit_raise(self, node: nodes.Raise) -> None:
  621. """Check if the node has a right sibling (if so, that's some unreachable
  622. code).
  623. """
  624. self._check_unreachable(node)
  625. def _check_misplaced_format_function(self, call_node: nodes.Call) -> None:
  626. if not isinstance(call_node.func, nodes.Attribute):
  627. return
  628. if call_node.func.attrname != "format":
  629. return
  630. expr = utils.safe_infer(call_node.func.expr)
  631. if isinstance(expr, util.UninferableBase):
  632. return
  633. if not expr:
  634. # we are doubtful on inferred type of node, so here just check if format
  635. # was called on print()
  636. call_expr = call_node.func.expr
  637. if not isinstance(call_expr, nodes.Call):
  638. return
  639. if (
  640. isinstance(call_expr.func, nodes.Name)
  641. and call_expr.func.name == "print"
  642. ):
  643. self.add_message("misplaced-format-function", node=call_node)
  644. @utils.only_required_for_messages(
  645. "eval-used",
  646. "exec-used",
  647. "bad-reversed-sequence",
  648. "misplaced-format-function",
  649. "unreachable",
  650. )
  651. def visit_call(self, node: nodes.Call) -> None:
  652. """Visit a Call node."""
  653. if utils.is_terminating_func(node):
  654. self._check_unreachable(node, confidence=INFERENCE)
  655. self._check_misplaced_format_function(node)
  656. if isinstance(node.func, nodes.Name):
  657. name = node.func.name
  658. # ignore the name if it's not a builtin (i.e. not defined in the
  659. # locals nor globals scope)
  660. if not (name in node.frame(future=True) or name in node.root()):
  661. if name == "exec":
  662. self.add_message("exec-used", node=node)
  663. elif name == "reversed":
  664. self._check_reversed(node)
  665. elif name == "eval":
  666. self.add_message("eval-used", node=node)
  667. @utils.only_required_for_messages("assert-on-tuple", "assert-on-string-literal")
  668. def visit_assert(self, node: nodes.Assert) -> None:
  669. """Check whether assert is used on a tuple or string literal."""
  670. if isinstance(node.test, nodes.Tuple) and len(node.test.elts) > 0:
  671. self.add_message("assert-on-tuple", node=node, confidence=HIGH)
  672. if isinstance(node.test, nodes.Const) and isinstance(node.test.value, str):
  673. if node.test.value:
  674. when = "never"
  675. else:
  676. when = "always"
  677. self.add_message("assert-on-string-literal", node=node, args=(when,))
  678. @utils.only_required_for_messages("duplicate-key")
  679. def visit_dict(self, node: nodes.Dict) -> None:
  680. """Check duplicate key in dictionary."""
  681. keys = set()
  682. for k, _ in node.items:
  683. if isinstance(k, nodes.Const):
  684. key = k.value
  685. elif isinstance(k, nodes.Attribute):
  686. key = k.as_string()
  687. else:
  688. continue
  689. if key in keys:
  690. self.add_message("duplicate-key", node=node, args=key)
  691. keys.add(key)
  692. @utils.only_required_for_messages("duplicate-value")
  693. def visit_set(self, node: nodes.Set) -> None:
  694. """Check duplicate value in set."""
  695. values = set()
  696. for v in node.elts:
  697. if isinstance(v, nodes.Const):
  698. value = v.value
  699. else:
  700. continue
  701. if value in values:
  702. self.add_message(
  703. "duplicate-value", node=node, args=value, confidence=HIGH
  704. )
  705. values.add(value)
  706. def visit_tryfinally(self, node: nodes.TryFinally) -> None:
  707. """Update try...finally flag."""
  708. assert self._tryfinallys is not None
  709. self._tryfinallys.append(node)
  710. def leave_tryfinally(self, _: nodes.TryFinally) -> None:
  711. """Update try...finally flag."""
  712. assert self._tryfinallys is not None
  713. self._tryfinallys.pop()
  714. def _check_unreachable(
  715. self,
  716. node: nodes.Return | nodes.Continue | nodes.Break | nodes.Raise | nodes.Call,
  717. confidence: Confidence = HIGH,
  718. ) -> None:
  719. """Check unreachable code."""
  720. unreachable_statement = node.next_sibling()
  721. if unreachable_statement is not None:
  722. if (
  723. isinstance(node, nodes.Return)
  724. and isinstance(unreachable_statement, nodes.Expr)
  725. and isinstance(unreachable_statement.value, nodes.Yield)
  726. ):
  727. # Don't add 'unreachable' for empty generators.
  728. # Only add warning if 'yield' is followed by another node.
  729. unreachable_statement = unreachable_statement.next_sibling()
  730. if unreachable_statement is None:
  731. return
  732. self.add_message(
  733. "unreachable", node=unreachable_statement, confidence=confidence
  734. )
  735. def _check_not_in_finally(
  736. self,
  737. node: nodes.Break | nodes.Return,
  738. node_name: str,
  739. breaker_classes: tuple[nodes.NodeNG, ...] = (),
  740. ) -> None:
  741. """Check that a node is not inside a 'finally' clause of a
  742. 'try...finally' statement.
  743. If we find a parent which type is in breaker_classes before
  744. a 'try...finally' block we skip the whole check.
  745. """
  746. # if self._tryfinallys is empty, we're not an in try...finally block
  747. if not self._tryfinallys:
  748. return
  749. # the node could be a grand-grand...-child of the 'try...finally'
  750. _parent = node.parent
  751. _node = node
  752. while _parent and not isinstance(_parent, breaker_classes):
  753. if hasattr(_parent, "finalbody") and _node in _parent.finalbody:
  754. self.add_message("lost-exception", node=node, args=node_name)
  755. return
  756. _node = _parent
  757. _parent = _node.parent
  758. def _check_reversed(self, node: nodes.Call) -> None:
  759. """Check that the argument to `reversed` is a sequence."""
  760. try:
  761. argument = utils.safe_infer(utils.get_argument_from_call(node, position=0))
  762. except utils.NoSuchArgumentError:
  763. pass
  764. else:
  765. if isinstance(argument, util.UninferableBase):
  766. return
  767. if argument is None:
  768. # Nothing was inferred.
  769. # Try to see if we have iter().
  770. if isinstance(node.args[0], nodes.Call):
  771. try:
  772. func = next(node.args[0].func.infer())
  773. except astroid.InferenceError:
  774. return
  775. if getattr(
  776. func, "name", None
  777. ) == "iter" and utils.is_builtin_object(func):
  778. self.add_message("bad-reversed-sequence", node=node)
  779. return
  780. if isinstance(argument, (nodes.List, nodes.Tuple)):
  781. return
  782. # dicts are reversible, but only from Python 3.8 onward. Prior to
  783. # that, any class based on dict must explicitly provide a
  784. # __reversed__ method
  785. if not self._py38_plus and isinstance(argument, astroid.Instance):
  786. if any(
  787. ancestor.name == "dict" and utils.is_builtin_object(ancestor)
  788. for ancestor in itertools.chain(
  789. (argument._proxied,), argument._proxied.ancestors()
  790. )
  791. ):
  792. try:
  793. argument.locals[REVERSED_PROTOCOL_METHOD]
  794. except KeyError:
  795. self.add_message("bad-reversed-sequence", node=node)
  796. return
  797. if hasattr(argument, "getattr"):
  798. # everything else is not a proper sequence for reversed()
  799. for methods in REVERSED_METHODS:
  800. for meth in methods:
  801. try:
  802. argument.getattr(meth)
  803. except astroid.NotFoundError:
  804. break
  805. else:
  806. break
  807. else:
  808. self.add_message("bad-reversed-sequence", node=node)
  809. else:
  810. self.add_message("bad-reversed-sequence", node=node)
  811. @utils.only_required_for_messages("confusing-with-statement")
  812. def visit_with(self, node: nodes.With) -> None:
  813. # a "with" statement with multiple managers corresponds
  814. # to one AST "With" node with multiple items
  815. pairs = node.items
  816. if pairs:
  817. for prev_pair, pair in zip(pairs, pairs[1:]):
  818. if isinstance(prev_pair[1], nodes.AssignName) and (
  819. pair[1] is None and not isinstance(pair[0], nodes.Call)
  820. ):
  821. # Don't emit a message if the second is a function call
  822. # there's no way that can be mistaken for a name assignment.
  823. # If the line number doesn't match
  824. # we assume it's a nested "with".
  825. self.add_message("confusing-with-statement", node=node)
  826. def _check_self_assigning_variable(self, node: nodes.Assign) -> None:
  827. # Detect assigning to the same variable.
  828. scope = node.scope()
  829. scope_locals = scope.locals
  830. rhs_names = []
  831. targets = node.targets
  832. if isinstance(targets[0], nodes.Tuple):
  833. if len(targets) != 1:
  834. # A complex assignment, so bail out early.
  835. return
  836. targets = targets[0].elts
  837. if len(targets) == 1:
  838. # Unpacking a variable into the same name.
  839. return
  840. if isinstance(node.value, nodes.Name):
  841. if len(targets) != 1:
  842. return
  843. rhs_names = [node.value]
  844. elif isinstance(node.value, nodes.Tuple):
  845. rhs_count = len(node.value.elts)
  846. if len(targets) != rhs_count or rhs_count == 1:
  847. return
  848. rhs_names = node.value.elts
  849. for target, lhs_name in zip(targets, rhs_names):
  850. if not isinstance(lhs_name, nodes.Name):
  851. continue
  852. if not isinstance(target, nodes.AssignName):
  853. continue
  854. # Check that the scope is different from a class level, which is usually
  855. # a pattern to expose module level attributes as class level ones.
  856. if isinstance(scope, nodes.ClassDef) and target.name in scope_locals:
  857. continue
  858. if target.name == lhs_name.name:
  859. self.add_message(
  860. "self-assigning-variable", args=(target.name,), node=target
  861. )
  862. def _check_redeclared_assign_name(self, targets: list[nodes.NodeNG | None]) -> None:
  863. dummy_variables_rgx = self.linter.config.dummy_variables_rgx
  864. for target in targets:
  865. if not isinstance(target, nodes.Tuple):
  866. continue
  867. found_names = []
  868. for element in target.elts:
  869. if isinstance(element, nodes.Tuple):
  870. self._check_redeclared_assign_name([element])
  871. elif isinstance(element, nodes.AssignName) and element.name != "_":
  872. if dummy_variables_rgx and dummy_variables_rgx.match(element.name):
  873. return
  874. found_names.append(element.name)
  875. names = collections.Counter(found_names)
  876. for name, count in names.most_common():
  877. if count > 1:
  878. self.add_message(
  879. "redeclared-assigned-name", args=(name,), node=target
  880. )
  881. @utils.only_required_for_messages(
  882. "self-assigning-variable", "redeclared-assigned-name"
  883. )
  884. def visit_assign(self, node: nodes.Assign) -> None:
  885. self._check_self_assigning_variable(node)
  886. self._check_redeclared_assign_name(node.targets)
  887. @utils.only_required_for_messages("redeclared-assigned-name")
  888. def visit_for(self, node: nodes.For) -> None:
  889. self._check_redeclared_assign_name([node.target])