Repository: python/mypy

Issue:
New semantic analyzer: support six.with_metaclass etc.
#6349 added support for metaclasses, but `six.with_metaclass` and
`@six.add_metaclass` still have not been tested.


Discussion:
Raising priority to high, since this causes a bunch of errors in S.

Resolving patch:
diff --git a/mypy/newsemanal/semanal.py b/mypy/newsemanal/semanal.py
--- a/mypy/newsemanal/semanal.py
+++ b/mypy/newsemanal/semanal.py
@@ -855,12 +855,13 @@ def analyze_class(self, defn: ClassDef) -> None:
 
         tag = self.track_incomplete_refs()
 
-        bases = defn.base_type_exprs
-
-        self.update_metaclass(defn)
         # Restore base classes after previous iteration (things like Generic[T] might be removed).
         defn.base_type_exprs.extend(defn.removed_base_type_exprs)
         defn.removed_base_type_exprs.clear()
+
+        self.update_metaclass(defn)
+
+        bases = defn.base_type_exprs
         bases, tvar_defs, is_protocol = self.clean_up_bases_and_infer_type_variables(defn, bases,
                                                                                      context=defn)
         self.analyze_class_keywords(defn)


Test patch:
diff --git a/mypy/test/hacks.py b/mypy/test/hacks.py
--- a/mypy/test/hacks.py
+++ b/mypy/test/hacks.py
@@ -8,7 +8,6 @@
 # Files to not run with new semantic analyzer.
 new_semanal_blacklist = [
     'check-async-await.test',
-    'check-classes.test',
     'check-expressions.test',
     'check-flags.test',
     'check-functions.test',
diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test
--- a/test-data/unit/check-classes.test
+++ b/test-data/unit/check-classes.test
@@ -230,6 +230,7 @@ class D(object):
 [out]
 
 [case testClassNamesDefinedOnSelUsedInClassBodyReveal]
+# flags: --no-new-semantic-analyzer
 class A(object):
     def f(self) -> None:
         self.attr = 1
@@ -4329,6 +4330,8 @@ class A(Tuple[int, str]): pass
 -- -----------------------
 
 [case testCrashOnSelfRecursiveNamedTupleVar]
+# FIXME: #6445
+# flags: --no-new-semantic-analyzer
 from typing import NamedTuple
 
 N = NamedTuple('N', [('x', N)]) # E: Recursive types not fully supported yet, nested types replaced with "Any"
@@ -4336,6 +4339,8 @@ n: N
 [out]
 
 [case testCrashOnSelfRecursiveTypedDictVar]
+# FIXME: #6445
+# flags: --no-new-semantic-analyzer
 from mypy_extensions import TypedDict
 
 A = TypedDict('A', {'a': 'A'})  # type: ignore
@@ -4344,6 +4349,8 @@ a: A
 [out]
 
 [case testCrashInJoinOfSelfRecursiveNamedTuples]
+# FIXME: #6445
+# flags: --no-new-semantic-analyzer
 from typing import NamedTuple
 
 class N(NamedTuple): # type: ignore
@@ -4357,11 +4364,13 @@ lst = [n, m]
 [builtins fixtures/isinstancelist.pyi]
 
 [case testCorrectJoinOfSelfRecursiveTypedDicts]
+# FIXME: #6445
+# flags: --no-new-semantic-analyzer
 from mypy_extensions import TypedDict
 
-class N(TypedDict):
+class N(TypedDict):  # E: Recursive types not fully supported yet, nested types replaced with "Any"
     x: N
-class M(TypedDict):
+class M(TypedDict):  # E: Recursive types not fully supported yet, nested types replaced with "Any"
     x: M
 
 n: N
@@ -4369,9 +4378,6 @@ m: M
 lst = [n, m]
 reveal_type(lst[0]['x'])  # E: Revealed type is 'TypedDict('__main__.N', {'x': Any})'
 [builtins fixtures/isinstancelist.pyi]
-[out]
-main:3: error: Recursive types not fully supported yet, nested types replaced with "Any"
-main:5: error: Recursive types not fully supported yet, nested types replaced with "Any"
 
 [case testCrashInForwardRefToNamedTupleWithIsinstance]
 from typing import Dict, NamedTuple
@@ -4461,6 +4467,7 @@ x = NT(N(1))
 [out]
 
 [case testNewTypeFromForwardTypedDict]
+# flags: --no-new-semantic-analyzer
 from typing import NewType, Tuple
 from mypy_extensions import TypedDict
 
diff --git a/test-data/unit/check-newsemanal.test b/test-data/unit/check-newsemanal.test
--- a/test-data/unit/check-newsemanal.test
+++ b/test-data/unit/check-newsemanal.test
@@ -995,6 +995,63 @@ class C(type):
 
 reveal_type(A.f()) # E: Revealed type is 'builtins.int'
 
+[case testNewAnalyzerMetaclassSix1]
+import six
+
+class A(six.with_metaclass(B)):
+    pass
+
+class B(type):
+    def f(cls) -> int:
+        return 0
+
+reveal_type(A.f()) # E: Revealed type is 'builtins.int'
+
+[case testNewAnalyzerMetaclassSix2]
+import six
+
+@six.add_metaclass(B)
+class A:
+    pass
+
+class B(type):
+    def f(cls) -> int:
+        return 0
+
+reveal_type(A.f()) # E: Revealed type is 'builtins.int'
+
+[case testNewAnalyzerMetaclassSix3]
+import six
+
+class A(six.with_metaclass(B, Defer)):
+    pass
+
+class B(type):
+    def f(cls) -> int:
+        return 0
+
+class Defer:
+    x: str
+
+reveal_type(A.f()) # E: Revealed type is 'builtins.int'
+reveal_type(A.x) # E: Revealed type is 'builtins.str'
+
+[case testNewAnalyzerMetaclassSix4]
+import six
+
+class B(type):
+    def f(cls) -> int:
+        return 0
+
+reveal_type(A.f()) # E: Revealed type is 'builtins.int'
+reveal_type(A.x) # E: Revealed type is 'builtins.str'
+
+class A(six.with_metaclass(B, Defer)):
+    pass
+
+class Defer:
+    x: str
+
 [case testNewAnalyzerMetaclass1_python2]
 class A:
     __metaclass__ = B