Detects the following cases: - A variable that's defined only part of the time. - If a variable is used before definition An example of a partial definition: if foo(): x = 1 print(x) # Error: "x" may be undefined. Example of a used before definition: x = y
| 302 | |
| 303 | |
| 304 | class PossiblyUndefinedVariableVisitor(ExtendedTraverserVisitor): |
| 305 | """Detects the following cases: |
| 306 | - A variable that's defined only part of the time. |
| 307 | - If a variable is used before definition |
| 308 | |
| 309 | An example of a partial definition: |
| 310 | if foo(): |
| 311 | x = 1 |
| 312 | print(x) # Error: "x" may be undefined. |
| 313 | |
| 314 | Example of a used before definition: |
| 315 | x = y |
| 316 | y: int = 2 |
| 317 | |
| 318 | Note that this code does not detect variables not defined in any of the branches -- that is |
| 319 | handled by the semantic analyzer. |
| 320 | """ |
| 321 | |
| 322 | def __init__( |
| 323 | self, |
| 324 | msg: MessageBuilder, |
| 325 | type_map: dict[Expression, Type], |
| 326 | options: Options, |
| 327 | names: SymbolTable, |
| 328 | ) -> None: |
| 329 | self.msg = msg |
| 330 | self.type_map = type_map |
| 331 | self.options = options |
| 332 | self.builtins = SymbolTable() |
| 333 | builtins_mod = names.get("__builtins__", None) |
| 334 | if builtins_mod: |
| 335 | assert isinstance(builtins_mod.node, MypyFile) |
| 336 | self.builtins = builtins_mod.node.names |
| 337 | self.loops: list[Loop] = [] |
| 338 | self.try_depth = 0 |
| 339 | self.tracker = DefinedVariableTracker() |
| 340 | for name in implicit_module_attrs: |
| 341 | self.tracker.record_definition(name) |
| 342 | |
| 343 | def var_used_before_def(self, name: str, context: Context) -> None: |
| 344 | if self.msg.errors.is_error_code_enabled(errorcodes.USED_BEFORE_DEF): |
| 345 | self.msg.var_used_before_def(name, context) |
| 346 | |
| 347 | def variable_may_be_undefined(self, name: str, context: Context) -> None: |
| 348 | if self.msg.errors.is_error_code_enabled(errorcodes.POSSIBLY_UNDEFINED): |
| 349 | self.msg.variable_may_be_undefined(name, context) |
| 350 | |
| 351 | def process_definition(self, name: str) -> None: |
| 352 | # Was this name previously used? If yes, it's a used-before-definition error. |
| 353 | if not self.tracker.in_scope(ScopeType.Class): |
| 354 | refs = self.tracker.pop_undefined_ref(name) |
| 355 | for ref in refs: |
| 356 | if self.loops: |
| 357 | self.variable_may_be_undefined(name, ref) |
| 358 | else: |
| 359 | self.var_used_before_def(name, ref) |
| 360 | else: |
| 361 | # Errors in class scopes are caught by the semantic analyzer. |
no outgoing calls
no test coverage detected
searching dependent graphs…