Repository: python/mypy

Issue:
Bare ClassVar should infer type instead of defaulting to Any
Currently we have:
```python
from typing import ClassVar
  
class C:
    x: ClassVar = 1

reveal_type(C.x)  # Revealed type is 'Any'
```
`ClassVar` is not a generic type (and not really a type, rather an access qualifier). So it will be better if a bare `ClassVar` would use inference instead of defaulting to `Any` thus reducing verbosity or false negatives (although `--disallow-any-generics` actually flags this). 

Probably not high priority.


Discussion:
I agree. Should this become an update to PEP 526?
> Should this become an update to PEP 526?

I think it is worth explicitly mentioning it in the PEP.
OK, but is it still provisional? It was provisionally accepted for 3.6. Can we still change it? (PEP 411 suggests provisional applies to a single feature release.)
Hm, this is a good question. I think PEP 484 stayed provisional longer than that, so there is a precedent. Also this is not runtime-affecting thing, so I would say this is OK.
Fair enough.
This is now part of the typing spec:
- https://github.com/python/typing-council/issues/43
- https://typing.python.org/en/latest/spec/class-compat.html#classvar



Patch:
diff --git a/mypy/semanal.py b/mypy/semanal.py
index 7cca406b661b..ab9075cd06ce 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,12 @@ 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:
+            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:
         if not isinstance(typ, UnboundType):


Test patch:
diff --git a/test-data/unit/check-classvar.test b/test-data/unit/check-classvar.test
index 8384e5624793..7918ccded2fe 100644
--- a/test-data/unit/check-classvar.test
+++ b/test-data/unit/check-classvar.test
@@ -360,3 +360,23 @@ 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"
+
+[case testClassVarBareAnnotationDisabled]
+# flags: --disallow-any-generics
+from typing import ClassVar
+
+class C:
+    x: ClassVar = 1
+    y: ClassVar  # E: ClassVar without type argument becomes 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]