diff --git a/mypy/build.py b/mypy/build.py index 1747c4518c63..a4817d1866c7 100644 --- a/mypy/build.py +++ b/mypy/build.py @@ -2359,7 +2359,10 @@ def detect_possibly_undefined_vars(self) -> None: ) or manager.errors.is_error_code_enabled(codes.USED_BEFORE_DEF): self.tree.accept( PossiblyUndefinedVariableVisitor( - MessageBuilder(manager.errors, manager.modules), self.type_map(), self.options + MessageBuilder(manager.errors, manager.modules), + self.type_map(), + self.options, + self.tree.names, ) ) diff --git a/mypy/partially_defined.py b/mypy/partially_defined.py index 9a58df04371f..af09493c9cae 100644 --- a/mypy/partially_defined.py +++ b/mypy/partially_defined.py @@ -27,12 +27,13 @@ ListExpr, Lvalue, MatchStmt, + MypyFile, NameExpr, NonlocalDecl, RaiseStmt, - RefExpr, ReturnStmt, StarExpr, + SymbolTable, TryStmt, TupleExpr, WhileStmt, @@ -286,10 +287,6 @@ def is_undefined(self, name: str) -> bool: return self._scope().branch_stmts[-1].is_undefined(name) -def refers_to_builtin(o: RefExpr) -> bool: - return o.fullname.startswith("builtins.") - - class Loop: def __init__(self) -> None: self.has_break = False @@ -314,11 +311,20 @@ class PossiblyUndefinedVariableVisitor(ExtendedTraverserVisitor): """ def __init__( - self, msg: MessageBuilder, type_map: dict[Expression, Type], options: Options + self, + msg: MessageBuilder, + type_map: dict[Expression, Type], + options: Options, + names: SymbolTable, ) -> None: self.msg = msg self.type_map = type_map self.options = options + self.builtins = SymbolTable() + builtins_mod = names.get("__builtins__", None) + if builtins_mod: + assert isinstance(builtins_mod.node, MypyFile) + self.builtins = builtins_mod.node.names self.loops: list[Loop] = [] self.try_depth = 0 self.tracker = DefinedVariableTracker() @@ -597,7 +603,7 @@ def visit_starred_pattern(self, o: StarredPattern) -> None: super().visit_starred_pattern(o) def visit_name_expr(self, o: NameExpr) -> None: - if refers_to_builtin(o): + if o.name in self.builtins: return if self.tracker.is_possibly_undefined(o.name): # A variable is only defined in some branches. diff --git a/test-data/unit/check-possibly-undefined.test b/test-data/unit/check-possibly-undefined.test index 802635c30b35..29c4868e97af 100644 --- a/test-data/unit/check-possibly-undefined.test +++ b/test-data/unit/check-possibly-undefined.test @@ -909,6 +909,16 @@ def f0() -> None: type = "abc" a = type +[case testUsedBeforeDefBuiltinsMultipass] +# flags: --enable-error-code used-before-def + +# When doing multiple passes, mypy resolves references slightly differently. +# In this case, it would refer the earlier `type` call to the range class defined below. +_type = type # No error +_C = C # E: Name "C" is used before definition +class type: pass +class C: pass + [case testUsedBeforeDefImplicitModuleAttrs] # flags: --enable-error-code used-before-def a = __name__ # No error.