Repository: python/mypy

Issue:
New semanal: Weird issues in namedtuple methods
This one is unfortunately not fully self-contained:
```
$ cat santa/services/task_queue.py 
from typing import Any, Counter, Dict, NamedTuple

from santa.services.base import TaskClsId


class GroupInfo(NamedTuple):
    log: Any
    result_to_count: Counter[Any]

    def log_failure_info(self) -> None:
        results = ", ".join(
            f"{result.name}: {count}"
            for result, count in self.result_to_count.most_common()
        )
        self.log.info(f"{results}")
        most_common = next(self.result_to_count.elements())
        suggestions: Dict[Any, str] = {}
        self.log.info(suggestions[most_common])
$ mypy --new-semantic-analyzer --config-file santa/mypy.ini santa/services/task_queue.py 
santa/services/task_queue.py:12:13: error: Name 'count' is not defined
santa/services/task_queue.py:12:16: error: Name 'result' is not defined
santa/services/task_queue.py:15:23: error: Cannot resolve name "results" (possible cyclic definition)
santa/services/task_queue.py:18:23: error: Name 'suggestions' is not defined
santa/services/task_queue.py:18:35: error: Name 'most_common' is not defined
```
The TaskClsId import is unused, but when I remove it all the errors go away. TaskClsId is just a newtype, but the files it imports from lead to an import cycle. I haven't had much luck so far trying to minimize it further.


Discussion:
Here is a self-contained repro:

```py
from typing import Any, Counter, Dict, NamedTuple

g: GroupInfo

class GroupInfo(NamedTuple):
    log: Any
    result_to_count: Counter[Any]

    def log_failure_info(self) -> None:
        results = ", ".join(
            f"{result.name}: {count}"
            for result, count in self.result_to_count.most_common()
        )
        self.log.info(f"{results}")
        most_common = next(self.result_to_count.elements())
        suggestions: Dict[Any, str] = {}
        self.log.info(suggestions[most_common])
```

Resolving patch:
diff --git a/mypy/newsemanal/semanal_namedtuple.py b/mypy/newsemanal/semanal_namedtuple.py
--- a/mypy/newsemanal/semanal_namedtuple.py
+++ b/mypy/newsemanal/semanal_namedtuple.py
@@ -437,9 +437,11 @@ def add_method(funcname: str,
                 v._fullname = func._fullname
                 func.is_decorated = True
                 dec = Decorator(func, [NameExpr('classmethod')], v)
-                info.names[funcname] = SymbolTableNode(MDEF, dec)
+                sym = SymbolTableNode(MDEF, dec)
             else:
-                info.names[funcname] = SymbolTableNode(MDEF, func)
+                sym = SymbolTableNode(MDEF, func)
+            sym.plugin_generated = True
+            info.names[funcname] = sym
 
         add_method('_replace', ret=selftype,
                    args=[Argument(var, var.type, EllipsisExpr(), ARG_NAMED_OPT) for var in vars])
@@ -495,10 +497,14 @@ def save_namedtuple_body(self, named_tuple_info: TypeInfo) -> Iterator[None]:
             if key in named_tuple_info.names:
                 if key == '__doc__':
                     continue
+                sym = named_tuple_info.names[key]
+                if isinstance(sym.node, (FuncBase, Decorator)) and not sym.plugin_generated:
+                    # Keep user-defined methods as is.
+                    continue
                 # Keep existing (user-provided) definitions under mangled names, so they
                 # get semantically analyzed.
                 r_key = get_unique_redefinition_name(key, named_tuple_info.names)
-                named_tuple_info.names[r_key] = named_tuple_info.names[key]
+                named_tuple_info.names[r_key] = sym
             named_tuple_info.names[key] = value
 
     # Helpers


Test patch:
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
@@ -2599,3 +2599,17 @@ import a.b.c
 from a.b.a import foo
 [builtins fixtures/module.pyi]
 [out]
+
+[case testNewAnalyzerNamedTupleMethod]
+from typing import NamedTuple
+
+g: N
+
+class N(NamedTuple):
+    def f(self) -> None:
+        b = (
+            a
+            for a in [1]
+        )
+        b
+[builtins fixtures/tuple.pyi]