variables.py 137 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534
  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. """Variables checkers for Python code."""
  5. from __future__ import annotations
  6. import copy
  7. import itertools
  8. import math
  9. import os
  10. import re
  11. from collections import defaultdict
  12. from enum import Enum
  13. from functools import cached_property
  14. from typing import TYPE_CHECKING
  15. import astroid
  16. import astroid.exceptions
  17. import astroid.modutils
  18. from astroid import bases, extract_node, nodes, objects, util
  19. from pylint.checkers import BaseChecker, utils
  20. from pylint.checkers.utils import (
  21. in_type_checking_block,
  22. is_module_ignored,
  23. is_postponed_evaluation_enabled,
  24. is_sys_guard,
  25. overridden_method,
  26. )
  27. from pylint.constants import TYPING_NEVER, TYPING_NORETURN
  28. from pylint.interfaces import CONTROL_FLOW, HIGH, INFERENCE, INFERENCE_FAILURE
  29. if TYPE_CHECKING:
  30. from collections.abc import Generator, Iterable, Iterator
  31. from astroid.nodes import _base_nodes
  32. from astroid.typing import InferenceResult
  33. from pylint.lint import PyLinter
  34. from pylint.typing import MessageDefinitionTuple
  35. Consumption = dict[str, list[nodes.NodeNG]]
  36. SPECIAL_OBJ = re.compile("^_{2}[a-z]+_{2}$")
  37. FUTURE = "__future__"
  38. # regexp for ignored argument name
  39. IGNORED_ARGUMENT_NAMES = re.compile("_.*|^ignored_|^unused_")
  40. # In Python 3.7 abc has a Python implementation which is preferred
  41. # by astroid. Unfortunately this also messes up our explicit checks
  42. # for `abc`
  43. METACLASS_NAME_TRANSFORMS = {"_py_abc": "abc"}
  44. BUILTIN_RANGE = "builtins.range"
  45. TYPING_MODULE = "typing"
  46. DICT_TYPES = (
  47. objects.DictValues,
  48. objects.DictKeys,
  49. objects.DictItems,
  50. nodes.Dict,
  51. )
  52. NODES_WITH_VALUE_ATTR = (
  53. nodes.Assign,
  54. nodes.AnnAssign,
  55. nodes.AugAssign,
  56. nodes.Expr,
  57. nodes.Return,
  58. nodes.Match,
  59. nodes.TypeAlias,
  60. )
  61. class VariableVisitConsumerAction(Enum):
  62. """Reported by _check_consumer() and its sub-methods to determine the
  63. subsequent action to take in _undefined_and_used_before_checker().
  64. Continue -> continue loop to next consumer
  65. Return -> return and thereby break the loop
  66. """
  67. CONTINUE = 0
  68. RETURN = 1
  69. def _is_from_future_import(stmt: nodes.ImportFrom, name: str) -> bool | None:
  70. """Check if the name is a future import from another module."""
  71. try:
  72. module = stmt.do_import_module(stmt.modname)
  73. except astroid.AstroidBuildingError:
  74. return None
  75. for local_node in module.locals.get(name, []):
  76. if isinstance(local_node, nodes.ImportFrom) and local_node.modname == FUTURE:
  77. return True
  78. return None
  79. def _get_unpacking_extra_info(node: nodes.Assign, inferred: InferenceResult) -> str:
  80. """Return extra information to add to the message for unpacking-non-sequence
  81. and unbalanced-tuple/dict-unpacking errors.
  82. """
  83. more = ""
  84. if isinstance(inferred, DICT_TYPES):
  85. match node:
  86. case nodes.Assign():
  87. more = node.value.as_string()
  88. case nodes.For():
  89. more = node.iter.as_string()
  90. return more
  91. inferred_module = inferred.root().name
  92. if node.root().name == inferred_module:
  93. if node.lineno == inferred.lineno:
  94. more = f"'{inferred.as_string()}'"
  95. elif inferred.lineno:
  96. more = f"defined at line {inferred.lineno}"
  97. elif inferred.lineno:
  98. more = f"defined at line {inferred.lineno} of {inferred_module}"
  99. return more
  100. def _detect_global_scope(
  101. node: nodes.Name,
  102. frame: nodes.LocalsDictNodeNG,
  103. defframe: nodes.LocalsDictNodeNG,
  104. ) -> bool:
  105. """Detect that the given frames share a global scope.
  106. Two frames share a global scope when neither
  107. of them are hidden under a function scope, as well
  108. as any parent scope of them, until the root scope.
  109. In this case, depending from something defined later on
  110. will only work if guarded by a nested function definition.
  111. Example:
  112. class A:
  113. # B has the same global scope as `C`, leading to a NameError.
  114. # Return True to indicate a shared scope.
  115. class B(C): ...
  116. class C: ...
  117. Whereas this does not lead to a NameError:
  118. class A:
  119. def guard():
  120. # Return False to indicate no scope sharing.
  121. class B(C): ...
  122. class C: ...
  123. """
  124. def_scope = scope = None
  125. if frame and frame.parent:
  126. scope = frame.parent.scope()
  127. if defframe and defframe.parent:
  128. def_scope = defframe.parent.scope()
  129. if (
  130. isinstance(frame, nodes.ClassDef)
  131. and scope is not def_scope
  132. and scope is utils.get_node_first_ancestor_of_type(node, nodes.FunctionDef)
  133. ):
  134. # If the current node's scope is a class nested under a function,
  135. # and the def_scope is something else, then they aren't shared.
  136. return False
  137. if isinstance(frame, nodes.FunctionDef):
  138. # If the parent of the current node is a
  139. # function, then it can be under its scope (defined in); or
  140. # the `->` part of annotations. The same goes
  141. # for annotations of function arguments, they'll have
  142. # their parent the Arguments node.
  143. if frame.parent_of(defframe):
  144. return node.lineno < defframe.lineno # type: ignore[no-any-return]
  145. if not isinstance(node.parent, (nodes.FunctionDef, nodes.Arguments)):
  146. return False
  147. break_scopes = []
  148. for current_scope in (scope or frame, def_scope):
  149. # Look for parent scopes. If there is anything different
  150. # than a module or a class scope, then the frames don't
  151. # share a global scope.
  152. parent_scope = current_scope
  153. while parent_scope:
  154. if not isinstance(parent_scope, (nodes.ClassDef, nodes.Module)):
  155. break_scopes.append(parent_scope)
  156. break
  157. if parent_scope.parent:
  158. parent_scope = parent_scope.parent.scope()
  159. else:
  160. break
  161. if len(set(break_scopes)) > 1:
  162. # Store different scopes than expected.
  163. # If the stored scopes are, in fact, the very same, then it means
  164. # that the two frames (frame and defframe) share the same scope,
  165. # and we could apply our lineno analysis over them.
  166. # For instance, this works when they are inside a function, the node
  167. # that uses a definition and the definition itself.
  168. return False
  169. # At this point, we are certain that frame and defframe share a scope
  170. # and the definition of the first depends on the second.
  171. return frame.lineno < defframe.lineno # type: ignore[no-any-return]
  172. def _infer_name_module(node: nodes.Import, name: str) -> Generator[InferenceResult]:
  173. context = astroid.context.InferenceContext()
  174. context.lookupname = name
  175. return node.infer(context, asname=False) # type: ignore[no-any-return]
  176. def _fix_dot_imports(
  177. not_consumed: Consumption,
  178. ) -> list[tuple[str, _base_nodes.ImportNode]]:
  179. """Try to fix imports with multiple dots, by returning a dictionary
  180. with the import names expanded.
  181. The function unflattens root imports,
  182. like 'xml' (when we have both 'xml.etree' and 'xml.sax'), to 'xml.etree'
  183. and 'xml.sax' respectively.
  184. """
  185. names: dict[str, _base_nodes.ImportNode] = {}
  186. for name, stmts in not_consumed.items():
  187. if any(
  188. isinstance(stmt, nodes.AssignName)
  189. and isinstance(stmt.assign_type(), nodes.AugAssign)
  190. for stmt in stmts
  191. ):
  192. continue
  193. for stmt in stmts:
  194. if not isinstance(stmt, (nodes.ImportFrom, nodes.Import)):
  195. continue
  196. for imports in stmt.names:
  197. second_name = None
  198. import_module_name = imports[0]
  199. if import_module_name == "*":
  200. # In case of wildcard imports,
  201. # pick the name from inside the imported module.
  202. second_name = name
  203. else:
  204. name_matches_dotted_import = False
  205. if (
  206. import_module_name.startswith(name)
  207. and import_module_name.find(".") > -1
  208. ):
  209. name_matches_dotted_import = True
  210. if name_matches_dotted_import or name in imports:
  211. # Most likely something like 'xml.etree',
  212. # which will appear in the .locals as 'xml'.
  213. # Only pick the name if it wasn't consumed.
  214. second_name = import_module_name
  215. if second_name and second_name not in names:
  216. names[second_name] = stmt
  217. return sorted(names.items(), key=lambda a: a[1].fromlineno)
  218. def _find_frame_imports(name: str, frame: nodes.LocalsDictNodeNG) -> bool:
  219. """Detect imports in the frame, with the required *name*.
  220. Such imports can be considered assignments if they are not globals.
  221. Returns True if an import for the given name was found.
  222. """
  223. if name in _flattened_scope_names(frame.nodes_of_class(nodes.Global)):
  224. return False
  225. imports = frame.nodes_of_class((nodes.Import, nodes.ImportFrom))
  226. for import_node in imports:
  227. for import_name, import_alias in import_node.names:
  228. # If the import uses an alias, check only that.
  229. # Otherwise, check only the import name.
  230. if import_alias:
  231. if import_alias == name:
  232. return True
  233. elif import_name and import_name == name:
  234. return True
  235. return False
  236. def _import_name_is_global(
  237. stmt: nodes.Global | _base_nodes.ImportNode,
  238. global_names: set[str],
  239. ) -> bool:
  240. for import_name, import_alias in stmt.names:
  241. # If the import uses an alias, check only that.
  242. # Otherwise, check only the import name.
  243. if import_alias:
  244. if import_alias in global_names:
  245. return True
  246. elif import_name in global_names:
  247. return True
  248. return False
  249. def _flattened_scope_names(
  250. iterator: Iterator[nodes.Global | nodes.Nonlocal],
  251. ) -> set[str]:
  252. values = (set(stmt.names) for stmt in iterator)
  253. return set(itertools.chain.from_iterable(values))
  254. def _assigned_locally(name_node: nodes.Name) -> bool:
  255. """Checks if name_node has corresponding assign statement in same scope."""
  256. name_node_scope = name_node.scope()
  257. assign_stmts = name_node_scope.nodes_of_class(nodes.AssignName)
  258. return any(a.name == name_node.name for a in assign_stmts) or _find_frame_imports(
  259. name_node.name, name_node_scope
  260. )
  261. def _is_before(node: nodes.NodeNG, reference_node: nodes.NodeNG) -> bool:
  262. """Checks if node appears before reference_node."""
  263. if node.lineno < reference_node.lineno:
  264. return True
  265. if (
  266. node.lineno == reference_node.lineno
  267. and node.col_offset < reference_node.col_offset
  268. ):
  269. return True
  270. return False
  271. def _is_nonlocal_name(node: nodes.Name, frame: nodes.LocalsDictNodeNG) -> bool:
  272. """Checks if name node has a nonlocal declaration in the given frame."""
  273. if not isinstance(frame, nodes.FunctionDef):
  274. return False
  275. return any(
  276. isinstance(stmt, nodes.Nonlocal)
  277. and node.name in stmt.names
  278. and _is_before(stmt, node)
  279. for stmt in frame.body
  280. )
  281. def _has_locals_call_after_node(stmt: nodes.NodeNG, scope: nodes.FunctionDef) -> bool:
  282. skip_nodes = (
  283. nodes.FunctionDef,
  284. nodes.ClassDef,
  285. nodes.Import,
  286. nodes.ImportFrom,
  287. )
  288. for call in scope.nodes_of_class(nodes.Call, skip_klass=skip_nodes):
  289. inferred = utils.safe_infer(call.func)
  290. if (
  291. utils.is_builtin_object(inferred)
  292. and getattr(inferred, "name", None) == "locals"
  293. ):
  294. if stmt.lineno < call.lineno:
  295. return True
  296. return False
  297. MSGS: dict[str, MessageDefinitionTuple] = {
  298. "E0601": (
  299. "Using variable %r before assignment",
  300. "used-before-assignment",
  301. "Emitted when a local variable is accessed before its assignment took place. "
  302. "Assignments in try blocks are assumed not to have occurred when evaluating "
  303. "associated except/finally blocks. Assignments in except blocks are assumed "
  304. "not to have occurred when evaluating statements outside the block, except "
  305. "when the associated try block contains a return statement.",
  306. ),
  307. "E0602": (
  308. "Undefined variable %r",
  309. "undefined-variable",
  310. "Used when an undefined variable is accessed.",
  311. ),
  312. "E0603": (
  313. "Undefined variable name %r in __all__",
  314. "undefined-all-variable",
  315. "Used when an undefined variable name is referenced in __all__.",
  316. ),
  317. "E0604": (
  318. "Invalid object %r in __all__, must contain only strings",
  319. "invalid-all-object",
  320. "Used when an invalid (non-string) object occurs in __all__.",
  321. ),
  322. "E0605": (
  323. "Invalid format for __all__, must be tuple or list",
  324. "invalid-all-format",
  325. "Used when __all__ has an invalid format.",
  326. ),
  327. "E0606": (
  328. "Possibly using variable %r before assignment",
  329. "possibly-used-before-assignment",
  330. "Emitted when a local variable is accessed before its assignment took place "
  331. "in both branches of an if/else switch.",
  332. ),
  333. "E0611": (
  334. "No name %r in module %r",
  335. "no-name-in-module",
  336. "Used when a name cannot be found in a module.",
  337. ),
  338. "W0601": (
  339. "Global variable %r undefined at the module level",
  340. "global-variable-undefined",
  341. 'Used when a variable is defined through the "global" statement '
  342. "but the variable is not defined in the module scope.",
  343. ),
  344. "W0602": (
  345. "Using global for %r but no assignment is done",
  346. "global-variable-not-assigned",
  347. "When a variable defined in the global scope is modified in an inner scope, "
  348. "the 'global' keyword is required in the inner scope only if there is an "
  349. "assignment operation done in the inner scope.",
  350. ),
  351. "W0603": (
  352. "Using the global statement", # W0121
  353. "global-statement",
  354. 'Used when you use the "global" statement to update a global '
  355. "variable. Pylint discourages its usage. That doesn't mean you cannot "
  356. "use it!",
  357. ),
  358. "W0604": (
  359. "Using the global statement at the module level", # W0103
  360. "global-at-module-level",
  361. 'Used when you use the "global" statement at the module level '
  362. "since it has no effect.",
  363. ),
  364. "W0611": (
  365. "Unused %s",
  366. "unused-import",
  367. "Used when an imported module or variable is not used.",
  368. ),
  369. "W0612": (
  370. "Unused variable %r",
  371. "unused-variable",
  372. "Used when a variable is defined but not used.",
  373. ),
  374. "W0613": (
  375. "Unused argument %r",
  376. "unused-argument",
  377. "Used when a function or method argument is not used.",
  378. ),
  379. "W0614": (
  380. "Unused import(s) %s from wildcard import of %s",
  381. "unused-wildcard-import",
  382. "Used when an imported module or variable is not used from a "
  383. "`'from X import *'` style import.",
  384. ),
  385. "W0621": (
  386. "Redefining name %r from outer scope (line %s)",
  387. "redefined-outer-name",
  388. "Used when a variable's name hides a name defined in an outer scope or except handler.",
  389. ),
  390. "W0622": (
  391. "Redefining built-in %r",
  392. "redefined-builtin",
  393. "Used when a variable or function override a built-in.",
  394. ),
  395. "W0631": (
  396. "Using possibly undefined loop variable %r",
  397. "undefined-loop-variable",
  398. "Used when a loop variable (i.e. defined by a for loop or "
  399. "a list comprehension or a generator expression) is used outside "
  400. "the loop.",
  401. ),
  402. "W0632": (
  403. "Possible unbalanced tuple unpacking with sequence %s: left side has %d "
  404. "label%s, right side has %d value%s",
  405. "unbalanced-tuple-unpacking",
  406. "Used when there is an unbalanced tuple unpacking in assignment",
  407. {"old_names": [("E0632", "old-unbalanced-tuple-unpacking")]},
  408. ),
  409. "E0633": (
  410. "Attempting to unpack a non-sequence%s",
  411. "unpacking-non-sequence",
  412. "Used when something which is not a sequence is used in an unpack assignment",
  413. {"old_names": [("W0633", "old-unpacking-non-sequence")]},
  414. ),
  415. "W0640": (
  416. "Cell variable %s defined in loop",
  417. "cell-var-from-loop",
  418. "A variable used in a closure is defined in a loop. "
  419. "This will result in all closures using the same value for "
  420. "the closed-over variable.",
  421. ),
  422. "W0641": (
  423. "Possibly unused variable %r",
  424. "possibly-unused-variable",
  425. "Used when a variable is defined but might not be used. "
  426. "The possibility comes from the fact that locals() might be used, "
  427. "which could consume or not the said variable",
  428. ),
  429. "W0642": (
  430. "Invalid assignment to %s in method",
  431. "self-cls-assignment",
  432. "Invalid assignment to self or cls in instance or class method "
  433. "respectively.",
  434. ),
  435. "E0643": (
  436. "Invalid index for iterable length",
  437. "potential-index-error",
  438. "Emitted when an index used on an iterable goes beyond the length of that "
  439. "iterable.",
  440. ),
  441. "W0644": (
  442. "Possible unbalanced dict unpacking with %s: "
  443. "left side has %d label%s, right side has %d value%s",
  444. "unbalanced-dict-unpacking",
  445. "Used when there is an unbalanced dict unpacking in assignment or for loop",
  446. ),
  447. }
  448. class NamesConsumer:
  449. """A simple class to handle consumed, to consume and scope type info of node locals."""
  450. node: nodes.NodeNG
  451. scope_type: str
  452. to_consume: Consumption
  453. consumed: Consumption
  454. consumed_uncertain: Consumption
  455. """Retrieves nodes filtered out by get_next_to_consume() that may not
  456. have executed.
  457. These include nodes such as statements in except blocks, or statements
  458. in try blocks (when evaluating their corresponding except and finally
  459. blocks). Checkers that want to treat the statements as executed
  460. (e.g. for unused-variable) may need to add them back.
  461. """
  462. def __init__(self, node: nodes.NodeNG, scope_type: str):
  463. self.node = node
  464. self.scope_type = scope_type
  465. self.to_consume = copy.copy(node.locals)
  466. self.consumed = {}
  467. self.consumed_uncertain = defaultdict(list)
  468. self.names_under_always_false_test: set[str] = set()
  469. self.names_defined_under_one_branch_only: set[str] = set()
  470. def __repr__(self) -> str:
  471. _to_consumes = [f"{k}->{v}" for k, v in self.to_consume.items()]
  472. _consumed = [f"{k}->{v}" for k, v in self.consumed.items()]
  473. _consumed_uncertain = [f"{k}->{v}" for k, v in self.consumed_uncertain.items()]
  474. to_consumes = ", ".join(_to_consumes)
  475. consumed = ", ".join(_consumed)
  476. consumed_uncertain = ", ".join(_consumed_uncertain)
  477. return f"""
  478. to_consume : {to_consumes}
  479. consumed : {consumed}
  480. consumed_uncertain: {consumed_uncertain}
  481. scope_type : {self.scope_type}
  482. """
  483. def mark_as_consumed(self, name: str, consumed_nodes: list[nodes.NodeNG]) -> None:
  484. """Mark the given nodes as consumed for the name.
  485. If all of the nodes for the name were consumed, delete the name from
  486. the to_consume dictionary
  487. """
  488. unconsumed = [n for n in self.to_consume[name] if n not in set(consumed_nodes)]
  489. self.consumed[name] = consumed_nodes
  490. if unconsumed:
  491. self.to_consume[name] = unconsumed
  492. else:
  493. del self.to_consume[name]
  494. def get_next_to_consume(self, node: nodes.Name) -> list[nodes.NodeNG] | None:
  495. """Return a list of the nodes that define `node` from this scope.
  496. If it is uncertain whether a node will be consumed, such as for statements in
  497. except blocks, add it to self.consumed_uncertain instead of returning it.
  498. Return None to indicate a special case that needs to be handled by the caller.
  499. """
  500. name = node.name
  501. parent_node = node.parent
  502. found_nodes = self.to_consume.get(name)
  503. node_statement = node.statement()
  504. if (
  505. found_nodes
  506. and isinstance(parent_node, nodes.Assign)
  507. and parent_node == found_nodes[0].parent
  508. ):
  509. lhs = found_nodes[0].parent.targets[0]
  510. if (
  511. isinstance(lhs, nodes.AssignName) and lhs.name == name
  512. ): # this name is defined in this very statement
  513. found_nodes = None
  514. if (
  515. found_nodes
  516. and isinstance(parent_node, nodes.For)
  517. and parent_node.iter == node
  518. and parent_node.target in found_nodes
  519. ):
  520. # Only filter out the for-loop target if there are other definitions besides the target
  521. other_definitions = [fn for fn in found_nodes if fn != parent_node.target]
  522. if other_definitions:
  523. found_nodes = other_definitions
  524. else:
  525. found_nodes = None
  526. # Before filtering, check that this node's name is not a nonlocal
  527. if _is_nonlocal_name(node, node.frame()):
  528. return found_nodes
  529. # And no comprehension is under the node's frame
  530. if VariablesChecker._comprehension_between_frame_and_node(node):
  531. return found_nodes
  532. # Filter out assignments in ExceptHandlers that node is not contained in
  533. if found_nodes:
  534. found_nodes = [
  535. n
  536. for n in found_nodes
  537. if not isinstance(n.statement(), nodes.ExceptHandler)
  538. or n.statement().parent_of(node)
  539. ]
  540. # Filter out assignments guarded by always false conditions
  541. if found_nodes:
  542. uncertain_nodes = self._uncertain_nodes_if_tests(found_nodes, node)
  543. self.consumed_uncertain[node.name] += uncertain_nodes
  544. uncertain_nodes_set = set(uncertain_nodes)
  545. found_nodes = [n for n in found_nodes if n not in uncertain_nodes_set]
  546. # Filter out assignments in an Except clause that the node is not
  547. # contained in, assuming they may fail
  548. if found_nodes:
  549. uncertain_nodes = self._uncertain_nodes_in_except_blocks(
  550. found_nodes, node, node_statement
  551. )
  552. self.consumed_uncertain[node.name] += uncertain_nodes
  553. uncertain_nodes_set = set(uncertain_nodes)
  554. found_nodes = [n for n in found_nodes if n not in uncertain_nodes_set]
  555. # If this node is in a Finally block of a Try/Finally,
  556. # filter out assignments in the try portion, assuming they may fail
  557. if found_nodes:
  558. uncertain_nodes = (
  559. self._uncertain_nodes_in_try_blocks_when_evaluating_finally_blocks(
  560. found_nodes, node_statement, name
  561. )
  562. )
  563. self.consumed_uncertain[node.name] += uncertain_nodes
  564. uncertain_nodes_set = set(uncertain_nodes)
  565. found_nodes = [n for n in found_nodes if n not in uncertain_nodes_set]
  566. # If this node is in an ExceptHandler,
  567. # filter out assignments in the try portion, assuming they may fail
  568. if found_nodes:
  569. uncertain_nodes = (
  570. self._uncertain_nodes_in_try_blocks_when_evaluating_except_blocks(
  571. found_nodes, node_statement
  572. )
  573. )
  574. self.consumed_uncertain[node.name] += uncertain_nodes
  575. uncertain_nodes_set = set(uncertain_nodes)
  576. found_nodes = [n for n in found_nodes if n not in uncertain_nodes_set]
  577. return found_nodes
  578. def _inferred_to_define_name_raise_or_return(
  579. self,
  580. name: str,
  581. node: nodes.Try | nodes.With | nodes.For | nodes.While | nodes.Match | nodes.If,
  582. ) -> bool:
  583. """Return True if there is a path under this `if_node`
  584. that is inferred to define `name`, raise, or return.
  585. """
  586. match node:
  587. case nodes.Try():
  588. # Allow either a path through try/else/finally OR a path through ALL except handlers
  589. try_except_node = node
  590. if node.finalbody:
  591. try_except_node = next(
  592. (child for child in node.nodes_of_class(nodes.Try)),
  593. None,
  594. )
  595. handlers = try_except_node.handlers if try_except_node else []
  596. return NamesConsumer._defines_name_raises_or_returns_recursive(
  597. name, node
  598. ) or all(
  599. NamesConsumer._defines_name_raises_or_returns_recursive(
  600. name, handler
  601. )
  602. for handler in handlers
  603. )
  604. case nodes.With() | nodes.For() | nodes.While():
  605. return NamesConsumer._defines_name_raises_or_returns_recursive(
  606. name, node
  607. )
  608. case nodes.Match():
  609. return all(
  610. NamesConsumer._defines_name_raises_or_returns_recursive(name, case)
  611. for case in node.cases
  612. )
  613. case nodes.If():
  614. return self._inferred_to_define_name_raise_or_return_for_if_node(
  615. name, node
  616. )
  617. case _: # pragma: no cover
  618. # The function is only called for Try, With, For, While, Match and
  619. # If nodes. All of which are being handled above.
  620. raise AssertionError
  621. def _inferred_to_define_name_raise_or_return_for_if_node(
  622. self, name: str, node: nodes.If
  623. ) -> bool:
  624. # Be permissive if there is a break or a continue
  625. if any(node.nodes_of_class(nodes.Break, nodes.Continue)):
  626. return True
  627. # Is there an assignment in this node itself, e.g. in named expression?
  628. if NamesConsumer._defines_name_raises_or_returns(name, node):
  629. return True
  630. test = node.test.value if isinstance(node.test, nodes.NamedExpr) else node.test
  631. all_inferred = utils.infer_all(test)
  632. only_search_if = False
  633. only_search_else = True
  634. for inferred in all_inferred:
  635. if not isinstance(inferred, nodes.Const):
  636. only_search_else = False
  637. continue
  638. val = inferred.value
  639. only_search_if = only_search_if or (val != NotImplemented and val)
  640. only_search_else = only_search_else and not val
  641. # Only search else branch when test condition is inferred to be false
  642. if all_inferred and only_search_else:
  643. self.names_under_always_false_test.add(name)
  644. return self._branch_handles_name(name, node.orelse)
  645. # Search both if and else branches
  646. if_branch_handles = self._branch_handles_name(name, node.body)
  647. else_branch_handles = self._branch_handles_name(name, node.orelse)
  648. if if_branch_handles ^ else_branch_handles:
  649. self.names_defined_under_one_branch_only.add(name)
  650. elif name in self.names_defined_under_one_branch_only:
  651. self.names_defined_under_one_branch_only.remove(name)
  652. return if_branch_handles and else_branch_handles
  653. def _branch_handles_name(self, name: str, body: Iterable[nodes.NodeNG]) -> bool:
  654. return any(
  655. NamesConsumer._defines_name_raises_or_returns(name, if_body_stmt)
  656. or (
  657. isinstance(
  658. if_body_stmt,
  659. (
  660. nodes.If,
  661. nodes.Try,
  662. nodes.With,
  663. nodes.For,
  664. nodes.While,
  665. nodes.Match,
  666. ),
  667. )
  668. and self._inferred_to_define_name_raise_or_return(name, if_body_stmt)
  669. )
  670. for if_body_stmt in body
  671. )
  672. def _uncertain_nodes_if_tests(
  673. self,
  674. found_nodes: list[nodes.NodeNG],
  675. node: nodes.NodeNG,
  676. ) -> list[nodes.NodeNG]:
  677. """Identify nodes of uncertain execution because they are defined under if
  678. tests.
  679. Don't identify a node if there is a path that is inferred to
  680. define the name, raise, or return (e.g. any executed if/elif/else branch).
  681. """
  682. uncertain_nodes = []
  683. for other_node in found_nodes:
  684. match other_node:
  685. case nodes.AssignName():
  686. name = other_node.name
  687. case nodes.Import() | nodes.ImportFrom():
  688. name = node.name
  689. case nodes.FunctionDef() | nodes.ClassDef():
  690. name = other_node.name
  691. case _:
  692. continue
  693. all_if = [
  694. n
  695. for n in other_node.node_ancestors()
  696. if isinstance(n, nodes.If) and not n.parent_of(node)
  697. ]
  698. if not all_if:
  699. continue
  700. closest_if = all_if[0]
  701. if (
  702. isinstance(node, nodes.AssignName)
  703. and node.frame() is not closest_if.frame()
  704. ):
  705. continue
  706. if closest_if.parent_of(node):
  707. continue
  708. outer_if = all_if[-1]
  709. if NamesConsumer._node_guarded_by_same_test(node, outer_if):
  710. continue
  711. # Name defined in the if/else control flow
  712. if self._inferred_to_define_name_raise_or_return(name, outer_if):
  713. continue
  714. uncertain_nodes.append(other_node)
  715. return uncertain_nodes
  716. @staticmethod
  717. def _node_guarded_by_same_test(node: nodes.NodeNG, other_if: nodes.If) -> bool:
  718. """Identify if `node` is guarded by an equivalent test as `other_if`.
  719. Two tests are equivalent if their string representations are identical
  720. or if their inferred values consist only of constants and those constants
  721. are identical, and the if test guarding `node` is not a Name.
  722. """
  723. if isinstance(other_if.test, nodes.NamedExpr):
  724. other_if_test = other_if.test.target
  725. else:
  726. other_if_test = other_if.test
  727. other_if_test_as_string = other_if_test.as_string()
  728. other_if_test_all_inferred = utils.infer_all(other_if_test)
  729. for ancestor in node.node_ancestors():
  730. if not isinstance(ancestor, (nodes.If, nodes.IfExp)):
  731. continue
  732. if ancestor.test.as_string() == other_if_test_as_string:
  733. return True
  734. if isinstance(ancestor.test, nodes.Name):
  735. continue
  736. all_inferred = utils.infer_all(ancestor.test)
  737. if len(all_inferred) == len(other_if_test_all_inferred):
  738. if any(
  739. not isinstance(test, nodes.Const)
  740. for test in (*all_inferred, *other_if_test_all_inferred)
  741. ):
  742. continue
  743. if {test.value for test in all_inferred} != {
  744. test.value for test in other_if_test_all_inferred
  745. }:
  746. continue
  747. return True
  748. return False
  749. @staticmethod
  750. def _uncertain_nodes_in_except_blocks(
  751. found_nodes: list[nodes.NodeNG],
  752. node: nodes.NodeNG,
  753. node_statement: _base_nodes.Statement,
  754. ) -> list[nodes.NodeNG]:
  755. """Return any nodes in ``found_nodes`` that should be treated as uncertain
  756. because they are in an except block.
  757. """
  758. uncertain_nodes = []
  759. for other_node in found_nodes:
  760. other_node_statement = other_node.statement()
  761. # Only testing for statements in the except block of Try
  762. closest_except_handler = utils.get_node_first_ancestor_of_type(
  763. other_node_statement, nodes.ExceptHandler
  764. )
  765. if not closest_except_handler:
  766. continue
  767. # If the other node is in the same scope as this node, assume it executes
  768. if closest_except_handler.parent_of(node):
  769. continue
  770. closest_try_except: nodes.Try = closest_except_handler.parent
  771. # If the try or else blocks return, assume the except blocks execute.
  772. try_block_returns = any(
  773. isinstance(try_statement, nodes.Return)
  774. for try_statement in closest_try_except.body
  775. )
  776. else_block_returns = any(
  777. isinstance(else_statement, nodes.Return)
  778. for else_statement in closest_try_except.orelse
  779. )
  780. else_block_exits = any(
  781. isinstance(else_statement, nodes.Expr)
  782. and isinstance(else_statement.value, nodes.Call)
  783. and utils.is_terminating_func(else_statement.value)
  784. for else_statement in closest_try_except.orelse
  785. )
  786. else_block_continues = any(
  787. isinstance(else_statement, nodes.Continue)
  788. for else_statement in closest_try_except.orelse
  789. )
  790. if (
  791. else_block_continues
  792. and isinstance(node_statement.parent, (nodes.For, nodes.While))
  793. and closest_try_except.parent.parent_of(node_statement)
  794. ):
  795. continue
  796. if try_block_returns or else_block_returns or else_block_exits:
  797. # Exception: if this node is in the final block of the other_node_statement,
  798. # it will execute before returning. Assume the except statements are uncertain.
  799. if (
  800. isinstance(node_statement.parent, nodes.Try)
  801. and node_statement in node_statement.parent.finalbody
  802. and closest_try_except.parent.parent_of(node_statement)
  803. ):
  804. uncertain_nodes.append(other_node)
  805. # Or the node_statement is in the else block of the relevant Try
  806. elif (
  807. isinstance(node_statement.parent, nodes.Try)
  808. and node_statement in node_statement.parent.orelse
  809. and closest_try_except.parent.parent_of(node_statement)
  810. ):
  811. uncertain_nodes.append(other_node)
  812. # Assume the except blocks execute, so long as each handler
  813. # defines the name, raises, or returns.
  814. elif all(
  815. NamesConsumer._defines_name_raises_or_returns_recursive(
  816. node.name, handler
  817. )
  818. for handler in closest_try_except.handlers
  819. ):
  820. continue
  821. if NamesConsumer._check_loop_finishes_via_except(node, closest_try_except):
  822. continue
  823. # Passed all tests for uncertain execution
  824. uncertain_nodes.append(other_node)
  825. return uncertain_nodes
  826. @staticmethod
  827. def _defines_name_raises_or_returns(name: str, node: nodes.NodeNG) -> bool:
  828. if isinstance(node, (nodes.Raise, nodes.Assert, nodes.Return, nodes.Continue)):
  829. return True
  830. if isinstance(node, nodes.Expr) and isinstance(node.value, nodes.Call):
  831. if utils.is_terminating_func(node.value):
  832. return True
  833. if (
  834. isinstance(node.value.func, nodes.Name)
  835. and node.value.func.name == "assert_never"
  836. ):
  837. return True
  838. if (
  839. isinstance(node, nodes.AnnAssign)
  840. and node.value
  841. and isinstance(node.target, nodes.AssignName)
  842. and node.target.name == name
  843. ):
  844. return True
  845. if isinstance(node, nodes.Assign):
  846. for target in node.targets:
  847. for elt in utils.get_all_elements(target):
  848. if isinstance(elt, nodes.Starred):
  849. elt = elt.value
  850. if isinstance(elt, nodes.AssignName) and elt.name == name:
  851. return True
  852. if isinstance(node, nodes.If):
  853. if any(
  854. child_named_expr.target.name == name
  855. for child_named_expr in node.nodes_of_class(nodes.NamedExpr)
  856. ):
  857. return True
  858. if isinstance(node, (nodes.Import, nodes.ImportFrom)) and any(
  859. (node_name[1] and node_name[1] == name)
  860. or (node_name[0] == name)
  861. or (node_name[0].startswith(name + "."))
  862. for node_name in node.names
  863. ):
  864. return True
  865. if isinstance(node, nodes.With) and any(
  866. isinstance(item[1], nodes.AssignName) and item[1].name == name
  867. for item in node.items
  868. ):
  869. return True
  870. if isinstance(node, (nodes.ClassDef, nodes.FunctionDef)) and node.name == name:
  871. return True
  872. if (
  873. isinstance(node, nodes.ExceptHandler)
  874. and node.name
  875. and node.name.name == name
  876. ):
  877. return True
  878. return False
  879. @staticmethod
  880. def _defines_name_raises_or_returns_recursive(
  881. name: str,
  882. node: nodes.NodeNG,
  883. ) -> bool:
  884. """Return True if some child of `node` defines the name `name`,
  885. raises, or returns.
  886. """
  887. for stmt in node.get_children():
  888. if NamesConsumer._defines_name_raises_or_returns(name, stmt):
  889. return True
  890. match stmt:
  891. case nodes.If() | nodes.With():
  892. if any(
  893. NamesConsumer._defines_name_raises_or_returns(name, nested_stmt)
  894. for nested_stmt in stmt.get_children()
  895. ):
  896. return True
  897. case nodes.Try() if (
  898. not stmt.finalbody
  899. and NamesConsumer._defines_name_raises_or_returns_recursive(
  900. name, stmt
  901. )
  902. ):
  903. return True
  904. case nodes.Match():
  905. return all(
  906. NamesConsumer._defines_name_raises_or_returns_recursive(
  907. name, case
  908. )
  909. for case in stmt.cases
  910. )
  911. return False
  912. @staticmethod
  913. def _check_loop_finishes_via_except(
  914. node: nodes.NodeNG,
  915. other_node_try_except: nodes.Try,
  916. ) -> bool:
  917. """Check for a specific control flow scenario.
  918. Described in https://github.com/pylint-dev/pylint/issues/5683.
  919. A scenario where the only non-break exit from a loop consists of the very
  920. except handler we are examining, such that code in the `else` branch of
  921. the loop can depend on it being assigned.
  922. Example:
  923. for _ in range(3):
  924. try:
  925. do_something()
  926. except:
  927. name = 1 <-- only non-break exit from loop
  928. else:
  929. break
  930. else:
  931. print(name)
  932. """
  933. if not other_node_try_except.orelse:
  934. return False
  935. closest_loop: nodes.For | nodes.While | None = (
  936. utils.get_node_first_ancestor_of_type(node, (nodes.For, nodes.While))
  937. )
  938. if closest_loop is None:
  939. return False
  940. if not any(
  941. else_statement is node or else_statement.parent_of(node)
  942. for else_statement in closest_loop.orelse
  943. ):
  944. # `node` not guarded by `else`
  945. return False
  946. for inner_else_statement in other_node_try_except.orelse:
  947. if isinstance(inner_else_statement, nodes.Break):
  948. break_stmt = inner_else_statement
  949. break
  950. else:
  951. # No break statement
  952. return False
  953. def _try_in_loop_body(
  954. other_node_try_except: nodes.Try,
  955. loop: nodes.For | nodes.While,
  956. ) -> bool:
  957. """Return True if `other_node_try_except` is a descendant of `loop`."""
  958. return any(
  959. loop_body_statement is other_node_try_except
  960. or loop_body_statement.parent_of(other_node_try_except)
  961. for loop_body_statement in loop.body
  962. )
  963. if not _try_in_loop_body(other_node_try_except, closest_loop):
  964. for ancestor in closest_loop.node_ancestors():
  965. if isinstance(ancestor, (nodes.For, nodes.While)):
  966. if _try_in_loop_body(other_node_try_except, ancestor):
  967. break
  968. else:
  969. # `other_node_try_except` didn't have a shared ancestor loop
  970. return False
  971. for loop_stmt in closest_loop.body:
  972. if NamesConsumer._recursive_search_for_continue_before_break(
  973. loop_stmt, break_stmt
  974. ):
  975. break
  976. else:
  977. # No continue found, so we arrived at our special case!
  978. return True
  979. return False
  980. @staticmethod
  981. def _recursive_search_for_continue_before_break(
  982. stmt: _base_nodes.Statement,
  983. break_stmt: nodes.Break,
  984. ) -> bool:
  985. """Return True if any Continue node can be found in descendants of `stmt`
  986. before encountering `break_stmt`, ignoring any nested loops.
  987. """
  988. if stmt is break_stmt:
  989. return False
  990. if isinstance(stmt, nodes.Continue):
  991. return True
  992. for child in stmt.get_children():
  993. if isinstance(stmt, (nodes.For, nodes.While)):
  994. continue
  995. if NamesConsumer._recursive_search_for_continue_before_break(
  996. child, break_stmt
  997. ):
  998. return True
  999. return False
  1000. @staticmethod
  1001. def _uncertain_nodes_in_try_blocks_when_evaluating_except_blocks(
  1002. found_nodes: list[nodes.NodeNG],
  1003. node_statement: _base_nodes.Statement,
  1004. ) -> list[nodes.NodeNG]:
  1005. """Return any nodes in ``found_nodes`` that should be treated as uncertain.
  1006. Nodes are uncertain when they are in a try block and the ``node_statement``
  1007. being evaluated is in one of its except handlers.
  1008. """
  1009. uncertain_nodes: list[nodes.NodeNG] = []
  1010. closest_except_handler = utils.get_node_first_ancestor_of_type(
  1011. node_statement, nodes.ExceptHandler
  1012. )
  1013. if closest_except_handler is None:
  1014. return uncertain_nodes
  1015. for other_node in found_nodes:
  1016. other_node_statement = other_node.statement()
  1017. # If the other statement is the except handler guarding `node`, it executes
  1018. if other_node_statement is closest_except_handler:
  1019. continue
  1020. # Ensure other_node is in a try block
  1021. (
  1022. other_node_try_ancestor,
  1023. other_node_try_ancestor_visited_child,
  1024. ) = utils.get_node_first_ancestor_of_type_and_its_child(
  1025. other_node_statement, nodes.Try
  1026. )
  1027. if other_node_try_ancestor is None:
  1028. continue
  1029. if (
  1030. other_node_try_ancestor_visited_child
  1031. not in other_node_try_ancestor.body
  1032. ):
  1033. continue
  1034. # Make sure nesting is correct -- there should be at least one
  1035. # except handler that is a sibling attached to the try ancestor,
  1036. # or is an ancestor of the try ancestor.
  1037. if not any(
  1038. closest_except_handler in other_node_try_ancestor.handlers
  1039. or other_node_try_ancestor_except_handler
  1040. in closest_except_handler.node_ancestors()
  1041. for other_node_try_ancestor_except_handler in other_node_try_ancestor.handlers
  1042. ):
  1043. continue
  1044. # Passed all tests for uncertain execution
  1045. uncertain_nodes.append(other_node)
  1046. return uncertain_nodes
  1047. @staticmethod
  1048. def _uncertain_nodes_in_try_blocks_when_evaluating_finally_blocks(
  1049. found_nodes: list[nodes.NodeNG],
  1050. node_statement: _base_nodes.Statement,
  1051. name: str,
  1052. ) -> list[nodes.NodeNG]:
  1053. uncertain_nodes: list[nodes.NodeNG] = []
  1054. (
  1055. closest_try_finally_ancestor,
  1056. child_of_closest_try_finally_ancestor,
  1057. ) = utils.get_node_first_ancestor_of_type_and_its_child(
  1058. node_statement, nodes.Try
  1059. )
  1060. if closest_try_finally_ancestor is None:
  1061. return uncertain_nodes
  1062. if (
  1063. child_of_closest_try_finally_ancestor
  1064. not in closest_try_finally_ancestor.finalbody
  1065. ):
  1066. return uncertain_nodes
  1067. for other_node in found_nodes:
  1068. other_node_statement = other_node.statement()
  1069. (
  1070. other_node_try_finally_ancestor,
  1071. child_of_other_node_try_finally_ancestor,
  1072. ) = utils.get_node_first_ancestor_of_type_and_its_child(
  1073. other_node_statement, nodes.Try
  1074. )
  1075. if other_node_try_finally_ancestor is None:
  1076. continue
  1077. # other_node needs to descend from the try of a try/finally.
  1078. if (
  1079. child_of_other_node_try_finally_ancestor
  1080. not in other_node_try_finally_ancestor.body
  1081. ):
  1082. continue
  1083. # If the two try/finally ancestors are not the same, then
  1084. # node_statement's closest try/finally ancestor needs to be in
  1085. # the final body of other_node's try/finally ancestor, or
  1086. # descend from one of the statements in that final body.
  1087. if (
  1088. other_node_try_finally_ancestor is not closest_try_finally_ancestor
  1089. and not any(
  1090. other_node_final_statement is closest_try_finally_ancestor
  1091. or other_node_final_statement.parent_of(
  1092. closest_try_finally_ancestor
  1093. )
  1094. for other_node_final_statement in other_node_try_finally_ancestor.finalbody
  1095. )
  1096. ):
  1097. continue
  1098. # Is the name defined in all exception clauses?
  1099. if other_node_try_finally_ancestor.handlers and all(
  1100. NamesConsumer._defines_name_raises_or_returns_recursive(name, handler)
  1101. for handler in other_node_try_finally_ancestor.handlers
  1102. ):
  1103. continue
  1104. # Passed all tests for uncertain execution
  1105. uncertain_nodes.append(other_node)
  1106. return uncertain_nodes
  1107. # pylint: disable=too-many-public-methods
  1108. class VariablesChecker(BaseChecker):
  1109. """BaseChecker for variables.
  1110. Checks for
  1111. * unused variables / imports
  1112. * undefined variables
  1113. * redefinition of variable from builtins or from an outer scope or except handler
  1114. * use of variable before assignment
  1115. * __all__ consistency
  1116. * self/cls assignment
  1117. """
  1118. name = "variables"
  1119. msgs = MSGS
  1120. options = (
  1121. (
  1122. "init-import",
  1123. {
  1124. "default": False,
  1125. "type": "yn",
  1126. "metavar": "<y or n>",
  1127. "help": "Tells whether we should check for unused import in "
  1128. "__init__ files.",
  1129. },
  1130. ),
  1131. (
  1132. "dummy-variables-rgx",
  1133. {
  1134. "default": "_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_",
  1135. "type": "regexp",
  1136. "metavar": "<regexp>",
  1137. "help": "A regular expression matching the name of dummy "
  1138. "variables (i.e. expected to not be used).",
  1139. },
  1140. ),
  1141. (
  1142. "additional-builtins",
  1143. {
  1144. "default": (),
  1145. "type": "csv",
  1146. "metavar": "<comma separated list>",
  1147. "help": "List of additional names supposed to be defined in "
  1148. "builtins. Remember that you should avoid defining new builtins "
  1149. "when possible.",
  1150. },
  1151. ),
  1152. (
  1153. "callbacks",
  1154. {
  1155. "default": ("cb_", "_cb"),
  1156. "type": "csv",
  1157. "metavar": "<callbacks>",
  1158. "help": "List of strings which can identify a callback "
  1159. "function by name. A callback name must start or "
  1160. "end with one of those strings.",
  1161. },
  1162. ),
  1163. (
  1164. "redefining-builtins-modules",
  1165. {
  1166. "default": (
  1167. "six.moves",
  1168. "past.builtins",
  1169. "future.builtins",
  1170. "builtins",
  1171. "io",
  1172. ),
  1173. "type": "csv",
  1174. "metavar": "<comma separated list>",
  1175. "help": "List of qualified module names which can have objects "
  1176. "that can redefine builtins.",
  1177. },
  1178. ),
  1179. (
  1180. "ignored-argument-names",
  1181. {
  1182. "default": IGNORED_ARGUMENT_NAMES,
  1183. "type": "regexp",
  1184. "metavar": "<regexp>",
  1185. "help": "Argument names that match this expression will be ignored.",
  1186. },
  1187. ),
  1188. (
  1189. "allow-global-unused-variables",
  1190. {
  1191. "default": True,
  1192. "type": "yn",
  1193. "metavar": "<y or n>",
  1194. "help": "Tells whether unused global variables should be treated as a violation.",
  1195. },
  1196. ),
  1197. (
  1198. "allowed-redefined-builtins",
  1199. {
  1200. "default": (),
  1201. "type": "csv",
  1202. "metavar": "<comma separated list>",
  1203. "help": "List of names allowed to shadow builtins",
  1204. },
  1205. ),
  1206. )
  1207. def __init__(self, linter: PyLinter) -> None:
  1208. super().__init__(linter)
  1209. self._to_consume: list[NamesConsumer] = []
  1210. self._type_annotation_names: list[str] = []
  1211. self._except_handler_names_queue: list[
  1212. tuple[nodes.ExceptHandler, nodes.AssignName]
  1213. ] = []
  1214. """This is a queue, last in first out."""
  1215. self._reported_type_checking_usage_scopes: dict[
  1216. str, list[nodes.LocalsDictNodeNG]
  1217. ] = {}
  1218. self._postponed_evaluation_enabled = False
  1219. def open(self) -> None:
  1220. py_version = self.linter.config.py_version
  1221. self._py314_plus = py_version >= (3, 14)
  1222. @utils.only_required_for_messages(
  1223. "unbalanced-dict-unpacking",
  1224. )
  1225. def visit_for(self, node: nodes.For) -> None:
  1226. if not isinstance(node.target, nodes.Tuple):
  1227. return
  1228. targets = node.target.elts
  1229. inferred = utils.safe_infer(node.iter)
  1230. if not isinstance(inferred, DICT_TYPES):
  1231. return
  1232. values = self._nodes_to_unpack(inferred)
  1233. if not values:
  1234. # no dict items returned
  1235. return
  1236. if isinstance(inferred, objects.DictItems):
  1237. # dict.items() is a bit special because values will be a tuple
  1238. # So as long as there are always 2 targets and values each are
  1239. # a tuple with two items, this will unpack correctly.
  1240. # Example: `for key, val in {1: 2, 3: 4}.items()`
  1241. if len(targets) == 2 and all(len(x.elts) == 2 for x in values):
  1242. return
  1243. # Starred nodes indicate ambiguous unpacking
  1244. # if `dict.items()` is used so we won't flag them.
  1245. if any(isinstance(target, nodes.Starred) for target in targets):
  1246. return
  1247. if isinstance(inferred, nodes.Dict):
  1248. if isinstance(node.iter, nodes.Name):
  1249. # If this a case of 'dict-items-missing-iter', we don't want to
  1250. # report it as an 'unbalanced-dict-unpacking' as well
  1251. # TODO (performance), merging both checks would streamline this
  1252. if len(targets) == 2:
  1253. return
  1254. else:
  1255. is_starred_targets = any(
  1256. isinstance(target, nodes.Starred) for target in targets
  1257. )
  1258. for value in values:
  1259. value_length = self._get_value_length(value)
  1260. is_valid_star_unpack = is_starred_targets and value_length >= len(
  1261. targets
  1262. )
  1263. if len(targets) != value_length and not is_valid_star_unpack:
  1264. details = _get_unpacking_extra_info(node, inferred)
  1265. self._report_unbalanced_unpacking(
  1266. node, inferred, targets, value_length, details
  1267. )
  1268. break
  1269. def leave_for(self, node: nodes.For) -> None:
  1270. self._store_type_annotation_names(node)
  1271. def visit_module(self, node: nodes.Module) -> None:
  1272. """Visit module : update consumption analysis variable
  1273. checks globals doesn't overrides builtins.
  1274. """
  1275. self._to_consume = [NamesConsumer(node, "module")]
  1276. self._postponed_evaluation_enabled = (
  1277. self._py314_plus or is_postponed_evaluation_enabled(node)
  1278. )
  1279. for name, stmts in node.locals.items():
  1280. if utils.is_builtin(name):
  1281. if self._should_ignore_redefined_builtin(stmts[0]) or name == "__doc__":
  1282. continue
  1283. self.add_message("redefined-builtin", args=name, node=stmts[0])
  1284. @utils.only_required_for_messages(
  1285. "unused-import",
  1286. "unused-wildcard-import",
  1287. "redefined-builtin",
  1288. "undefined-all-variable",
  1289. "invalid-all-object",
  1290. "invalid-all-format",
  1291. "unused-variable",
  1292. "undefined-variable",
  1293. )
  1294. def leave_module(self, node: nodes.Module) -> None:
  1295. """Leave module: check globals."""
  1296. assert len(self._to_consume) == 1
  1297. self._check_metaclasses(node)
  1298. not_consumed = self._to_consume.pop().to_consume
  1299. # attempt to check for __all__ if defined
  1300. if "__all__" in node.locals:
  1301. self._check_all(node, not_consumed)
  1302. # check for unused globals
  1303. self._check_globals(not_consumed)
  1304. # don't check unused imports in __init__ files
  1305. if not self.linter.config.init_import and node.package:
  1306. return
  1307. self._check_imports(not_consumed)
  1308. self._type_annotation_names = []
  1309. def visit_classdef(self, node: nodes.ClassDef) -> None:
  1310. """Visit class: update consumption analysis variable."""
  1311. self._to_consume.append(NamesConsumer(node, "class"))
  1312. def leave_classdef(self, node: nodes.ClassDef) -> None:
  1313. """Leave class: update consumption analysis variable."""
  1314. # Check for hidden ancestor names
  1315. # e.g. "six" in: Class X(six.with_metaclass(ABCMeta, object)):
  1316. for name_node in node.nodes_of_class(nodes.Name):
  1317. match name_node.parent:
  1318. case nodes.Call(func=nodes.Attribute(expr=nodes.Name(name=name))):
  1319. for consumer in self._to_consume:
  1320. if name in consumer.to_consume:
  1321. consumer.mark_as_consumed(name, consumer.to_consume[name])
  1322. break
  1323. self._to_consume.pop()
  1324. def visit_lambda(self, node: nodes.Lambda) -> None:
  1325. """Visit lambda: update consumption analysis variable."""
  1326. self._to_consume.append(NamesConsumer(node, "lambda"))
  1327. def leave_lambda(self, _: nodes.Lambda) -> None:
  1328. """Leave lambda: update consumption analysis variable."""
  1329. # do not check for not used locals here
  1330. self._to_consume.pop()
  1331. def visit_generatorexp(self, node: nodes.GeneratorExp) -> None:
  1332. """Visit genexpr: update consumption analysis variable."""
  1333. self._to_consume.append(NamesConsumer(node, "comprehension"))
  1334. def leave_generatorexp(self, _: nodes.GeneratorExp) -> None:
  1335. """Leave genexpr: update consumption analysis variable."""
  1336. # do not check for not used locals here
  1337. self._to_consume.pop()
  1338. def visit_dictcomp(self, node: nodes.DictComp) -> None:
  1339. """Visit dictcomp: update consumption analysis variable."""
  1340. self._to_consume.append(NamesConsumer(node, "comprehension"))
  1341. def leave_dictcomp(self, _: nodes.DictComp) -> None:
  1342. """Leave dictcomp: update consumption analysis variable."""
  1343. # do not check for not used locals here
  1344. self._to_consume.pop()
  1345. def visit_setcomp(self, node: nodes.SetComp) -> None:
  1346. """Visit setcomp: update consumption analysis variable."""
  1347. self._to_consume.append(NamesConsumer(node, "comprehension"))
  1348. def leave_setcomp(self, _: nodes.SetComp) -> None:
  1349. """Leave setcomp: update consumption analysis variable."""
  1350. # do not check for not used locals here
  1351. self._to_consume.pop()
  1352. def visit_functiondef(self, node: nodes.FunctionDef) -> None:
  1353. """Visit function: update consumption analysis variable and check locals."""
  1354. self._to_consume.append(NamesConsumer(node, "function"))
  1355. if not (
  1356. self.linter.is_message_enabled("redefined-outer-name")
  1357. or self.linter.is_message_enabled("redefined-builtin")
  1358. ):
  1359. return
  1360. globs = node.root().globals
  1361. for name, stmt in node.items():
  1362. if name in globs and not isinstance(stmt, nodes.Global):
  1363. definition = globs[name][0]
  1364. if (
  1365. isinstance(definition, nodes.ImportFrom)
  1366. and definition.modname == FUTURE
  1367. ):
  1368. # It is a __future__ directive, not a symbol.
  1369. continue
  1370. # Do not take in account redefined names for the purpose
  1371. # of type checking.:
  1372. if any(
  1373. in_type_checking_block(definition) for definition in globs[name]
  1374. ):
  1375. continue
  1376. # Suppress emitting the message if the outer name is in the
  1377. # scope of an exception assignment.
  1378. # For example: the `e` in `except ValueError as e`
  1379. match globs[name][0]:
  1380. case nodes.AssignName(parent=nodes.ExceptHandler()):
  1381. continue
  1382. line = definition.fromlineno
  1383. if not self._is_name_ignored(stmt, name):
  1384. self.add_message(
  1385. "redefined-outer-name", args=(name, line), node=stmt
  1386. )
  1387. elif (
  1388. utils.is_builtin(name)
  1389. and not self._allowed_redefined_builtin(name)
  1390. and not self._should_ignore_redefined_builtin(stmt)
  1391. ):
  1392. # do not print Redefining builtin for additional builtins
  1393. self.add_message("redefined-builtin", args=name, node=stmt)
  1394. def leave_functiondef(self, node: nodes.FunctionDef) -> None:
  1395. """Leave function: check function's locals are consumed."""
  1396. self._check_metaclasses(node)
  1397. if node.type_comment_returns:
  1398. self._store_type_annotation_node(node.type_comment_returns)
  1399. if node.type_comment_args:
  1400. for argument_annotation in node.type_comment_args:
  1401. self._store_type_annotation_node(argument_annotation)
  1402. not_consumed = self._to_consume.pop().to_consume
  1403. if not (
  1404. self.linter.is_message_enabled("unused-variable")
  1405. or self.linter.is_message_enabled("possibly-unused-variable")
  1406. or self.linter.is_message_enabled("unused-argument")
  1407. ):
  1408. return
  1409. # Don't check arguments of function which are only raising an exception.
  1410. if utils.is_error(node):
  1411. return
  1412. # Don't check arguments of abstract methods or within an interface.
  1413. is_method = node.is_method()
  1414. if is_method and node.is_abstract():
  1415. return
  1416. global_names = _flattened_scope_names(node.nodes_of_class(nodes.Global))
  1417. nonlocal_names = _flattened_scope_names(node.nodes_of_class(nodes.Nonlocal))
  1418. comprehension_target_names: set[str] = set()
  1419. for comprehension_scope in node.nodes_of_class(nodes.ComprehensionScope):
  1420. for generator in comprehension_scope.generators:
  1421. for name in utils.find_assigned_names_recursive(generator.target):
  1422. comprehension_target_names.add(name)
  1423. for name, stmts in not_consumed.items():
  1424. self._check_is_unused(
  1425. name,
  1426. node,
  1427. stmts[0],
  1428. global_names,
  1429. nonlocal_names,
  1430. comprehension_target_names,
  1431. )
  1432. visit_asyncfunctiondef = visit_functiondef
  1433. leave_asyncfunctiondef = leave_functiondef
  1434. @utils.only_required_for_messages(
  1435. "global-variable-undefined",
  1436. "global-variable-not-assigned",
  1437. "global-statement",
  1438. "global-at-module-level",
  1439. "redefined-builtin",
  1440. )
  1441. def visit_global(self, node: nodes.Global) -> None:
  1442. """Check names imported exists in the global scope."""
  1443. frame = node.frame()
  1444. if isinstance(frame, nodes.Module):
  1445. self.add_message("global-at-module-level", node=node, confidence=HIGH)
  1446. return
  1447. module = frame.root()
  1448. default_message = True
  1449. module_locals = node.root().locals
  1450. for name in node.names:
  1451. try:
  1452. assign_nodes = module.getattr(name)
  1453. except astroid.NotFoundError:
  1454. # unassigned global, skip
  1455. assign_nodes = []
  1456. not_defined_locally_by_import = not any(
  1457. isinstance(local, (nodes.Import, nodes.ImportFrom))
  1458. for local in module_locals.get(name, ())
  1459. )
  1460. if (
  1461. not utils.is_reassigned_after_current(node, name)
  1462. and not utils.is_deleted_after_current(node, name)
  1463. and not_defined_locally_by_import
  1464. ):
  1465. self.add_message(
  1466. "global-variable-not-assigned",
  1467. args=name,
  1468. node=node,
  1469. confidence=HIGH,
  1470. )
  1471. default_message = False
  1472. continue
  1473. for anode in assign_nodes:
  1474. if (
  1475. isinstance(anode, nodes.AssignName)
  1476. and anode.name in module.special_attributes
  1477. ):
  1478. self.add_message("redefined-builtin", args=name, node=node)
  1479. break
  1480. if anode.frame() is module:
  1481. # module level assignment
  1482. break
  1483. if (
  1484. isinstance(anode, (nodes.ClassDef, nodes.FunctionDef))
  1485. and anode.parent is module
  1486. ):
  1487. # module level function assignment
  1488. break
  1489. else:
  1490. if not_defined_locally_by_import:
  1491. # global undefined at the module scope
  1492. self.add_message(
  1493. "global-variable-undefined",
  1494. args=name,
  1495. node=node,
  1496. confidence=HIGH,
  1497. )
  1498. default_message = False
  1499. if default_message:
  1500. self.add_message("global-statement", node=node, confidence=HIGH)
  1501. def visit_assignname(self, node: nodes.AssignName) -> None:
  1502. if isinstance(node.assign_type(), nodes.AugAssign):
  1503. self.visit_name(node)
  1504. def visit_delname(self, node: nodes.DelName) -> None:
  1505. self.visit_name(node)
  1506. def visit_name(self, node: nodes.Name | nodes.AssignName | nodes.DelName) -> None:
  1507. """Don't add the 'utils.only_required_for_messages' decorator here!
  1508. It's important that all 'Name' nodes are visited, otherwise the
  1509. 'NamesConsumers' won't be correct.
  1510. """
  1511. stmt = node.statement()
  1512. if stmt.fromlineno is None:
  1513. # name node from an astroid built from live code, skip
  1514. assert not stmt.root().file.endswith(".py")
  1515. return
  1516. self._undefined_and_used_before_checker(node, stmt)
  1517. self._loopvar_name(node)
  1518. @utils.only_required_for_messages("redefined-outer-name")
  1519. def visit_excepthandler(self, node: nodes.ExceptHandler) -> None:
  1520. if not isinstance(node.name, nodes.AssignName):
  1521. return
  1522. for outer_except, outer_except_assign_name in self._except_handler_names_queue:
  1523. if node.name.name == outer_except_assign_name.name:
  1524. self.add_message(
  1525. "redefined-outer-name",
  1526. args=(outer_except_assign_name.name, outer_except.fromlineno),
  1527. node=node,
  1528. )
  1529. break
  1530. self._except_handler_names_queue.append((node, node.name))
  1531. @utils.only_required_for_messages("redefined-outer-name")
  1532. def leave_excepthandler(self, node: nodes.ExceptHandler) -> None:
  1533. if not (node.name and isinstance(node.name, nodes.AssignName)):
  1534. return
  1535. self._except_handler_names_queue.pop()
  1536. def _undefined_and_used_before_checker(
  1537. self,
  1538. node: nodes.Name,
  1539. stmt: nodes.NodeNG,
  1540. ) -> None:
  1541. frame = stmt.scope()
  1542. start_index = len(self._to_consume) - 1
  1543. # iterates through parent scopes, from the inner to the outer
  1544. base_scope_type = self._to_consume[start_index].scope_type
  1545. for i in range(start_index, -1, -1):
  1546. current_consumer = self._to_consume[i]
  1547. # Certain nodes shouldn't be checked as they get checked another time
  1548. if self._should_node_be_skipped(node, current_consumer, i == start_index):
  1549. continue
  1550. action, nodes_to_consume = self._check_consumer(
  1551. node, stmt, frame, current_consumer, base_scope_type
  1552. )
  1553. if nodes_to_consume:
  1554. # Any nodes added to consumed_uncertain by get_next_to_consume()
  1555. # should be added back so that they are marked as used.
  1556. # They will have already had a chance to emit used-before-assignment.
  1557. # We check here instead of before every single return in _check_consumer()
  1558. nodes_to_consume += current_consumer.consumed_uncertain[node.name]
  1559. current_consumer.mark_as_consumed(node.name, nodes_to_consume)
  1560. if action is VariableVisitConsumerAction.CONTINUE:
  1561. continue
  1562. if action is VariableVisitConsumerAction.RETURN:
  1563. return
  1564. # we have not found the name, if it isn't a builtin, that's an
  1565. # undefined name !
  1566. if not (
  1567. node.name in nodes.Module.scope_attrs
  1568. or utils.is_builtin(node.name)
  1569. or node.name in self.linter.config.additional_builtins
  1570. or (
  1571. node.name == "__class__"
  1572. and any(
  1573. i.is_method()
  1574. for i in node.node_ancestors()
  1575. if isinstance(i, nodes.FunctionDef)
  1576. )
  1577. )
  1578. ) and not utils.node_ignores_exception(node, NameError):
  1579. self.add_message("undefined-variable", args=node.name, node=node)
  1580. def _should_node_be_skipped(
  1581. self,
  1582. node: nodes.Name,
  1583. consumer: NamesConsumer,
  1584. is_start_index: bool,
  1585. ) -> bool:
  1586. """Tests a consumer and node for various conditions in which the node shouldn't
  1587. be checked for the undefined-variable and used-before-assignment checks.
  1588. """
  1589. if consumer.scope_type == "class":
  1590. # The list of base classes in the class definition is not part
  1591. # of the class body.
  1592. # If the current scope is a class scope but it's not the inner
  1593. # scope, ignore it. This prevents to access this scope instead of
  1594. # the globals one in function members when there are some common
  1595. # names.
  1596. if utils.is_ancestor_name(consumer.node, node) or (
  1597. not is_start_index and self._ignore_class_scope(node)
  1598. ):
  1599. if any(
  1600. node.name == param.name.name for param in consumer.node.type_params
  1601. ):
  1602. return False
  1603. return True
  1604. match node.parent:
  1605. case nodes.Keyword(parent=nodes.ClassDef()):
  1606. # Ignore inner class scope for keywords in class definition
  1607. return True
  1608. elif consumer.scope_type == "function" and self._defined_in_function_definition(
  1609. node, consumer.node
  1610. ):
  1611. if any(node.name == param.name.name for param in consumer.node.type_params):
  1612. return False
  1613. # If the name node is used as a function default argument's value or as
  1614. # a decorator, then start from the parent frame of the function instead
  1615. # of the function frame - and thus open an inner class scope
  1616. return True
  1617. elif consumer.scope_type == "lambda" and utils.is_default_argument(
  1618. node, consumer.node
  1619. ):
  1620. return True
  1621. return False
  1622. # pylint: disable = too-many-return-statements, too-many-branches
  1623. def _check_consumer(
  1624. self,
  1625. node: nodes.Name,
  1626. stmt: nodes.NodeNG,
  1627. frame: nodes.LocalsDictNodeNG,
  1628. current_consumer: NamesConsumer,
  1629. base_scope_type: str,
  1630. ) -> tuple[VariableVisitConsumerAction, list[nodes.NodeNG] | None]:
  1631. """Checks a consumer for conditions that should trigger messages."""
  1632. # If the name has already been consumed, only check it's not a loop
  1633. # variable used outside the loop.
  1634. if node.name in current_consumer.consumed:
  1635. # Avoid the case where there are homonyms inside function scope and
  1636. # comprehension current scope (avoid bug #1731)
  1637. if utils.is_func_decorator(current_consumer.node) or not isinstance(
  1638. node, nodes.ComprehensionScope
  1639. ):
  1640. self._check_late_binding_closure(node)
  1641. return (VariableVisitConsumerAction.RETURN, None)
  1642. found_nodes = current_consumer.get_next_to_consume(node)
  1643. if found_nodes is None:
  1644. return (VariableVisitConsumerAction.CONTINUE, None)
  1645. if not found_nodes:
  1646. is_reported = self._report_unfound_name_definition(node, current_consumer)
  1647. # Mark for consumption any nodes added to consumed_uncertain by
  1648. # get_next_to_consume() because they might not have executed.
  1649. nodes_to_consume = current_consumer.consumed_uncertain[node.name]
  1650. nodes_to_consume = self._filter_type_checking_definitions_from_consumption(
  1651. node, nodes_to_consume, is_reported
  1652. )
  1653. return (
  1654. VariableVisitConsumerAction.RETURN,
  1655. nodes_to_consume,
  1656. )
  1657. self._check_late_binding_closure(node)
  1658. defnode = utils.assign_parent(found_nodes[0])
  1659. defstmt = defnode.statement()
  1660. defframe = defstmt.frame()
  1661. # The class reuses itself in the class scope.
  1662. is_recursive_klass: bool = (
  1663. frame is defframe
  1664. and defframe.parent_of(node)
  1665. and isinstance(defframe, nodes.ClassDef)
  1666. and node.name == defframe.name
  1667. )
  1668. if (
  1669. is_recursive_klass
  1670. and utils.get_node_first_ancestor_of_type(node, nodes.Lambda)
  1671. and not (
  1672. utils.is_default_argument(node)
  1673. and node.scope().parent.scope() is defframe
  1674. )
  1675. ):
  1676. # Self-referential class references are fine in lambda's --
  1677. # As long as they are not part of the default argument directly
  1678. # under the scope of the parent self-referring class.
  1679. # Example of valid default argument:
  1680. # class MyName3:
  1681. # myattr = 1
  1682. # mylambda3 = lambda: lambda a=MyName3: a
  1683. # Example of invalid default argument:
  1684. # class MyName4:
  1685. # myattr = 1
  1686. # mylambda4 = lambda a=MyName4: lambda: a
  1687. # If the above conditional is True,
  1688. # there is no possibility of undefined-variable
  1689. # Also do not consume class name
  1690. # (since consuming blocks subsequent checks)
  1691. # -- quit
  1692. return (VariableVisitConsumerAction.RETURN, None)
  1693. (
  1694. maybe_before_assign,
  1695. annotation_return,
  1696. use_outer_definition,
  1697. ) = self._is_variable_violation(
  1698. node,
  1699. defnode,
  1700. stmt,
  1701. defstmt,
  1702. frame,
  1703. defframe,
  1704. base_scope_type,
  1705. is_recursive_klass,
  1706. )
  1707. if use_outer_definition:
  1708. return (VariableVisitConsumerAction.CONTINUE, None)
  1709. if (
  1710. maybe_before_assign
  1711. and not utils.is_defined_before(node)
  1712. and not astroid.are_exclusive(stmt, defstmt, ("NameError",))
  1713. ):
  1714. # Used and defined in the same place, e.g `x += 1` and `del x`
  1715. defined_by_stmt = defstmt is stmt and isinstance(
  1716. node, (nodes.DelName, nodes.AssignName)
  1717. )
  1718. if (
  1719. is_recursive_klass
  1720. or defined_by_stmt
  1721. or annotation_return
  1722. or isinstance(defstmt, nodes.Delete)
  1723. ):
  1724. if not utils.node_ignores_exception(node, NameError):
  1725. # Handle postponed evaluation of annotations
  1726. if not (
  1727. self._postponed_evaluation_enabled
  1728. and isinstance(
  1729. stmt,
  1730. (
  1731. nodes.AnnAssign,
  1732. nodes.FunctionDef,
  1733. nodes.Arguments,
  1734. ),
  1735. )
  1736. and node.name in node.root().locals
  1737. ):
  1738. if defined_by_stmt:
  1739. return (VariableVisitConsumerAction.CONTINUE, [node])
  1740. return (VariableVisitConsumerAction.CONTINUE, None)
  1741. elif base_scope_type != "lambda":
  1742. # E0601 may *not* occurs in lambda scope.
  1743. # Skip postponed evaluation of annotations
  1744. # and unevaluated annotations inside a function body
  1745. # as well as TypeAlias nodes.
  1746. if not (
  1747. self._postponed_evaluation_enabled # noqa: RUF021
  1748. and (
  1749. isinstance(stmt, nodes.AnnAssign)
  1750. or isinstance(stmt, nodes.FunctionDef) # noqa: RUF021
  1751. and node
  1752. not in {
  1753. *(stmt.args.defaults or ()),
  1754. *(stmt.args.kw_defaults or ()),
  1755. }
  1756. )
  1757. or isinstance(stmt, nodes.AnnAssign) # noqa: RUF021
  1758. and utils.get_node_first_ancestor_of_type(stmt, nodes.FunctionDef)
  1759. or isinstance(stmt, nodes.TypeAlias)
  1760. ):
  1761. self.add_message(
  1762. "used-before-assignment",
  1763. args=node.name,
  1764. node=node,
  1765. confidence=HIGH,
  1766. )
  1767. return (VariableVisitConsumerAction.RETURN, found_nodes)
  1768. elif base_scope_type == "lambda":
  1769. # E0601 can occur in class-level scope in lambdas, as in
  1770. # the following example:
  1771. # class A:
  1772. # x = lambda attr: f + attr
  1773. # f = 42
  1774. # We check lineno because doing the following is fine:
  1775. # class A:
  1776. # x = 42
  1777. # y = lambda attr: x + attr
  1778. if (
  1779. isinstance(frame, nodes.ClassDef)
  1780. and node.name in frame.locals
  1781. and stmt.fromlineno <= defstmt.fromlineno
  1782. ):
  1783. self.add_message(
  1784. "used-before-assignment",
  1785. args=node.name,
  1786. node=node,
  1787. confidence=HIGH,
  1788. )
  1789. elif not self._is_builtin(node.name) and self._is_only_type_assignment(
  1790. node, defstmt
  1791. ):
  1792. if node.scope().locals.get(node.name):
  1793. self.add_message(
  1794. "used-before-assignment", args=node.name, node=node, confidence=HIGH
  1795. )
  1796. else:
  1797. self.add_message(
  1798. "undefined-variable", args=node.name, node=node, confidence=HIGH
  1799. )
  1800. return (VariableVisitConsumerAction.RETURN, found_nodes)
  1801. elif (
  1802. isinstance(defstmt, nodes.ClassDef) and defnode not in defframe.type_params
  1803. ):
  1804. return self._is_first_level_self_reference(node, defstmt, found_nodes)
  1805. elif isinstance(defnode, nodes.NamedExpr):
  1806. if isinstance(defnode.parent, nodes.IfExp):
  1807. if self._is_never_evaluated(defnode, defnode.parent):
  1808. self.add_message(
  1809. "undefined-variable",
  1810. args=node.name,
  1811. node=node,
  1812. confidence=INFERENCE,
  1813. )
  1814. return (VariableVisitConsumerAction.RETURN, found_nodes)
  1815. return (VariableVisitConsumerAction.RETURN, found_nodes)
  1816. def _report_unfound_name_definition(
  1817. self,
  1818. node: nodes.Name,
  1819. current_consumer: NamesConsumer,
  1820. ) -> bool:
  1821. """Reports used-before-assignment error when all name definition nodes
  1822. are filtered out by NamesConsumer.
  1823. Returns True if an error is reported; otherwise, returns False.
  1824. """
  1825. if (
  1826. self._postponed_evaluation_enabled
  1827. and utils.is_node_in_type_annotation_context(node)
  1828. ) or utils.is_node_in_pep695_type_context(node):
  1829. return False
  1830. if self._is_builtin(node.name):
  1831. return False
  1832. if self._is_variable_annotation_in_function(node):
  1833. return False
  1834. if self._has_nonlocal_in_enclosing_frame(
  1835. node, current_consumer.consumed_uncertain.get(node.name, [])
  1836. ):
  1837. return False
  1838. if (
  1839. node.name in self._reported_type_checking_usage_scopes
  1840. and node.scope() in self._reported_type_checking_usage_scopes[node.name]
  1841. ):
  1842. return False
  1843. confidence = HIGH
  1844. if node.name in current_consumer.names_under_always_false_test:
  1845. confidence = INFERENCE
  1846. elif node.name in current_consumer.consumed_uncertain:
  1847. confidence = CONTROL_FLOW
  1848. if node.name in current_consumer.names_defined_under_one_branch_only:
  1849. msg = "possibly-used-before-assignment"
  1850. else:
  1851. msg = "used-before-assignment"
  1852. self.add_message(
  1853. msg,
  1854. args=node.name,
  1855. node=node,
  1856. confidence=confidence,
  1857. )
  1858. return True
  1859. def _filter_type_checking_definitions_from_consumption(
  1860. self,
  1861. node: nodes.NodeNG,
  1862. nodes_to_consume: list[nodes.NodeNG],
  1863. is_reported: bool,
  1864. ) -> list[nodes.NodeNG]:
  1865. """Filters out type-checking definition nodes (e.g. imports, class definitions)
  1866. from consumption, as used-before-assignment may invoke in a different context.
  1867. If used-before-assignment is reported for the usage of a type-checking definition,
  1868. track the scope of that usage for future evaluation.
  1869. """
  1870. type_checking_definitions = {
  1871. n
  1872. for n in nodes_to_consume
  1873. if isinstance(n, (nodes.Import, nodes.ImportFrom, nodes.ClassDef))
  1874. and in_type_checking_block(n)
  1875. }
  1876. if type_checking_definitions and is_reported:
  1877. self._reported_type_checking_usage_scopes.setdefault(node.name, []).append(
  1878. node.scope()
  1879. )
  1880. return [n for n in nodes_to_consume if n not in type_checking_definitions]
  1881. @utils.only_required_for_messages("no-name-in-module")
  1882. def visit_import(self, node: nodes.Import) -> None:
  1883. """Check modules attribute accesses."""
  1884. if not self._analyse_fallback_blocks and utils.is_from_fallback_block(node):
  1885. # No need to verify this, since ImportError is already
  1886. # handled by the client code.
  1887. return
  1888. # Don't verify import if part of guarded import block
  1889. if in_type_checking_block(node):
  1890. return
  1891. if isinstance(node.parent, nodes.If) and is_sys_guard(node.parent):
  1892. return
  1893. for name, _ in node.names:
  1894. parts = name.split(".")
  1895. try:
  1896. module = next(_infer_name_module(node, parts[0]))
  1897. except astroid.ResolveError:
  1898. continue
  1899. if not isinstance(module, nodes.Module):
  1900. continue
  1901. self._check_module_attrs(node, module, parts[1:])
  1902. @utils.only_required_for_messages("no-name-in-module")
  1903. def visit_importfrom(self, node: nodes.ImportFrom) -> None:
  1904. """Check modules attribute accesses."""
  1905. if not self._analyse_fallback_blocks and utils.is_from_fallback_block(node):
  1906. # No need to verify this, since ImportError is already
  1907. # handled by the client code.
  1908. return
  1909. # Don't verify import if part of guarded import block
  1910. # I.e. `sys.version_info` or `typing.TYPE_CHECKING`
  1911. if in_type_checking_block(node):
  1912. return
  1913. if isinstance(node.parent, nodes.If) and is_sys_guard(node.parent):
  1914. return
  1915. name_parts = node.modname.split(".")
  1916. try:
  1917. module = node.do_import_module(name_parts[0])
  1918. except astroid.AstroidBuildingError:
  1919. return
  1920. module = self._check_module_attrs(node, module, name_parts[1:])
  1921. if not module:
  1922. return
  1923. for name, _ in node.names:
  1924. if name == "*":
  1925. continue
  1926. self._check_module_attrs(node, module, name.split("."))
  1927. @utils.only_required_for_messages(
  1928. "unbalanced-tuple-unpacking",
  1929. "unpacking-non-sequence",
  1930. "self-cls-assignment",
  1931. "unbalanced_dict_unpacking",
  1932. )
  1933. def visit_assign(self, node: nodes.Assign) -> None:
  1934. """Check unbalanced tuple unpacking for assignments and unpacking
  1935. non-sequences as well as in case self/cls get assigned.
  1936. """
  1937. self._check_self_cls_assign(node)
  1938. if not isinstance(node.targets[0], (nodes.Tuple, nodes.List)):
  1939. return
  1940. targets = node.targets[0].itered()
  1941. # Check if we have starred nodes.
  1942. if any(isinstance(target, nodes.Starred) for target in targets):
  1943. return
  1944. try:
  1945. inferred = node.value.inferred()
  1946. if inferred is not None and len(inferred) == 1:
  1947. self._check_unpacking(inferred[0], node, targets)
  1948. except astroid.InferenceError:
  1949. return
  1950. # listcomp have now also their scope
  1951. def visit_listcomp(self, node: nodes.ListComp) -> None:
  1952. """Visit listcomp: update consumption analysis variable."""
  1953. self._to_consume.append(NamesConsumer(node, "comprehension"))
  1954. def leave_listcomp(self, _: nodes.ListComp) -> None:
  1955. """Leave listcomp: update consumption analysis variable."""
  1956. # do not check for not used locals here
  1957. self._to_consume.pop()
  1958. def leave_assign(self, node: nodes.Assign) -> None:
  1959. self._store_type_annotation_names(node)
  1960. def leave_with(self, node: nodes.With) -> None:
  1961. self._store_type_annotation_names(node)
  1962. def visit_arguments(self, node: nodes.Arguments) -> None:
  1963. for annotation in node.type_comment_args:
  1964. self._store_type_annotation_node(annotation)
  1965. # Relying on other checker's options, which might not have been initialized yet.
  1966. @cached_property
  1967. def _analyse_fallback_blocks(self) -> bool:
  1968. return bool(self.linter.config.analyse_fallback_blocks)
  1969. @cached_property
  1970. def _ignored_modules(self) -> Iterable[str]:
  1971. return self.linter.config.ignored_modules # type: ignore[no-any-return]
  1972. @cached_property
  1973. def _allow_global_unused_variables(self) -> bool:
  1974. return bool(self.linter.config.allow_global_unused_variables)
  1975. @staticmethod
  1976. def _defined_in_function_definition(
  1977. node: nodes.NodeNG,
  1978. frame: nodes.NodeNG,
  1979. ) -> bool:
  1980. in_annotation_or_default_or_decorator = False
  1981. if isinstance(frame, nodes.FunctionDef) and node.statement() is frame:
  1982. in_annotation_or_default_or_decorator = (
  1983. (
  1984. node in frame.args.annotations
  1985. or node in frame.args.posonlyargs_annotations
  1986. or node in frame.args.kwonlyargs_annotations
  1987. or node is frame.args.varargannotation
  1988. or node is frame.args.kwargannotation
  1989. )
  1990. or frame.args.parent_of(node)
  1991. or (frame.decorators and frame.decorators.parent_of(node))
  1992. or (
  1993. frame.returns
  1994. and (node is frame.returns or frame.returns.parent_of(node))
  1995. )
  1996. )
  1997. return in_annotation_or_default_or_decorator
  1998. @staticmethod
  1999. def _in_lambda_or_comprehension_body(
  2000. node: nodes.NodeNG,
  2001. frame: nodes.NodeNG,
  2002. ) -> bool:
  2003. """Return True if node within a lambda/comprehension body (or similar) and thus
  2004. should not have access to class attributes in frame.
  2005. """
  2006. child = node
  2007. parent = node.parent
  2008. while parent is not None:
  2009. if parent is frame:
  2010. return False
  2011. match parent:
  2012. case nodes.Lambda() if child is not parent.args:
  2013. # Body of lambda should not have access to class attributes.
  2014. return True
  2015. case nodes.Comprehension() if child is not parent.iter:
  2016. # Only iter of list/set/dict/generator comprehension should have access.
  2017. return True
  2018. case nodes.ComprehensionScope() if not (
  2019. parent.generators and child is parent.generators[0]
  2020. ):
  2021. # Body of list/set/dict/generator comprehension should not have access to class attributes.
  2022. # Furthermore, only the first generator (if multiple) in comprehension should have access.
  2023. return True
  2024. child = parent
  2025. parent = parent.parent
  2026. return False
  2027. @staticmethod
  2028. def _is_variable_violation(
  2029. node: nodes.Name,
  2030. defnode: nodes.NodeNG,
  2031. stmt: _base_nodes.Statement,
  2032. defstmt: _base_nodes.Statement,
  2033. frame: nodes.LocalsDictNodeNG, # scope of statement of node
  2034. defframe: nodes.LocalsDictNodeNG,
  2035. base_scope_type: str,
  2036. is_recursive_klass: bool,
  2037. ) -> tuple[bool, bool, bool]:
  2038. maybe_before_assign = True
  2039. annotation_return = False
  2040. use_outer_definition = False
  2041. if frame is not defframe:
  2042. maybe_before_assign = _detect_global_scope(node, frame, defframe)
  2043. elif defframe.parent is None:
  2044. # we are at the module level, check the name is not
  2045. # defined in builtins
  2046. if (
  2047. node.name in defframe.scope_attrs
  2048. or astroid.builtin_lookup(node.name)[1]
  2049. ):
  2050. maybe_before_assign = False
  2051. else:
  2052. # we are in a local scope, check the name is not
  2053. # defined in global or builtin scope
  2054. # skip this lookup if name is assigned later in function scope/lambda
  2055. # Note: the node.frame() is not the same as the `frame` argument which is
  2056. # equivalent to frame.statement().scope()
  2057. forbid_lookup = (
  2058. isinstance(frame, nodes.FunctionDef)
  2059. or isinstance(node.frame(), nodes.Lambda)
  2060. ) and _assigned_locally(node)
  2061. if not forbid_lookup and defframe.root().lookup(node.name)[1]:
  2062. maybe_before_assign = False
  2063. use_outer_definition = stmt == defstmt and not isinstance(
  2064. defnode, nodes.Comprehension
  2065. )
  2066. # check if we have a nonlocal
  2067. elif node.name in defframe.locals:
  2068. maybe_before_assign = not _is_nonlocal_name(node, defframe)
  2069. if (
  2070. base_scope_type == "lambda"
  2071. and isinstance(frame, nodes.ClassDef)
  2072. and node.name in frame.locals
  2073. ):
  2074. # This rule verifies that if the definition node of the
  2075. # checked name is an Arguments node and if the name
  2076. # is used a default value in the arguments defaults
  2077. # and the actual definition of the variable label
  2078. # is happening before the Arguments definition.
  2079. #
  2080. # bar = None
  2081. # foo = lambda bar=bar: bar
  2082. #
  2083. # In this case, maybe_before_assign should be False, otherwise
  2084. # it should be True.
  2085. maybe_before_assign = not (
  2086. isinstance(defnode, nodes.Arguments)
  2087. and node in defnode.defaults
  2088. and frame.locals[node.name][0].fromlineno < defstmt.fromlineno
  2089. )
  2090. elif isinstance(defframe, nodes.ClassDef) and isinstance(
  2091. frame, nodes.FunctionDef
  2092. ):
  2093. # Special rules for function return annotations.
  2094. if node is frame.returns:
  2095. # Using a name defined earlier in the class containing the function.
  2096. if defframe.parent_of(frame.returns):
  2097. annotation_return = True
  2098. if frame.returns.name in defframe.locals:
  2099. definition = defframe.locals[node.name][0]
  2100. # no warning raised if a name was defined earlier in the class
  2101. maybe_before_assign = (
  2102. definition.lineno is not None
  2103. and definition.lineno >= frame.lineno
  2104. )
  2105. else:
  2106. maybe_before_assign = True
  2107. # Using a name defined in the module if this is a nested function.
  2108. elif (
  2109. # defframe is the class containing the function.
  2110. # It shouldn't be nested: expect its parent to be a module.
  2111. (defframe_parent := next(defframe.node_ancestors()))
  2112. and isinstance(defframe_parent, nodes.Module)
  2113. # frame is the function inside the class.
  2114. and (frame_ancestors := tuple(frame.node_ancestors()))
  2115. # Does that function have any functions as ancestors?
  2116. and any(
  2117. isinstance(ancestor, nodes.FunctionDef)
  2118. for ancestor in frame_ancestors
  2119. )
  2120. # And is its last ancestor the same module as the class's?
  2121. and frame_ancestors[-1] is defframe_parent
  2122. ):
  2123. annotation_return = True
  2124. maybe_before_assign = False
  2125. if isinstance(node.parent, nodes.Arguments):
  2126. maybe_before_assign = stmt.fromlineno <= defstmt.fromlineno
  2127. elif is_recursive_klass:
  2128. maybe_before_assign = True
  2129. else:
  2130. maybe_before_assign = (
  2131. maybe_before_assign and stmt.fromlineno <= defstmt.fromlineno
  2132. )
  2133. if maybe_before_assign and stmt.fromlineno == defstmt.fromlineno:
  2134. if (
  2135. isinstance(defframe, nodes.FunctionDef)
  2136. and frame is defframe
  2137. and defframe.parent_of(node)
  2138. and (
  2139. defnode in defframe.type_params
  2140. # Single statement function, with the statement on the
  2141. # same line as the function definition
  2142. or stmt is not defstmt
  2143. )
  2144. ):
  2145. maybe_before_assign = False
  2146. elif (
  2147. isinstance(defstmt, NODES_WITH_VALUE_ATTR)
  2148. and VariablesChecker._maybe_used_and_assigned_at_once(defstmt)
  2149. and frame is defframe
  2150. and defframe.parent_of(node)
  2151. and stmt is defstmt
  2152. ):
  2153. # Single statement if, with assignment expression on same
  2154. # line as assignment
  2155. # x = b if (b := True) else False
  2156. maybe_before_assign = False
  2157. elif (
  2158. isinstance(defnode, nodes.NamedExpr)
  2159. and frame is defframe
  2160. and defframe.parent_of(stmt)
  2161. and stmt is defstmt
  2162. and _is_before(defnode, node)
  2163. ):
  2164. # Relation of a name to the same name in a named expression
  2165. # Could be used before assignment if self-referencing:
  2166. # (b := b)
  2167. # Otherwise, safe if used after assignment:
  2168. # (b := 2) and b
  2169. maybe_before_assign = defnode.value is node or any(
  2170. a is defnode.value for a in node.node_ancestors()
  2171. )
  2172. elif (
  2173. isinstance(defframe, nodes.ClassDef)
  2174. and defnode in defframe.type_params
  2175. ):
  2176. # Generic on parent class:
  2177. # class Child[_T](Parent[_T])
  2178. maybe_before_assign = False
  2179. return maybe_before_assign, annotation_return, use_outer_definition
  2180. @staticmethod
  2181. def _maybe_used_and_assigned_at_once(defstmt: _base_nodes.Statement) -> bool:
  2182. """Check if `defstmt` has the potential to use and assign a name in the
  2183. same statement.
  2184. """
  2185. if isinstance(defstmt, nodes.Match):
  2186. return any(case.guard for case in defstmt.cases)
  2187. if isinstance(defstmt, nodes.IfExp):
  2188. return True
  2189. if isinstance(defstmt, nodes.TypeAlias):
  2190. return True
  2191. if isinstance(defstmt.value, nodes.BaseContainer):
  2192. return any(
  2193. VariablesChecker._maybe_used_and_assigned_at_once(elt)
  2194. for elt in defstmt.value.elts
  2195. if isinstance(elt, (*NODES_WITH_VALUE_ATTR, nodes.IfExp, nodes.Match))
  2196. )
  2197. match value := defstmt.value:
  2198. case nodes.IfExp():
  2199. return True
  2200. case nodes.Lambda(body=nodes.IfExp()):
  2201. return True
  2202. case nodes.Dict() if any(
  2203. isinstance(item[0], nodes.IfExp) or isinstance(item[1], nodes.IfExp)
  2204. for item in value.items
  2205. ):
  2206. return True
  2207. case nodes.Call():
  2208. pass
  2209. case _:
  2210. return False
  2211. return any(
  2212. any(isinstance(kwarg.value, nodes.IfExp) for kwarg in call.keywords)
  2213. or any(isinstance(arg, nodes.IfExp) for arg in call.args)
  2214. or (
  2215. isinstance(call.func, nodes.Attribute)
  2216. and isinstance(call.func.expr, nodes.IfExp)
  2217. )
  2218. for call in value.nodes_of_class(klass=nodes.Call)
  2219. )
  2220. def _is_builtin(self, name: str) -> bool:
  2221. return name in self.linter.config.additional_builtins or utils.is_builtin(name)
  2222. def _has_nonlocal_in_enclosing_frame(
  2223. self, node: nodes.Name, uncertain_definitions: list[nodes.NodeNG]
  2224. ) -> bool:
  2225. """Check if there is a nonlocal declaration in the nearest frame that encloses
  2226. both usage and definitions.
  2227. """
  2228. defining_frames = {definition.frame() for definition in uncertain_definitions}
  2229. frame = node.frame()
  2230. is_enclosing_frame = False
  2231. while frame and not is_enclosing_frame:
  2232. is_enclosing_frame = all(
  2233. (frame is defining_frame) or frame.parent_of(defining_frame)
  2234. for defining_frame in defining_frames
  2235. )
  2236. if is_enclosing_frame and _is_nonlocal_name(node, frame):
  2237. return True
  2238. frame = frame.parent.frame() if frame.parent else None
  2239. return False
  2240. @staticmethod
  2241. def _is_only_type_assignment(
  2242. node: nodes.Name,
  2243. defstmt: _base_nodes.Statement,
  2244. ) -> bool:
  2245. """Check if variable only gets assigned a type and never a value."""
  2246. if not (isinstance(defstmt, nodes.AnnAssign) and defstmt.value is None):
  2247. return False
  2248. defstmt_frame = defstmt.frame()
  2249. node_frame = node.frame()
  2250. parent = node
  2251. while parent not in {defstmt_frame.parent, None}:
  2252. parent_scope = parent.scope()
  2253. # Find out if any nonlocals receive values in nested functions
  2254. for inner_func in parent_scope.nodes_of_class(nodes.FunctionDef):
  2255. if inner_func is parent_scope:
  2256. continue
  2257. if any(
  2258. node.name in nl.names
  2259. for nl in inner_func.nodes_of_class(nodes.Nonlocal)
  2260. ) and any(
  2261. node.name == an.name
  2262. for an in inner_func.nodes_of_class(nodes.AssignName)
  2263. ):
  2264. return False
  2265. local_refs = parent_scope.locals.get(node.name, [])
  2266. for ref_node in local_refs:
  2267. # If local ref is in the same frame as our node, but on a later lineno
  2268. # we don't actually care about this local ref.
  2269. # Local refs are ordered, so we break.
  2270. # print(var)
  2271. # var = 1 # <- irrelevant
  2272. if defstmt_frame == node_frame and ref_node.lineno > node.lineno:
  2273. break
  2274. # If the parent of the local reference is anything but an AnnAssign
  2275. # Or if the AnnAssign adds a value the variable will now have a value
  2276. # var = 1 # OR
  2277. # var: int = 1
  2278. if (
  2279. not isinstance(ref_node.parent, nodes.AnnAssign)
  2280. or ref_node.parent.value
  2281. ) and not (
  2282. # EXCEPTION: will not have a value if a self-referencing named expression
  2283. # var: int
  2284. # if (var := var * var) <-- "var" still undefined
  2285. isinstance(ref_node.parent, nodes.NamedExpr)
  2286. and any(a is ref_node.parent.value for a in node.node_ancestors())
  2287. ):
  2288. return False
  2289. parent = parent_scope.parent
  2290. return True
  2291. def _is_first_level_self_reference(
  2292. self,
  2293. node: nodes.Name,
  2294. defstmt: nodes.ClassDef,
  2295. found_nodes: list[nodes.NodeNG],
  2296. ) -> tuple[VariableVisitConsumerAction, list[nodes.NodeNG] | None]:
  2297. """Check if a first level method's annotation or default values
  2298. refers to its own class, and return a consumer action.
  2299. """
  2300. if node.frame().parent == defstmt and node.statement() == node.frame():
  2301. # Check if used as type annotation
  2302. # Break if postponed evaluation is enabled
  2303. if utils.is_node_in_type_annotation_context(node):
  2304. if not self._postponed_evaluation_enabled:
  2305. return (VariableVisitConsumerAction.CONTINUE, None)
  2306. return (VariableVisitConsumerAction.RETURN, None)
  2307. # Check if used as default value by calling the class
  2308. match node.parent:
  2309. case nodes.Call(parent=nodes.Arguments()):
  2310. return (VariableVisitConsumerAction.CONTINUE, None)
  2311. return (VariableVisitConsumerAction.RETURN, found_nodes)
  2312. @staticmethod
  2313. def _is_never_evaluated(
  2314. defnode: nodes.NamedExpr,
  2315. defnode_parent: nodes.IfExp,
  2316. ) -> bool:
  2317. """Check if a NamedExpr is inside a side of if ... else that never
  2318. gets evaluated.
  2319. """
  2320. match utils.safe_infer(defnode_parent.test):
  2321. case nodes.Const(value=True) if defnode == defnode_parent.orelse:
  2322. return True
  2323. case nodes.Const(value=False) if defnode == defnode_parent.body:
  2324. return True
  2325. case _:
  2326. return False
  2327. @staticmethod
  2328. def _is_variable_annotation_in_function(node: nodes.Name) -> bool:
  2329. ann_assign = utils.get_node_first_ancestor_of_type(node, nodes.AnnAssign)
  2330. return (
  2331. ann_assign
  2332. and (node is ann_assign.annotation or ann_assign.annotation.parent_of(node))
  2333. and utils.get_node_first_ancestor_of_type( # type: ignore[return-value]
  2334. ann_assign, nodes.FunctionDef
  2335. )
  2336. )
  2337. def _ignore_class_scope(self, node: nodes.NodeNG) -> bool:
  2338. """Return True if the node is in a local class scope, as an assignment.
  2339. Detect if we are in a local class scope, as an assignment.
  2340. For example, the following is fair game.
  2341. class A:
  2342. b = 1
  2343. c = lambda b=b: b * b
  2344. class B:
  2345. tp = 1
  2346. def func(self, arg: tp):
  2347. ...
  2348. class C:
  2349. tp = 2
  2350. def func(self, arg=tp):
  2351. ...
  2352. class C:
  2353. class Tp:
  2354. pass
  2355. class D(Tp):
  2356. ...
  2357. """
  2358. name = node.name
  2359. frame = node.statement().scope()
  2360. in_annotation_or_default_or_decorator = self._defined_in_function_definition(
  2361. node, frame
  2362. )
  2363. in_ancestor_list = utils.is_ancestor_name(frame, node)
  2364. if in_annotation_or_default_or_decorator or in_ancestor_list:
  2365. frame_locals = frame.parent.scope().locals
  2366. else:
  2367. frame_locals = frame.locals
  2368. return not (
  2369. (isinstance(frame, nodes.ClassDef) or in_annotation_or_default_or_decorator)
  2370. and not self._in_lambda_or_comprehension_body(node, frame)
  2371. and name in frame_locals
  2372. )
  2373. # pylint: disable-next=too-many-branches,too-many-statements
  2374. def _loopvar_name(self, node: nodes.Name) -> None:
  2375. # filter variables according to node's scope
  2376. astmts = [s for s in node.lookup(node.name)[1] if hasattr(s, "assign_type")]
  2377. # If this variable usage exists inside a function definition
  2378. # that exists in the same loop,
  2379. # the usage is safe because the function will not be defined either if
  2380. # the variable is not defined.
  2381. scope = node.scope()
  2382. if isinstance(scope, (nodes.Lambda, nodes.FunctionDef)) and any(
  2383. asmt.scope().parent_of(scope) for asmt in astmts
  2384. ):
  2385. return
  2386. # Filter variables according to their respective scope. Test parent
  2387. # and statement to avoid #74747. This is not a total fix, which would
  2388. # introduce a mechanism similar to special attribute lookup in
  2389. # modules. Also, in order to get correct inference in this case, the
  2390. # scope lookup rules would need to be changed to return the initial
  2391. # assignment (which does not exist in code per se) as well as any later
  2392. # modifications.
  2393. if (
  2394. not astmts # pylint: disable=too-many-boolean-expressions
  2395. or (
  2396. astmts[0].parent == astmts[0].root()
  2397. and astmts[0].parent.parent_of(node)
  2398. )
  2399. or (
  2400. astmts[0].is_statement
  2401. or (
  2402. not isinstance(astmts[0].parent, nodes.Module)
  2403. and astmts[0].statement().parent_of(node)
  2404. )
  2405. )
  2406. ):
  2407. _astmts = []
  2408. else:
  2409. _astmts = astmts[:1]
  2410. for i, stmt in enumerate(astmts[1:]):
  2411. try:
  2412. astmt_statement = astmts[i].statement()
  2413. except astroid.exceptions.ParentMissingError:
  2414. continue
  2415. if astmt_statement.parent_of(stmt) and not utils.in_for_else_branch(
  2416. astmt_statement, stmt
  2417. ):
  2418. continue
  2419. _astmts.append(stmt)
  2420. astmts = _astmts
  2421. if len(astmts) != 1:
  2422. return
  2423. assign = astmts[0].assign_type()
  2424. if not (
  2425. isinstance(assign, (nodes.For, nodes.Comprehension, nodes.GeneratorExp))
  2426. and assign.statement() is not node.statement()
  2427. ):
  2428. return
  2429. if not isinstance(assign, nodes.For):
  2430. self.add_message("undefined-loop-variable", args=node.name, node=node)
  2431. return
  2432. for else_stmt in assign.orelse:
  2433. if isinstance(
  2434. else_stmt, (nodes.Return, nodes.Raise, nodes.Break, nodes.Continue)
  2435. ):
  2436. return
  2437. # TODO: 4.0: Consider using utils.is_terminating_func
  2438. # after merging it with RefactoringChecker._is_function_def_never_returning
  2439. if isinstance(else_stmt, nodes.Expr) and isinstance(
  2440. else_stmt.value, nodes.Call
  2441. ):
  2442. inferred_func = utils.safe_infer(else_stmt.value.func)
  2443. if (
  2444. isinstance(inferred_func, nodes.FunctionDef)
  2445. and inferred_func.returns
  2446. ):
  2447. inferred_return = utils.safe_infer(inferred_func.returns)
  2448. if isinstance(
  2449. inferred_return, nodes.FunctionDef
  2450. ) and inferred_return.qname() in {
  2451. *TYPING_NORETURN,
  2452. *TYPING_NEVER,
  2453. "typing._SpecialForm",
  2454. }:
  2455. return
  2456. # typing_extensions.NoReturn returns a _SpecialForm
  2457. if (
  2458. isinstance(inferred_return, bases.Instance)
  2459. and inferred_return.qname() == "typing._SpecialForm"
  2460. ):
  2461. return
  2462. maybe_walrus = utils.get_node_first_ancestor_of_type(node, nodes.NamedExpr)
  2463. if maybe_walrus:
  2464. maybe_comprehension = utils.get_node_first_ancestor_of_type(
  2465. maybe_walrus, nodes.Comprehension
  2466. )
  2467. if maybe_comprehension:
  2468. comprehension_scope = utils.get_node_first_ancestor_of_type(
  2469. maybe_comprehension, nodes.ComprehensionScope
  2470. )
  2471. if comprehension_scope is None:
  2472. # Should not be possible.
  2473. pass
  2474. elif (
  2475. comprehension_scope.parent.scope() is scope
  2476. and node.name in comprehension_scope.locals
  2477. ):
  2478. return
  2479. # For functions we can do more by inferring the length of the itered object
  2480. try:
  2481. inferred = next(assign.iter.infer())
  2482. # Prefer the target of enumerate() rather than the enumerate object itself
  2483. if (
  2484. isinstance(inferred, astroid.Instance)
  2485. and inferred.qname() == "builtins.enumerate"
  2486. ):
  2487. likely_call = assign.iter
  2488. if isinstance(assign.iter, nodes.IfExp):
  2489. likely_call = assign.iter.body
  2490. if isinstance(likely_call, nodes.Call) and likely_call.args:
  2491. inferred = next(likely_call.args[0].infer())
  2492. except astroid.InferenceError:
  2493. self.add_message("undefined-loop-variable", args=node.name, node=node)
  2494. else:
  2495. if (
  2496. isinstance(inferred, astroid.Instance)
  2497. and inferred.qname() == BUILTIN_RANGE
  2498. ):
  2499. # Consider range() objects safe, even if they might not yield any results.
  2500. return
  2501. # Consider sequences.
  2502. sequences = (
  2503. nodes.List,
  2504. nodes.Tuple,
  2505. nodes.Dict,
  2506. nodes.Set,
  2507. objects.FrozenSet,
  2508. )
  2509. if not isinstance(inferred, sequences):
  2510. self.add_message("undefined-loop-variable", args=node.name, node=node)
  2511. return
  2512. elements = getattr(inferred, "elts", getattr(inferred, "items", []))
  2513. if not elements:
  2514. self.add_message("undefined-loop-variable", args=node.name, node=node)
  2515. # pylint: disable = too-many-branches
  2516. def _check_is_unused(
  2517. self,
  2518. name: str,
  2519. node: nodes.FunctionDef,
  2520. stmt: nodes.NodeNG,
  2521. global_names: set[str],
  2522. nonlocal_names: Iterable[str],
  2523. comprehension_target_names: Iterable[str],
  2524. ) -> None:
  2525. # Ignore some special names specified by user configuration.
  2526. if self._is_name_ignored(stmt, name):
  2527. return
  2528. # Ignore names that were added dynamically to the Function scope
  2529. match node:
  2530. case nodes.FunctionDef(locals={"__class__": [nodes.ClassDef()]}) if (
  2531. name == "__class__"
  2532. ):
  2533. return
  2534. # Ignore names imported by the global statement.
  2535. if isinstance(stmt, (nodes.Global, nodes.Import, nodes.ImportFrom)):
  2536. # Detect imports, assigned to global statements.
  2537. if global_names and _import_name_is_global(stmt, global_names):
  2538. return
  2539. # Ignore names in comprehension targets
  2540. if name in comprehension_target_names:
  2541. return
  2542. # Ignore names in string literal type annotation.
  2543. if name in self._type_annotation_names:
  2544. return
  2545. argnames = node.argnames()
  2546. # Care about functions with unknown argument (builtins)
  2547. if name in argnames:
  2548. if node.name == "__new__":
  2549. is_init_def = False
  2550. # Look for the `__init__` method in all the methods of the same class.
  2551. for n in node.parent.get_children():
  2552. is_init_def = hasattr(n, "name") and (n.name == "__init__")
  2553. if is_init_def:
  2554. break
  2555. # Ignore unused arguments check for `__new__` if `__init__` is defined.
  2556. if is_init_def:
  2557. return
  2558. self._check_unused_arguments(name, node, stmt, argnames, nonlocal_names)
  2559. else:
  2560. if stmt.parent and isinstance(
  2561. stmt.parent, (nodes.Assign, nodes.AnnAssign, nodes.Tuple, nodes.For)
  2562. ):
  2563. if name in nonlocal_names:
  2564. return
  2565. qname = asname = None
  2566. if isinstance(stmt, (nodes.Import, nodes.ImportFrom)):
  2567. # Need the complete name, which we don't have in .locals.
  2568. if len(stmt.names) > 1:
  2569. import_names = next(
  2570. (names for names in stmt.names if name in names), None
  2571. )
  2572. else:
  2573. import_names = stmt.names[0]
  2574. if import_names:
  2575. qname, asname = import_names
  2576. name = asname or qname
  2577. if _has_locals_call_after_node(stmt, node.scope()):
  2578. message_name = "possibly-unused-variable"
  2579. else:
  2580. match stmt:
  2581. case nodes.Import():
  2582. if asname is not None:
  2583. msg = f"{qname} imported as {asname}"
  2584. else:
  2585. msg = f"import {name}"
  2586. self.add_message("unused-import", args=msg, node=stmt)
  2587. return
  2588. case nodes.ImportFrom():
  2589. if asname is not None:
  2590. msg = f"{qname} imported from {stmt.modname} as {asname}"
  2591. else:
  2592. msg = f"{name} imported from {stmt.modname}"
  2593. self.add_message("unused-import", args=msg, node=stmt)
  2594. return
  2595. message_name = "unused-variable"
  2596. if isinstance(stmt, nodes.FunctionDef) and stmt.decorators:
  2597. return
  2598. # Don't check function stubs created only for type information
  2599. if utils.is_overload_stub(node):
  2600. return
  2601. # Special case for exception variable
  2602. if self._is_exception_binding_used_in_handler(stmt, name):
  2603. return
  2604. self.add_message(message_name, args=name, node=stmt)
  2605. def _is_name_ignored(
  2606. self,
  2607. stmt: nodes.NodeNG,
  2608. name: str,
  2609. ) -> re.Pattern[str] | re.Match[str] | None:
  2610. authorized_rgx = self.linter.config.dummy_variables_rgx
  2611. match stmt:
  2612. case nodes.AssignName(parent=nodes.Arguments()) | nodes.Arguments():
  2613. regex: re.Pattern[str] = self.linter.config.ignored_argument_names
  2614. case _:
  2615. regex = authorized_rgx
  2616. # See https://stackoverflow.com/a/47007761/2519059 to
  2617. # understand what this function return. Please do NOT use
  2618. # this elsewhere, this is confusing for no benefit
  2619. return regex and regex.match(name)
  2620. def _check_unused_arguments(
  2621. self,
  2622. name: str,
  2623. node: nodes.FunctionDef,
  2624. stmt: nodes.NodeNG,
  2625. argnames: list[str],
  2626. nonlocal_names: Iterable[str],
  2627. ) -> None:
  2628. is_method = node.is_method()
  2629. klass = node.parent.frame()
  2630. if is_method and isinstance(klass, nodes.ClassDef):
  2631. confidence = (
  2632. INFERENCE if utils.has_known_bases(klass) else INFERENCE_FAILURE
  2633. )
  2634. else:
  2635. confidence = HIGH
  2636. if is_method:
  2637. # Don't warn for the first argument of a (non static) method
  2638. if node.type != "staticmethod" and name == argnames[0]:
  2639. return
  2640. # Don't warn for argument of an overridden method
  2641. overridden = overridden_method(klass, node.name)
  2642. if overridden is not None and name in overridden.argnames():
  2643. return
  2644. if node.name in utils.PYMETHODS and node.name not in (
  2645. "__init__",
  2646. "__new__",
  2647. ):
  2648. return
  2649. # Don't check callback arguments
  2650. if any(
  2651. node.name.startswith(cb) or node.name.endswith(cb)
  2652. for cb in self.linter.config.callbacks
  2653. ):
  2654. return
  2655. # Don't check arguments of singledispatch.register function.
  2656. if utils.is_registered_in_singledispatch_function(node):
  2657. return
  2658. # Don't check function stubs created only for type information
  2659. if utils.is_overload_stub(node):
  2660. return
  2661. # Don't check protocol classes
  2662. if utils.is_protocol_class(klass):
  2663. return
  2664. if name in nonlocal_names:
  2665. return
  2666. self.add_message("unused-argument", args=name, node=stmt, confidence=confidence)
  2667. def _is_exception_binding_used_in_handler(
  2668. self, stmt: nodes.NodeNG, name: str
  2669. ) -> bool:
  2670. return (
  2671. isinstance(stmt.parent, nodes.ExceptHandler)
  2672. and stmt is stmt.parent.name
  2673. and any(n.name == name for n in stmt.parent.nodes_of_class(nodes.Name))
  2674. )
  2675. def _check_late_binding_closure(self, node: nodes.Name) -> None:
  2676. """Check whether node is a cell var that is assigned within a containing loop.
  2677. Special cases where we don't care about the error:
  2678. 1. When the node's function is immediately called, e.g. (lambda: i)()
  2679. 2. When the node's function is returned from within the loop, e.g. return lambda: i
  2680. """
  2681. if not self.linter.is_message_enabled("cell-var-from-loop"):
  2682. return
  2683. node_scope = node.frame()
  2684. # If node appears in a default argument expression,
  2685. # look at the next enclosing frame instead
  2686. if utils.is_default_argument(node, node_scope):
  2687. node_scope = node_scope.parent.frame()
  2688. # Check if node is a cell var
  2689. if (
  2690. not isinstance(node_scope, (nodes.Lambda, nodes.FunctionDef))
  2691. or node.name in node_scope.locals
  2692. ):
  2693. return
  2694. assign_scope, stmts = node.lookup(node.name)
  2695. if not (stmts and assign_scope.parent_of(node_scope)):
  2696. return
  2697. if utils.is_comprehension(assign_scope):
  2698. self.add_message("cell-var-from-loop", node=node, args=node.name)
  2699. else:
  2700. # Look for an enclosing For loop.
  2701. # Currently, we only consider the first assignment
  2702. assignment_node = stmts[0]
  2703. maybe_for = assignment_node
  2704. while maybe_for and not isinstance(maybe_for, nodes.For):
  2705. if maybe_for is assign_scope:
  2706. break
  2707. maybe_for = maybe_for.parent
  2708. else:
  2709. if (
  2710. maybe_for
  2711. and maybe_for.parent_of(node_scope)
  2712. and not utils.is_being_called(node_scope)
  2713. and node_scope.parent
  2714. and not isinstance(node_scope.statement(), nodes.Return)
  2715. ):
  2716. self.add_message("cell-var-from-loop", node=node, args=node.name)
  2717. def _should_ignore_redefined_builtin(self, stmt: nodes.NodeNG) -> bool:
  2718. if not isinstance(stmt, nodes.ImportFrom):
  2719. return False
  2720. return stmt.modname in self.linter.config.redefining_builtins_modules
  2721. def _allowed_redefined_builtin(self, name: str) -> bool:
  2722. return name in self.linter.config.allowed_redefined_builtins
  2723. @staticmethod
  2724. def _comprehension_between_frame_and_node(node: nodes.Name) -> bool:
  2725. """Return True if a ComprehensionScope intervenes between `node` and its
  2726. frame.
  2727. """
  2728. closest_comprehension_scope = utils.get_node_first_ancestor_of_type(
  2729. node, nodes.ComprehensionScope
  2730. )
  2731. return closest_comprehension_scope is not None and node.frame().parent_of(
  2732. closest_comprehension_scope
  2733. )
  2734. def _store_type_annotation_node(self, type_annotation: nodes.NodeNG) -> None:
  2735. """Given a type annotation, store all the name nodes it refers to."""
  2736. match type_annotation:
  2737. case nodes.Name():
  2738. self._type_annotation_names.append(type_annotation.name)
  2739. return
  2740. case nodes.Attribute():
  2741. self._store_type_annotation_node(type_annotation.expr)
  2742. return
  2743. case nodes.Subscript():
  2744. pass
  2745. case _:
  2746. return
  2747. match type_annotation.value:
  2748. case nodes.Attribute(expr=nodes.Name(name=name)) if name == TYPING_MODULE:
  2749. self._type_annotation_names.append(TYPING_MODULE)
  2750. return
  2751. self._type_annotation_names.extend(
  2752. annotation.name for annotation in type_annotation.nodes_of_class(nodes.Name)
  2753. )
  2754. def _store_type_annotation_names(
  2755. self,
  2756. node: nodes.For | nodes.Assign | nodes.With,
  2757. ) -> None:
  2758. type_annotation = node.type_annotation
  2759. if not type_annotation:
  2760. return
  2761. self._store_type_annotation_node(node.type_annotation)
  2762. def _check_self_cls_assign(self, node: nodes.Assign) -> None:
  2763. """Check that self/cls don't get assigned."""
  2764. assign_names: set[str | None] = set()
  2765. for target in node.targets:
  2766. match target:
  2767. case nodes.AssignName():
  2768. assign_names.add(target.name)
  2769. case nodes.Tuple():
  2770. assign_names.update(
  2771. elt.name
  2772. for elt in target.elts
  2773. if isinstance(elt, nodes.AssignName)
  2774. )
  2775. scope = node.scope()
  2776. nonlocals_with_same_name = node.scope().parent and any(
  2777. child for child in scope.body if isinstance(child, nodes.Nonlocal)
  2778. )
  2779. if nonlocals_with_same_name:
  2780. scope = node.scope().parent.scope()
  2781. if not (
  2782. isinstance(scope, nodes.FunctionDef)
  2783. and scope.is_method()
  2784. and "builtins.staticmethod" not in scope.decoratornames()
  2785. ):
  2786. return
  2787. argument_names = scope.argnames()
  2788. if not argument_names:
  2789. return
  2790. self_cls_name = argument_names[0]
  2791. if self_cls_name in assign_names:
  2792. self.add_message("self-cls-assignment", node=node, args=(self_cls_name,))
  2793. def _check_unpacking(
  2794. self,
  2795. inferred: InferenceResult,
  2796. node: nodes.Assign,
  2797. targets: list[nodes.NodeNG],
  2798. ) -> None:
  2799. """Check for unbalanced tuple unpacking
  2800. and unpacking non sequences.
  2801. """
  2802. if utils.is_inside_abstract_class(node):
  2803. return
  2804. if utils.is_comprehension(node):
  2805. return
  2806. if isinstance(inferred, util.UninferableBase):
  2807. return
  2808. if (
  2809. isinstance(inferred.parent, nodes.Arguments)
  2810. and isinstance(node.value, nodes.Name)
  2811. and node.value.name == inferred.parent.vararg
  2812. ):
  2813. # Variable-length argument, we can't determine the length.
  2814. return
  2815. # Attempt to check unpacking is properly balanced
  2816. values = self._nodes_to_unpack(inferred)
  2817. details = _get_unpacking_extra_info(node, inferred)
  2818. if values is not None:
  2819. if len(targets) != len(values):
  2820. self._report_unbalanced_unpacking(
  2821. node, inferred, targets, len(values), details
  2822. )
  2823. # attempt to check unpacking may be possible (i.e. RHS is iterable)
  2824. elif not utils.is_iterable(inferred):
  2825. self._report_unpacking_non_sequence(node, details)
  2826. @staticmethod
  2827. def _get_value_length(value_node: nodes.NodeNG) -> int:
  2828. value_subnodes = VariablesChecker._nodes_to_unpack(value_node)
  2829. if value_subnodes is not None:
  2830. return len(value_subnodes)
  2831. match value_node:
  2832. case nodes.Const(value=str() | bytes()):
  2833. return len(value_node.value)
  2834. case nodes.Subscript():
  2835. step = value_node.slice.step or 1
  2836. splice_range = (
  2837. value_node.slice.upper.value - value_node.slice.lower.value
  2838. )
  2839. # RUF046 says the return of 'math.ceil' is always an int, mypy doesn't see it
  2840. return math.ceil(splice_range / step) # type: ignore[no-any-return]
  2841. return 1
  2842. @staticmethod
  2843. def _nodes_to_unpack(node: nodes.NodeNG) -> list[nodes.NodeNG] | None:
  2844. """Return the list of values of the `Assign` node."""
  2845. if isinstance(node, (nodes.Tuple, nodes.List, nodes.Set, *DICT_TYPES)):
  2846. return node.itered() # type: ignore[no-any-return]
  2847. if isinstance(node, astroid.Instance) and any(
  2848. ancestor.qname() == "typing.NamedTuple" for ancestor in node.ancestors()
  2849. ):
  2850. return [i for i in node.values() if isinstance(i, nodes.AssignName)]
  2851. return None
  2852. def _report_unbalanced_unpacking(
  2853. self,
  2854. node: nodes.NodeNG,
  2855. inferred: InferenceResult,
  2856. targets: list[nodes.NodeNG],
  2857. values_count: int,
  2858. details: str,
  2859. ) -> None:
  2860. args = (
  2861. details,
  2862. len(targets),
  2863. "" if len(targets) == 1 else "s",
  2864. values_count,
  2865. "" if values_count == 1 else "s",
  2866. )
  2867. symbol = (
  2868. "unbalanced-dict-unpacking"
  2869. if isinstance(inferred, DICT_TYPES)
  2870. else "unbalanced-tuple-unpacking"
  2871. )
  2872. self.add_message(symbol, node=node, args=args, confidence=INFERENCE)
  2873. def _report_unpacking_non_sequence(self, node: nodes.NodeNG, details: str) -> None:
  2874. if details and not details.startswith(" "):
  2875. details = f" {details}"
  2876. self.add_message("unpacking-non-sequence", node=node, args=details)
  2877. def _check_module_attrs(
  2878. self,
  2879. node: _base_nodes.ImportNode,
  2880. module: nodes.Module,
  2881. module_names: list[str],
  2882. ) -> nodes.Module | None:
  2883. """Check that module_names (list of string) are accessible through the
  2884. given module, if the latest access name corresponds to a module, return it.
  2885. """
  2886. while module_names:
  2887. name = module_names.pop(0)
  2888. if name == "__dict__":
  2889. module = None
  2890. break
  2891. try:
  2892. module = module.getattr(name)[0]
  2893. if not isinstance(module, nodes.Module):
  2894. module = next(module.infer())
  2895. if not isinstance(module, nodes.Module):
  2896. return None
  2897. except astroid.NotFoundError:
  2898. # Unable to import `name` from `module`. Since `name` may itself be a
  2899. # module, we first check if it matches the ignored modules.
  2900. if is_module_ignored(f"{module.qname()}.{name}", self._ignored_modules):
  2901. return None
  2902. self.add_message(
  2903. "no-name-in-module", args=(name, module.name), node=node
  2904. )
  2905. return None
  2906. except astroid.InferenceError:
  2907. return None
  2908. if module_names:
  2909. modname = module.name if module else "__dict__"
  2910. self.add_message(
  2911. "no-name-in-module", node=node, args=(".".join(module_names), modname)
  2912. )
  2913. return None
  2914. if isinstance(module, nodes.Module):
  2915. return module
  2916. return None
  2917. def _check_all(
  2918. self,
  2919. node: nodes.Module,
  2920. not_consumed: Consumption,
  2921. ) -> None:
  2922. try:
  2923. assigned = next(node.igetattr("__all__"))
  2924. except astroid.InferenceError:
  2925. return
  2926. if isinstance(assigned, util.UninferableBase):
  2927. return
  2928. if assigned.pytype() not in {"builtins.list", "builtins.tuple"}:
  2929. line, col = assigned.tolineno, assigned.col_offset
  2930. self.add_message("invalid-all-format", line=line, col_offset=col, node=node)
  2931. return
  2932. for elt in getattr(assigned, "elts", ()):
  2933. try:
  2934. elt_name = next(elt.infer())
  2935. except astroid.InferenceError:
  2936. continue
  2937. if isinstance(elt_name, util.UninferableBase):
  2938. continue
  2939. if not elt_name.parent:
  2940. continue
  2941. if not (
  2942. isinstance(elt_name, nodes.Const) and isinstance(elt_name.value, str)
  2943. ):
  2944. self.add_message("invalid-all-object", args=elt.as_string(), node=elt)
  2945. continue
  2946. elt_name = elt_name.value
  2947. # If elt is in not_consumed, remove it from not_consumed
  2948. if elt_name in not_consumed:
  2949. del not_consumed[elt_name]
  2950. continue
  2951. if elt_name not in node.locals:
  2952. if not node.package:
  2953. self.add_message(
  2954. "undefined-all-variable", args=(elt_name,), node=elt
  2955. )
  2956. else:
  2957. basename = os.path.splitext(node.file)[0]
  2958. if os.path.basename(basename) == "__init__":
  2959. name = node.name + "." + elt_name
  2960. try:
  2961. astroid.modutils.file_from_modpath(name.split("."))
  2962. except ImportError:
  2963. self.add_message(
  2964. "undefined-all-variable", args=(elt_name,), node=elt
  2965. )
  2966. except SyntaxError:
  2967. # don't yield a syntax-error warning,
  2968. # because it will be later yielded
  2969. # when the file will be checked
  2970. pass
  2971. def _check_globals(self, not_consumed: Consumption) -> None:
  2972. if self._allow_global_unused_variables:
  2973. return
  2974. for name, node_lst in not_consumed.items():
  2975. for node in node_lst:
  2976. if in_type_checking_block(node):
  2977. continue
  2978. if self._is_exception_binding_used_in_handler(node, name):
  2979. continue
  2980. if isinstance(node, nodes.AssignName) and node.name == "__all__":
  2981. continue
  2982. if (
  2983. isinstance(node, nodes.ImportFrom)
  2984. and name == "annotations"
  2985. and node.modname == "__future__"
  2986. ):
  2987. continue
  2988. self.add_message("unused-variable", args=(name,), node=node)
  2989. # pylint: disable = too-many-branches
  2990. def _check_imports(self, not_consumed: Consumption) -> None:
  2991. local_names = _fix_dot_imports(not_consumed)
  2992. checked = set()
  2993. unused_wildcard_imports: defaultdict[
  2994. tuple[str, nodes.ImportFrom],
  2995. list[str],
  2996. ] = defaultdict(list)
  2997. for name, stmt in local_names:
  2998. for imports in stmt.names:
  2999. real_name = imported_name = imports[0]
  3000. if imported_name == "*":
  3001. real_name = name
  3002. as_name = imports[1]
  3003. if real_name in checked:
  3004. continue
  3005. if name not in (real_name, as_name):
  3006. continue
  3007. checked.add(real_name)
  3008. is_type_annotation_import = (
  3009. imported_name in self._type_annotation_names
  3010. or as_name in self._type_annotation_names
  3011. )
  3012. is_dummy_import = (
  3013. as_name
  3014. and self.linter.config.dummy_variables_rgx
  3015. and self.linter.config.dummy_variables_rgx.match(as_name)
  3016. )
  3017. if isinstance(stmt, nodes.Import) or (
  3018. isinstance(stmt, nodes.ImportFrom) and not stmt.modname
  3019. ):
  3020. if isinstance(stmt, nodes.ImportFrom) and SPECIAL_OBJ.search(
  3021. imported_name
  3022. ):
  3023. # Filter special objects (__doc__, __all__) etc.,
  3024. # because they can be imported for exporting.
  3025. continue
  3026. if is_type_annotation_import or is_dummy_import:
  3027. # Most likely a typing import if it wasn't used so far.
  3028. # Also filter dummy variables.
  3029. continue
  3030. if as_name is None:
  3031. msg = f"import {imported_name}"
  3032. else:
  3033. msg = f"{imported_name} imported as {as_name}"
  3034. if not in_type_checking_block(stmt):
  3035. self.add_message("unused-import", args=msg, node=stmt)
  3036. elif isinstance(stmt, nodes.ImportFrom) and stmt.modname != FUTURE:
  3037. if SPECIAL_OBJ.search(imported_name):
  3038. # Filter special objects (__doc__, __all__) etc.,
  3039. # because they can be imported for exporting.
  3040. continue
  3041. if _is_from_future_import(stmt, name):
  3042. # Check if the name is in fact loaded from a
  3043. # __future__ import in another module.
  3044. continue
  3045. if is_type_annotation_import or is_dummy_import:
  3046. # Most likely a typing import if it wasn't used so far.
  3047. # Also filter dummy variables.
  3048. continue
  3049. if imported_name == "*":
  3050. unused_wildcard_imports[(stmt.modname, stmt)].append(name)
  3051. else:
  3052. if as_name is None:
  3053. msg = f"{imported_name} imported from {stmt.modname}"
  3054. else:
  3055. msg = f"{imported_name} imported from {stmt.modname} as {as_name}"
  3056. if not in_type_checking_block(stmt):
  3057. self.add_message("unused-import", args=msg, node=stmt)
  3058. # Construct string for unused-wildcard-import message
  3059. for module, unused_list in unused_wildcard_imports.items():
  3060. if len(unused_list) == 1:
  3061. arg_string = unused_list[0]
  3062. else:
  3063. arg_string = (
  3064. f"{', '.join(i for i in unused_list[:-1])} and {unused_list[-1]}"
  3065. )
  3066. self.add_message(
  3067. "unused-wildcard-import", args=(arg_string, module[0]), node=module[1]
  3068. )
  3069. del self._to_consume
  3070. def _check_metaclasses(self, node: nodes.Module | nodes.FunctionDef) -> None:
  3071. """Update consumption analysis for metaclasses."""
  3072. consumed: list[tuple[Consumption, str]] = []
  3073. for child_node in node.get_children():
  3074. if isinstance(child_node, nodes.ClassDef):
  3075. consumed.extend(self._check_classdef_metaclasses(child_node, node))
  3076. # Pop the consumed items, in order to avoid having
  3077. # unused-import and unused-variable false positives
  3078. for scope_locals, name in consumed:
  3079. scope_locals.pop(name, None)
  3080. def _check_classdef_metaclasses(
  3081. self,
  3082. klass: nodes.ClassDef,
  3083. parent_node: nodes.Module | nodes.FunctionDef,
  3084. ) -> list[tuple[Consumption, str]]:
  3085. if not klass._metaclass:
  3086. # Skip if this class doesn't use explicitly a metaclass, but inherits it from ancestors
  3087. return []
  3088. consumed: list[tuple[Consumption, str]] = []
  3089. metaclass = klass.metaclass()
  3090. name = ""
  3091. match klass._metaclass:
  3092. case nodes.Name(name=name):
  3093. # bind name
  3094. pass
  3095. case nodes.Attribute(expr=attr):
  3096. while not isinstance(attr, nodes.Name):
  3097. attr = attr.expr
  3098. name = attr.name
  3099. case nodes.Call(func=nodes.Name(name=name)):
  3100. # bind name
  3101. pass
  3102. case _ if metaclass:
  3103. name = metaclass.root().name
  3104. found = False
  3105. name = METACLASS_NAME_TRANSFORMS.get(name, name)
  3106. if name:
  3107. # check enclosing scopes starting from most local
  3108. for to_consume in self._to_consume[::-1]:
  3109. scope_locals = to_consume.to_consume
  3110. found_nodes = scope_locals.get(name, [])
  3111. for found_node in found_nodes:
  3112. if found_node.lineno <= klass.lineno:
  3113. consumed.append((scope_locals, name))
  3114. found = True
  3115. break
  3116. # Check parent scope
  3117. nodes_in_parent_scope = parent_node.locals.get(name, [])
  3118. for found_node_parent in nodes_in_parent_scope:
  3119. if found_node_parent.lineno <= klass.lineno:
  3120. found = True
  3121. break
  3122. if (
  3123. not found
  3124. and not metaclass
  3125. and not (
  3126. name in nodes.Module.scope_attrs
  3127. or utils.is_builtin(name)
  3128. or name in self.linter.config.additional_builtins
  3129. )
  3130. ):
  3131. self.add_message("undefined-variable", node=klass, args=(name,))
  3132. return consumed
  3133. def visit_subscript(self, node: nodes.Subscript) -> None:
  3134. inferred_slice = utils.safe_infer(node.slice)
  3135. self._check_potential_index_error(node, inferred_slice)
  3136. def _inferred_iterable_length(self, iterable: nodes.Tuple | nodes.List) -> int:
  3137. length = 0
  3138. for elt in iterable.elts:
  3139. if not isinstance(elt, nodes.Starred):
  3140. length += 1
  3141. continue
  3142. unpacked = utils.safe_infer(elt.value)
  3143. if isinstance(unpacked, nodes.BaseContainer):
  3144. length += len(unpacked.elts)
  3145. else:
  3146. length += 1
  3147. return length
  3148. def _check_potential_index_error(
  3149. self,
  3150. node: nodes.Subscript,
  3151. inferred_slice: nodes.NodeNG | None,
  3152. ) -> None:
  3153. """Check for the potential-index-error message."""
  3154. # Currently we only check simple slices of a single integer
  3155. if not (
  3156. isinstance(inferred_slice, nodes.Const)
  3157. and isinstance(inferred_slice.value, int)
  3158. ):
  3159. return
  3160. # If the node.value is a Tuple or List without inference it is defined in place
  3161. if isinstance(node.value, (nodes.Tuple, nodes.List)):
  3162. # Add 1 because iterables are 0-indexed
  3163. if self._inferred_iterable_length(node.value) < inferred_slice.value + 1:
  3164. self.add_message(
  3165. "potential-index-error", node=node, confidence=INFERENCE
  3166. )
  3167. return
  3168. @utils.only_required_for_messages(
  3169. "unused-import",
  3170. "unused-variable",
  3171. )
  3172. def visit_const(self, node: nodes.Const) -> None:
  3173. """Take note of names that appear inside string literal type annotations
  3174. unless the string is a parameter to `typing.Literal` or `typing.Annotation`.
  3175. """
  3176. if node.pytype() != "builtins.str":
  3177. return
  3178. if not utils.is_node_in_type_annotation_context(node):
  3179. return
  3180. # Check if parent's or grandparent's first child is typing.Literal
  3181. parent = node.parent
  3182. if isinstance(parent, nodes.Tuple):
  3183. parent = parent.parent
  3184. if isinstance(parent, nodes.Subscript):
  3185. origin = next(parent.get_children(), None)
  3186. if origin is not None and utils.is_typing_member(
  3187. origin, ("Annotated", "Literal")
  3188. ):
  3189. return
  3190. try:
  3191. annotation = extract_node(node.value)
  3192. self._store_type_annotation_node(annotation)
  3193. except ValueError:
  3194. # e.g. node.value is white space
  3195. pass
  3196. except astroid.AstroidSyntaxError:
  3197. # e.g. "?" or ":" in typing.Literal["?", ":"]
  3198. pass
  3199. def register(linter: PyLinter) -> None:
  3200. linter.register_checker(VariablesChecker(linter))