partially_defined.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675
  1. from __future__ import annotations
  2. from enum import Enum
  3. from mypy import checker, errorcodes
  4. from mypy.messages import MessageBuilder
  5. from mypy.nodes import (
  6. AssertStmt,
  7. AssignmentExpr,
  8. AssignmentStmt,
  9. BreakStmt,
  10. ClassDef,
  11. Context,
  12. ContinueStmt,
  13. DictionaryComprehension,
  14. Expression,
  15. ExpressionStmt,
  16. ForStmt,
  17. FuncDef,
  18. FuncItem,
  19. GeneratorExpr,
  20. GlobalDecl,
  21. IfStmt,
  22. Import,
  23. ImportFrom,
  24. LambdaExpr,
  25. ListExpr,
  26. Lvalue,
  27. MatchStmt,
  28. MypyFile,
  29. NameExpr,
  30. NonlocalDecl,
  31. RaiseStmt,
  32. ReturnStmt,
  33. StarExpr,
  34. SymbolTable,
  35. TryStmt,
  36. TupleExpr,
  37. WhileStmt,
  38. WithStmt,
  39. implicit_module_attrs,
  40. )
  41. from mypy.options import Options
  42. from mypy.patterns import AsPattern, StarredPattern
  43. from mypy.reachability import ALWAYS_TRUE, infer_pattern_value
  44. from mypy.traverser import ExtendedTraverserVisitor
  45. from mypy.types import Type, UninhabitedType
  46. class BranchState:
  47. """BranchState contains information about variable definition at the end of a branching statement.
  48. `if` and `match` are examples of branching statements.
  49. `may_be_defined` contains variables that were defined in only some branches.
  50. `must_be_defined` contains variables that were defined in all branches.
  51. """
  52. def __init__(
  53. self,
  54. must_be_defined: set[str] | None = None,
  55. may_be_defined: set[str] | None = None,
  56. skipped: bool = False,
  57. ) -> None:
  58. if may_be_defined is None:
  59. may_be_defined = set()
  60. if must_be_defined is None:
  61. must_be_defined = set()
  62. self.may_be_defined = set(may_be_defined)
  63. self.must_be_defined = set(must_be_defined)
  64. self.skipped = skipped
  65. def copy(self) -> BranchState:
  66. return BranchState(
  67. must_be_defined=set(self.must_be_defined),
  68. may_be_defined=set(self.may_be_defined),
  69. skipped=self.skipped,
  70. )
  71. class BranchStatement:
  72. def __init__(self, initial_state: BranchState | None = None) -> None:
  73. if initial_state is None:
  74. initial_state = BranchState()
  75. self.initial_state = initial_state
  76. self.branches: list[BranchState] = [
  77. BranchState(
  78. must_be_defined=self.initial_state.must_be_defined,
  79. may_be_defined=self.initial_state.may_be_defined,
  80. )
  81. ]
  82. def copy(self) -> BranchStatement:
  83. result = BranchStatement(self.initial_state)
  84. result.branches = [b.copy() for b in self.branches]
  85. return result
  86. def next_branch(self) -> None:
  87. self.branches.append(
  88. BranchState(
  89. must_be_defined=self.initial_state.must_be_defined,
  90. may_be_defined=self.initial_state.may_be_defined,
  91. )
  92. )
  93. def record_definition(self, name: str) -> None:
  94. assert len(self.branches) > 0
  95. self.branches[-1].must_be_defined.add(name)
  96. self.branches[-1].may_be_defined.discard(name)
  97. def delete_var(self, name: str) -> None:
  98. assert len(self.branches) > 0
  99. self.branches[-1].must_be_defined.discard(name)
  100. self.branches[-1].may_be_defined.discard(name)
  101. def record_nested_branch(self, state: BranchState) -> None:
  102. assert len(self.branches) > 0
  103. current_branch = self.branches[-1]
  104. if state.skipped:
  105. current_branch.skipped = True
  106. return
  107. current_branch.must_be_defined.update(state.must_be_defined)
  108. current_branch.may_be_defined.update(state.may_be_defined)
  109. current_branch.may_be_defined.difference_update(current_branch.must_be_defined)
  110. def skip_branch(self) -> None:
  111. assert len(self.branches) > 0
  112. self.branches[-1].skipped = True
  113. def is_possibly_undefined(self, name: str) -> bool:
  114. assert len(self.branches) > 0
  115. return name in self.branches[-1].may_be_defined
  116. def is_undefined(self, name: str) -> bool:
  117. assert len(self.branches) > 0
  118. branch = self.branches[-1]
  119. return name not in branch.may_be_defined and name not in branch.must_be_defined
  120. def is_defined_in_a_branch(self, name: str) -> bool:
  121. assert len(self.branches) > 0
  122. for b in self.branches:
  123. if name in b.must_be_defined or name in b.may_be_defined:
  124. return True
  125. return False
  126. def done(self) -> BranchState:
  127. # First, compute all vars, including skipped branches. We include skipped branches
  128. # because our goal is to capture all variables that semantic analyzer would
  129. # consider defined.
  130. all_vars = set()
  131. for b in self.branches:
  132. all_vars.update(b.may_be_defined)
  133. all_vars.update(b.must_be_defined)
  134. # For the rest of the things, we only care about branches that weren't skipped.
  135. non_skipped_branches = [b for b in self.branches if not b.skipped]
  136. if non_skipped_branches:
  137. must_be_defined = non_skipped_branches[0].must_be_defined
  138. for b in non_skipped_branches[1:]:
  139. must_be_defined.intersection_update(b.must_be_defined)
  140. else:
  141. must_be_defined = set()
  142. # Everything that wasn't defined in all branches but was defined
  143. # in at least one branch should be in `may_be_defined`!
  144. may_be_defined = all_vars.difference(must_be_defined)
  145. return BranchState(
  146. must_be_defined=must_be_defined,
  147. may_be_defined=may_be_defined,
  148. skipped=len(non_skipped_branches) == 0,
  149. )
  150. class ScopeType(Enum):
  151. Global = 1
  152. Class = 2
  153. Func = 3
  154. Generator = 4
  155. class Scope:
  156. def __init__(self, stmts: list[BranchStatement], scope_type: ScopeType) -> None:
  157. self.branch_stmts: list[BranchStatement] = stmts
  158. self.scope_type = scope_type
  159. self.undefined_refs: dict[str, set[NameExpr]] = {}
  160. def copy(self) -> Scope:
  161. result = Scope([s.copy() for s in self.branch_stmts], self.scope_type)
  162. result.undefined_refs = self.undefined_refs.copy()
  163. return result
  164. def record_undefined_ref(self, o: NameExpr) -> None:
  165. if o.name not in self.undefined_refs:
  166. self.undefined_refs[o.name] = set()
  167. self.undefined_refs[o.name].add(o)
  168. def pop_undefined_ref(self, name: str) -> set[NameExpr]:
  169. return self.undefined_refs.pop(name, set())
  170. class DefinedVariableTracker:
  171. """DefinedVariableTracker manages the state and scope for the UndefinedVariablesVisitor."""
  172. def __init__(self) -> None:
  173. # There's always at least one scope. Within each scope, there's at least one "global" BranchingStatement.
  174. self.scopes: list[Scope] = [Scope([BranchStatement()], ScopeType.Global)]
  175. # disable_branch_skip is used to disable skipping a branch due to a return/raise/etc. This is useful
  176. # in things like try/except/finally statements.
  177. self.disable_branch_skip = False
  178. def copy(self) -> DefinedVariableTracker:
  179. result = DefinedVariableTracker()
  180. result.scopes = [s.copy() for s in self.scopes]
  181. result.disable_branch_skip = self.disable_branch_skip
  182. return result
  183. def _scope(self) -> Scope:
  184. assert len(self.scopes) > 0
  185. return self.scopes[-1]
  186. def enter_scope(self, scope_type: ScopeType) -> None:
  187. assert len(self._scope().branch_stmts) > 0
  188. initial_state = None
  189. if scope_type == ScopeType.Generator:
  190. # Generators are special because they inherit the outer scope.
  191. initial_state = self._scope().branch_stmts[-1].branches[-1]
  192. self.scopes.append(Scope([BranchStatement(initial_state)], scope_type))
  193. def exit_scope(self) -> None:
  194. self.scopes.pop()
  195. def in_scope(self, scope_type: ScopeType) -> bool:
  196. return self._scope().scope_type == scope_type
  197. def start_branch_statement(self) -> None:
  198. assert len(self._scope().branch_stmts) > 0
  199. self._scope().branch_stmts.append(
  200. BranchStatement(self._scope().branch_stmts[-1].branches[-1])
  201. )
  202. def next_branch(self) -> None:
  203. assert len(self._scope().branch_stmts) > 1
  204. self._scope().branch_stmts[-1].next_branch()
  205. def end_branch_statement(self) -> None:
  206. assert len(self._scope().branch_stmts) > 1
  207. result = self._scope().branch_stmts.pop().done()
  208. self._scope().branch_stmts[-1].record_nested_branch(result)
  209. def skip_branch(self) -> None:
  210. # Only skip branch if we're outside of "root" branch statement.
  211. if len(self._scope().branch_stmts) > 1 and not self.disable_branch_skip:
  212. self._scope().branch_stmts[-1].skip_branch()
  213. def record_definition(self, name: str) -> None:
  214. assert len(self.scopes) > 0
  215. assert len(self.scopes[-1].branch_stmts) > 0
  216. self._scope().branch_stmts[-1].record_definition(name)
  217. def delete_var(self, name: str) -> None:
  218. assert len(self.scopes) > 0
  219. assert len(self.scopes[-1].branch_stmts) > 0
  220. self._scope().branch_stmts[-1].delete_var(name)
  221. def record_undefined_ref(self, o: NameExpr) -> None:
  222. """Records an undefined reference. These can later be retrieved via `pop_undefined_ref`."""
  223. assert len(self.scopes) > 0
  224. self._scope().record_undefined_ref(o)
  225. def pop_undefined_ref(self, name: str) -> set[NameExpr]:
  226. """If name has previously been reported as undefined, the NameExpr that was called will be returned."""
  227. assert len(self.scopes) > 0
  228. return self._scope().pop_undefined_ref(name)
  229. def is_possibly_undefined(self, name: str) -> bool:
  230. assert len(self._scope().branch_stmts) > 0
  231. # A variable is undefined if it's in a set of `may_be_defined` but not in `must_be_defined`.
  232. return self._scope().branch_stmts[-1].is_possibly_undefined(name)
  233. def is_defined_in_different_branch(self, name: str) -> bool:
  234. """This will return true if a variable is defined in a branch that's not the current branch."""
  235. assert len(self._scope().branch_stmts) > 0
  236. stmt = self._scope().branch_stmts[-1]
  237. if not stmt.is_undefined(name):
  238. return False
  239. for stmt in self._scope().branch_stmts:
  240. if stmt.is_defined_in_a_branch(name):
  241. return True
  242. return False
  243. def is_undefined(self, name: str) -> bool:
  244. assert len(self._scope().branch_stmts) > 0
  245. return self._scope().branch_stmts[-1].is_undefined(name)
  246. class Loop:
  247. def __init__(self) -> None:
  248. self.has_break = False
  249. class PossiblyUndefinedVariableVisitor(ExtendedTraverserVisitor):
  250. """Detects the following cases:
  251. - A variable that's defined only part of the time.
  252. - If a variable is used before definition
  253. An example of a partial definition:
  254. if foo():
  255. x = 1
  256. print(x) # Error: "x" may be undefined.
  257. Example of a used before definition:
  258. x = y
  259. y: int = 2
  260. Note that this code does not detect variables not defined in any of the branches -- that is
  261. handled by the semantic analyzer.
  262. """
  263. def __init__(
  264. self,
  265. msg: MessageBuilder,
  266. type_map: dict[Expression, Type],
  267. options: Options,
  268. names: SymbolTable,
  269. ) -> None:
  270. self.msg = msg
  271. self.type_map = type_map
  272. self.options = options
  273. self.builtins = SymbolTable()
  274. builtins_mod = names.get("__builtins__", None)
  275. if builtins_mod:
  276. assert isinstance(builtins_mod.node, MypyFile)
  277. self.builtins = builtins_mod.node.names
  278. self.loops: list[Loop] = []
  279. self.try_depth = 0
  280. self.tracker = DefinedVariableTracker()
  281. for name in implicit_module_attrs:
  282. self.tracker.record_definition(name)
  283. def var_used_before_def(self, name: str, context: Context) -> None:
  284. if self.msg.errors.is_error_code_enabled(errorcodes.USED_BEFORE_DEF):
  285. self.msg.var_used_before_def(name, context)
  286. def variable_may_be_undefined(self, name: str, context: Context) -> None:
  287. if self.msg.errors.is_error_code_enabled(errorcodes.POSSIBLY_UNDEFINED):
  288. self.msg.variable_may_be_undefined(name, context)
  289. def process_definition(self, name: str) -> None:
  290. # Was this name previously used? If yes, it's a used-before-definition error.
  291. if not self.tracker.in_scope(ScopeType.Class):
  292. refs = self.tracker.pop_undefined_ref(name)
  293. for ref in refs:
  294. if self.loops:
  295. self.variable_may_be_undefined(name, ref)
  296. else:
  297. self.var_used_before_def(name, ref)
  298. else:
  299. # Errors in class scopes are caught by the semantic analyzer.
  300. pass
  301. self.tracker.record_definition(name)
  302. def visit_global_decl(self, o: GlobalDecl) -> None:
  303. for name in o.names:
  304. self.process_definition(name)
  305. super().visit_global_decl(o)
  306. def visit_nonlocal_decl(self, o: NonlocalDecl) -> None:
  307. for name in o.names:
  308. self.process_definition(name)
  309. super().visit_nonlocal_decl(o)
  310. def process_lvalue(self, lvalue: Lvalue | None) -> None:
  311. if isinstance(lvalue, NameExpr):
  312. self.process_definition(lvalue.name)
  313. elif isinstance(lvalue, StarExpr):
  314. self.process_lvalue(lvalue.expr)
  315. elif isinstance(lvalue, (ListExpr, TupleExpr)):
  316. for item in lvalue.items:
  317. self.process_lvalue(item)
  318. def visit_assignment_stmt(self, o: AssignmentStmt) -> None:
  319. for lvalue in o.lvalues:
  320. self.process_lvalue(lvalue)
  321. super().visit_assignment_stmt(o)
  322. def visit_assignment_expr(self, o: AssignmentExpr) -> None:
  323. o.value.accept(self)
  324. self.process_lvalue(o.target)
  325. def visit_if_stmt(self, o: IfStmt) -> None:
  326. for e in o.expr:
  327. e.accept(self)
  328. self.tracker.start_branch_statement()
  329. for b in o.body:
  330. if b.is_unreachable:
  331. continue
  332. b.accept(self)
  333. self.tracker.next_branch()
  334. if o.else_body:
  335. if not o.else_body.is_unreachable:
  336. o.else_body.accept(self)
  337. else:
  338. self.tracker.skip_branch()
  339. self.tracker.end_branch_statement()
  340. def visit_match_stmt(self, o: MatchStmt) -> None:
  341. o.subject.accept(self)
  342. self.tracker.start_branch_statement()
  343. for i in range(len(o.patterns)):
  344. pattern = o.patterns[i]
  345. pattern.accept(self)
  346. guard = o.guards[i]
  347. if guard is not None:
  348. guard.accept(self)
  349. if not o.bodies[i].is_unreachable:
  350. o.bodies[i].accept(self)
  351. else:
  352. self.tracker.skip_branch()
  353. is_catchall = infer_pattern_value(pattern) == ALWAYS_TRUE
  354. if not is_catchall:
  355. self.tracker.next_branch()
  356. self.tracker.end_branch_statement()
  357. def visit_func_def(self, o: FuncDef) -> None:
  358. self.process_definition(o.name)
  359. super().visit_func_def(o)
  360. def visit_func(self, o: FuncItem) -> None:
  361. if o.is_dynamic() and not self.options.check_untyped_defs:
  362. return
  363. args = o.arguments or []
  364. # Process initializers (defaults) outside the function scope.
  365. for arg in args:
  366. if arg.initializer is not None:
  367. arg.initializer.accept(self)
  368. self.tracker.enter_scope(ScopeType.Func)
  369. for arg in args:
  370. self.process_definition(arg.variable.name)
  371. super().visit_var(arg.variable)
  372. o.body.accept(self)
  373. self.tracker.exit_scope()
  374. def visit_generator_expr(self, o: GeneratorExpr) -> None:
  375. self.tracker.enter_scope(ScopeType.Generator)
  376. for idx in o.indices:
  377. self.process_lvalue(idx)
  378. super().visit_generator_expr(o)
  379. self.tracker.exit_scope()
  380. def visit_dictionary_comprehension(self, o: DictionaryComprehension) -> None:
  381. self.tracker.enter_scope(ScopeType.Generator)
  382. for idx in o.indices:
  383. self.process_lvalue(idx)
  384. super().visit_dictionary_comprehension(o)
  385. self.tracker.exit_scope()
  386. def visit_for_stmt(self, o: ForStmt) -> None:
  387. o.expr.accept(self)
  388. self.process_lvalue(o.index)
  389. o.index.accept(self)
  390. self.tracker.start_branch_statement()
  391. loop = Loop()
  392. self.loops.append(loop)
  393. o.body.accept(self)
  394. self.tracker.next_branch()
  395. self.tracker.end_branch_statement()
  396. if o.else_body is not None:
  397. # If the loop has a `break` inside, `else` is executed conditionally.
  398. # If the loop doesn't have a `break` either the function will return or
  399. # execute the `else`.
  400. has_break = loop.has_break
  401. if has_break:
  402. self.tracker.start_branch_statement()
  403. self.tracker.next_branch()
  404. o.else_body.accept(self)
  405. if has_break:
  406. self.tracker.end_branch_statement()
  407. self.loops.pop()
  408. def visit_return_stmt(self, o: ReturnStmt) -> None:
  409. super().visit_return_stmt(o)
  410. self.tracker.skip_branch()
  411. def visit_lambda_expr(self, o: LambdaExpr) -> None:
  412. self.tracker.enter_scope(ScopeType.Func)
  413. super().visit_lambda_expr(o)
  414. self.tracker.exit_scope()
  415. def visit_assert_stmt(self, o: AssertStmt) -> None:
  416. super().visit_assert_stmt(o)
  417. if checker.is_false_literal(o.expr):
  418. self.tracker.skip_branch()
  419. def visit_raise_stmt(self, o: RaiseStmt) -> None:
  420. super().visit_raise_stmt(o)
  421. self.tracker.skip_branch()
  422. def visit_continue_stmt(self, o: ContinueStmt) -> None:
  423. super().visit_continue_stmt(o)
  424. self.tracker.skip_branch()
  425. def visit_break_stmt(self, o: BreakStmt) -> None:
  426. super().visit_break_stmt(o)
  427. if self.loops:
  428. self.loops[-1].has_break = True
  429. self.tracker.skip_branch()
  430. def visit_expression_stmt(self, o: ExpressionStmt) -> None:
  431. if isinstance(self.type_map.get(o.expr, None), UninhabitedType):
  432. self.tracker.skip_branch()
  433. super().visit_expression_stmt(o)
  434. def visit_try_stmt(self, o: TryStmt) -> None:
  435. """
  436. Note that finding undefined vars in `finally` requires different handling from
  437. the rest of the code. In particular, we want to disallow skipping branches due to jump
  438. statements in except/else clauses for finally but not for other cases. Imagine a case like:
  439. def f() -> int:
  440. try:
  441. x = 1
  442. except:
  443. # This jump statement needs to be handled differently depending on whether or
  444. # not we're trying to process `finally` or not.
  445. return 0
  446. finally:
  447. # `x` may be undefined here.
  448. pass
  449. # `x` is always defined here.
  450. return x
  451. """
  452. self.try_depth += 1
  453. if o.finally_body is not None:
  454. # In order to find undefined vars in `finally`, we need to
  455. # process try/except with branch skipping disabled. However, for the rest of the code
  456. # after finally, we need to process try/except with branch skipping enabled.
  457. # Therefore, we need to process try/finally twice.
  458. # Because processing is not idempotent, we should make a copy of the tracker.
  459. old_tracker = self.tracker.copy()
  460. self.tracker.disable_branch_skip = True
  461. self.process_try_stmt(o)
  462. self.tracker = old_tracker
  463. self.process_try_stmt(o)
  464. self.try_depth -= 1
  465. def process_try_stmt(self, o: TryStmt) -> None:
  466. """
  467. Processes try statement decomposing it into the following:
  468. if ...:
  469. body
  470. else_body
  471. elif ...:
  472. except 1
  473. elif ...:
  474. except 2
  475. else:
  476. except n
  477. finally
  478. """
  479. self.tracker.start_branch_statement()
  480. o.body.accept(self)
  481. if o.else_body is not None:
  482. o.else_body.accept(self)
  483. if len(o.handlers) > 0:
  484. assert len(o.handlers) == len(o.vars) == len(o.types)
  485. for i in range(len(o.handlers)):
  486. self.tracker.next_branch()
  487. exc_type = o.types[i]
  488. if exc_type is not None:
  489. exc_type.accept(self)
  490. var = o.vars[i]
  491. if var is not None:
  492. self.process_definition(var.name)
  493. var.accept(self)
  494. o.handlers[i].accept(self)
  495. if var is not None:
  496. self.tracker.delete_var(var.name)
  497. self.tracker.end_branch_statement()
  498. if o.finally_body is not None:
  499. o.finally_body.accept(self)
  500. def visit_while_stmt(self, o: WhileStmt) -> None:
  501. o.expr.accept(self)
  502. self.tracker.start_branch_statement()
  503. loop = Loop()
  504. self.loops.append(loop)
  505. o.body.accept(self)
  506. has_break = loop.has_break
  507. if not checker.is_true_literal(o.expr):
  508. # If this is a loop like `while True`, we can consider the body to be
  509. # a single branch statement (we're guaranteed that the body is executed at least once).
  510. # If not, call next_branch() to make all variables defined there conditional.
  511. self.tracker.next_branch()
  512. self.tracker.end_branch_statement()
  513. if o.else_body is not None:
  514. # If the loop has a `break` inside, `else` is executed conditionally.
  515. # If the loop doesn't have a `break` either the function will return or
  516. # execute the `else`.
  517. if has_break:
  518. self.tracker.start_branch_statement()
  519. self.tracker.next_branch()
  520. if o.else_body:
  521. o.else_body.accept(self)
  522. if has_break:
  523. self.tracker.end_branch_statement()
  524. self.loops.pop()
  525. def visit_as_pattern(self, o: AsPattern) -> None:
  526. if o.name is not None:
  527. self.process_lvalue(o.name)
  528. super().visit_as_pattern(o)
  529. def visit_starred_pattern(self, o: StarredPattern) -> None:
  530. if o.capture is not None:
  531. self.process_lvalue(o.capture)
  532. super().visit_starred_pattern(o)
  533. def visit_name_expr(self, o: NameExpr) -> None:
  534. if o.name in self.builtins and self.tracker.in_scope(ScopeType.Global):
  535. return
  536. if self.tracker.is_possibly_undefined(o.name):
  537. # A variable is only defined in some branches.
  538. self.variable_may_be_undefined(o.name, o)
  539. # We don't want to report the error on the same variable multiple times.
  540. self.tracker.record_definition(o.name)
  541. elif self.tracker.is_defined_in_different_branch(o.name):
  542. # A variable is defined in one branch but used in a different branch.
  543. if self.loops or self.try_depth > 0:
  544. # If we're in a loop or in a try, we can't be sure that this variable
  545. # is undefined. Report it as "may be undefined".
  546. self.variable_may_be_undefined(o.name, o)
  547. else:
  548. self.var_used_before_def(o.name, o)
  549. elif self.tracker.is_undefined(o.name):
  550. # A variable is undefined. It could be due to two things:
  551. # 1. A variable is just totally undefined
  552. # 2. The variable is defined later in the code.
  553. # Case (1) will be caught by semantic analyzer. Case (2) is a forward ref that should
  554. # be caught by this visitor. Save the ref for later, so that if we see a definition,
  555. # we know it's a used-before-definition scenario.
  556. self.tracker.record_undefined_ref(o)
  557. super().visit_name_expr(o)
  558. def visit_with_stmt(self, o: WithStmt) -> None:
  559. for expr, idx in zip(o.expr, o.target):
  560. expr.accept(self)
  561. self.process_lvalue(idx)
  562. o.body.accept(self)
  563. def visit_class_def(self, o: ClassDef) -> None:
  564. self.process_definition(o.name)
  565. self.tracker.enter_scope(ScopeType.Class)
  566. super().visit_class_def(o)
  567. self.tracker.exit_scope()
  568. def visit_import(self, o: Import) -> None:
  569. for mod, alias in o.ids:
  570. if alias is not None:
  571. self.tracker.record_definition(alias)
  572. else:
  573. # When you do `import x.y`, only `x` becomes defined.
  574. names = mod.split(".")
  575. if names:
  576. # `names` should always be nonempty, but we don't want mypy
  577. # to crash on invalid code.
  578. self.tracker.record_definition(names[0])
  579. super().visit_import(o)
  580. def visit_import_from(self, o: ImportFrom) -> None:
  581. for mod, alias in o.names:
  582. name = alias
  583. if name is None:
  584. name = mod
  585. self.tracker.record_definition(name)
  586. super().visit_import_from(o)