diff --git a/mypy/semanal.py b/mypy/semanal.py index a958043fa35c..b26ef45ef562 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -6754,7 +6754,23 @@ def lookup_qualified( # See https://github.com/python/mypy/pull/13468 if isinstance(node, ParamSpecExpr) and part in ("args", "kwargs"): return None - # Lookup through invalid node, such as variable or function + if not suppress_errors and isinstance(node, (Var, FuncDef, Decorator)): + # Attribute access through a variable or function cannot be + # resolved by mypy. Give a more specific error than "Name ... is + # not defined" so the user understands *why*. + prefix = ".".join(parts[:i]) + if isinstance(node, (FuncDef, Decorator)): + kind = "a function" + else: + kind = "a variable" + self.fail( + f'Name "{name}" is not defined' + f' ("{prefix}" is {kind}, not a module or class)', + ctx, + code=codes.NAME_DEFINED, + ) + return None + # Lookup through invalid node nextsym = None if not nextsym or nextsym.module_hidden: if not suppress_errors: diff --git a/test-data/unit/check-namedtuple.test b/test-data/unit/check-namedtuple.test index 285ae92325d8..faf6eb684d1b 100644 --- a/test-data/unit/check-namedtuple.test +++ b/test-data/unit/check-namedtuple.test @@ -1357,7 +1357,7 @@ from typing import NamedTuple class Test: def __init__(self, field) -> None: self.Item = NamedTuple("x", [(field, str)]) # E: NamedTuple type as an attribute is not supported - self.item: self.Item # E: Name "self.Item" is not defined + self.item: self.Item # E: Name "self.Item" is not defined ("self" is a variable, not a module or class) [builtins fixtures/tuple.pyi] [typing fixtures/typing-namedtuple.pyi] diff --git a/test-data/unit/check-newsemanal.test b/test-data/unit/check-newsemanal.test index 4148d04014a8..21acdd699459 100644 --- a/test-data/unit/check-newsemanal.test +++ b/test-data/unit/check-newsemanal.test @@ -2531,7 +2531,7 @@ from p.c import B [case testNewSemanticAnalyzerQualifiedFunctionAsType] import m -x: m.C.a.b # E: Name "m.C.a.b" is not defined +x: m.C.a.b # E: Name "m.C.a.b" is not defined ("m.C" is a function, not a module or class) [file m.py] def C(): pass @@ -2569,8 +2569,7 @@ class C: class E: pass def f(self) -> None: - # TODO: Error message could be better - class D(self.E): # E: Name "self.E" is not defined + class D(self.E): # E: Name "self.E" is not defined ("self" is a variable, not a module or class) pass [case testNewAnalyzerShadowOuterDefinitionBasedOnOrderSinglePass] @@ -2813,7 +2812,7 @@ class B: ... [case testNewAnalyzerImportAmbiguousWithTopLevelFunction] import a.b # This works at runtime -x: a.b.B # E: Name "a.b.B" is not defined +x: a.b.B # E: Name "a.b.B" is not defined ("a.b" is a function, not a module or class) reveal_type(a.b) # N: Revealed type is "def ()" [file a/__init__.py] import a.b diff --git a/test-data/unit/check-semanal-error.test b/test-data/unit/check-semanal-error.test index 52abbf09f1e5..5f4bd3513903 100644 --- a/test-data/unit/check-semanal-error.test +++ b/test-data/unit/check-semanal-error.test @@ -61,6 +61,15 @@ main:3: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#varia main:3: error: Invalid base class "X" main:6: error: Incompatible types in assignment (expression has type "str", variable has type "int") +[case testInvalidBaseClass3_AttributeAccess] +class A: + pass +class B: + def __init__(self) -> None: + self.a = A +b = B() +class C(b.a): # E: Name "b.a" is not defined ("b" is a variable, not a module or class) + pass [case testInvalidNumberOfTypeArgs] from typing import TypeVar diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index 4d68f93a21ed..68f5093f98da 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -884,19 +884,19 @@ reveal_type(plausible_child_4) # N: Revealed type is "def () -> builtins.int | # Use type aliases in a type alias context in an implausible way -def weird_parent_1() -> p.NormalImplicit: pass # E: Name "p.NormalImplicit" is not defined -def weird_parent_2() -> p.NormalExplicit: pass # E: Name "p.NormalExplicit" is not defined -def weird_parent_3() -> p.SpecialImplicit: pass # E: Name "p.SpecialImplicit" is not defined -def weird_parent_4() -> p.SpecialExplicit: pass # E: Name "p.SpecialExplicit" is not defined +def weird_parent_1() -> p.NormalImplicit: pass # E: Name "p.NormalImplicit" is not defined ("p" is a variable, not a module or class) +def weird_parent_2() -> p.NormalExplicit: pass # E: Name "p.NormalExplicit" is not defined ("p" is a variable, not a module or class) +def weird_parent_3() -> p.SpecialImplicit: pass # E: Name "p.SpecialImplicit" is not defined ("p" is a variable, not a module or class) +def weird_parent_4() -> p.SpecialExplicit: pass # E: Name "p.SpecialExplicit" is not defined ("p" is a variable, not a module or class) reveal_type(weird_parent_1) # N: Revealed type is "def () -> Any" reveal_type(weird_parent_2) # N: Revealed type is "def () -> Any" reveal_type(weird_parent_3) # N: Revealed type is "def () -> Any" reveal_type(weird_parent_4) # N: Revealed type is "def () -> Any" -def weird_child_1() -> c.NormalImplicit: pass # E: Name "c.NormalImplicit" is not defined -def weird_child_2() -> c.NormalExplicit: pass # E: Name "c.NormalExplicit" is not defined -def weird_child_3() -> c.SpecialImplicit: pass # E: Name "c.SpecialImplicit" is not defined -def weird_child_4() -> c.SpecialExplicit: pass # E: Name "c.SpecialExplicit" is not defined +def weird_child_1() -> c.NormalImplicit: pass # E: Name "c.NormalImplicit" is not defined ("c" is a variable, not a module or class) +def weird_child_2() -> c.NormalExplicit: pass # E: Name "c.NormalExplicit" is not defined ("c" is a variable, not a module or class) +def weird_child_3() -> c.SpecialImplicit: pass # E: Name "c.SpecialImplicit" is not defined ("c" is a variable, not a module or class) +def weird_child_4() -> c.SpecialExplicit: pass # E: Name "c.SpecialExplicit" is not defined ("c" is a variable, not a module or class) reveal_type(weird_child_1) # N: Revealed type is "def () -> Any" reveal_type(weird_child_2) # N: Revealed type is "def () -> Any" reveal_type(weird_child_3) # N: Revealed type is "def () -> Any"