From b4f5bc2896a5124459b277b08e5f74c1da4a15fc Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 2 Aug 2025 19:47:02 +0100 Subject: [PATCH 1/2] Interpret bare ClassVar as inferred, not Any --- mypy/semanal.py | 3 +++ test-data/unit/check-classvar.test | 12 ++++++++++++ test-data/unit/check-incremental.test | 2 +- test-data/unit/semanal-classvar.test | 2 +- 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 7cca406b661b..7acb94eb1af0 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -5094,6 +5094,7 @@ def check_classvar(self, s: AssignmentStmt) -> None: return if not s.type or not self.is_classvar(s.type): return + assert isinstance(s.type, UnboundType) if self.is_class_scope() and isinstance(lvalue, NameExpr): node = lvalue.node if isinstance(node, Var): @@ -5110,6 +5111,8 @@ def check_classvar(self, s: AssignmentStmt) -> None: # In case of member access, report error only when assigning to self # Other kinds of member assignments should be already reported self.fail_invalid_classvar(lvalue) + if not s.type.args: + s.type = None def is_classvar(self, typ: Type) -> bool: if not isinstance(typ, UnboundType): diff --git a/test-data/unit/check-classvar.test b/test-data/unit/check-classvar.test index 8384e5624793..492b37b56290 100644 --- a/test-data/unit/check-classvar.test +++ b/test-data/unit/check-classvar.test @@ -360,3 +360,15 @@ reveal_type(C.x) # E: Access to generic instance variables via class is ambiguo # N: Revealed type is "Any" reveal_type(C.y) # E: Access to generic class variables is ambiguous \ # N: Revealed type is "Any" + +[case testClassVarBareAnnotation] +from typing import ClassVar + +class C: + x: ClassVar = 1 + y: ClassVar + +reveal_type(C.x) # N: Revealed type is "builtins.int" +reveal_type(C().x) # N: Revealed type is "builtins.int" +reveal_type(C.y) # N: Revealed type is "Any" +reveal_type(C().y) # N: Revealed type is "Any" diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 4c170ec4753f..3c11166ef9af 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -2051,7 +2051,7 @@ warn_no_return = True [case testIncrementalClassVar] from typing import ClassVar class A: - x = None # type: ClassVar + x: ClassVar A().x = 0 [out1] main:4: error: Cannot assign to class variable "x" via instance diff --git a/test-data/unit/semanal-classvar.test b/test-data/unit/semanal-classvar.test index 8add559bdd27..62151666c011 100644 --- a/test-data/unit/semanal-classvar.test +++ b/test-data/unit/semanal-classvar.test @@ -52,7 +52,7 @@ MypyFile:1( AssignmentStmt:3( NameExpr(x [m]) IntExpr(1) - Any))) + builtins.int))) [case testClassVarWithTypeVar] From cb348188a3fb543e8557569c15397e959691fda0 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 3 Aug 2025 17:35:31 +0100 Subject: [PATCH 2/2] Flag empty ClassVar under a flag --- mypy/semanal.py | 4 ++++ test-data/unit/check-classvar.test | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/mypy/semanal.py b/mypy/semanal.py index 7acb94eb1af0..ab9075cd06ce 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -5112,6 +5112,10 @@ def check_classvar(self, s: AssignmentStmt) -> None: # Other kinds of member assignments should be already reported self.fail_invalid_classvar(lvalue) if not s.type.args: + if isinstance(s.rvalue, TempNode) and s.rvalue.no_rhs: + if self.options.disallow_any_generics: + self.fail("ClassVar without type argument becomes Any", s, code=codes.TYPE_ARG) + return s.type = None def is_classvar(self, typ: Type) -> bool: diff --git a/test-data/unit/check-classvar.test b/test-data/unit/check-classvar.test index 492b37b56290..7918ccded2fe 100644 --- a/test-data/unit/check-classvar.test +++ b/test-data/unit/check-classvar.test @@ -372,3 +372,11 @@ reveal_type(C.x) # N: Revealed type is "builtins.int" reveal_type(C().x) # N: Revealed type is "builtins.int" reveal_type(C.y) # N: Revealed type is "Any" reveal_type(C().y) # N: Revealed type is "Any" + +[case testClassVarBareAnnotationDisabled] +# flags: --disallow-any-generics +from typing import ClassVar + +class C: + x: ClassVar = 1 + y: ClassVar # E: ClassVar without type argument becomes Any