Repository: python/mypy

Issue:
attrs plugin fails to type check against AttrsInstance protocol with warm cache
**Bug Report**

Program using protocol for attrs instances fails to typecheck once the mypy cache is warm. 

**To Reproduce**

Given a virtualenv containing:
```bash
$ pip freeze
attrs==22.1.0
mypy==0.991
mypy-extensions==0.4.3
tomli==2.0.1
typing_extensions==4.4.0
```

a.py:
```python
import attr
from typing import Protocol, ClassVar, Any

class AttrsInstance(Protocol):
    """
    A protocol to be able to statically accept an attrs class.
    attr itself defines AttrsInstance but only at type checking time.
    """

    __attrs_attrs__: ClassVar[Any]


@attr.s
class Foo:
    bar: str = attr.ib()
```

b.py:
```python
from a import AttrsInstance, Foo
from typing import Type

thing: Type[AttrsInstance] = Foo 
print(thing)
```

First try with cold cache:
```bash
$ rm -rf .mypy_cache/  # Make sure the cache is cold
$ mypy a.py b.py
Success: no issues found in 2 source files
```

Now edit b.py adding one line at the end for example:
```python
from a import AttrsInstance, Foo
from typing import Type

thing: Type[AttrsInstance] = Foo 
print(thing)

foo = False
```

And re-run mypy, getting error unrelated to the change!
```bash
$ mypy a.py b.py
b.py:4: error: Incompatible types in assignment (expression has type "Type[Foo]", variable has type "Type[AttrsInstance]")  [assignment]
Found 1 error in 1 file (checked 2 source files)
```

Deleting the cache makes it work again:
```bash
$ rm -r .mypy_cache/
$ mypy a.py b.py
```

Note that we also get an error if using `AttrsInstance` directly instead of `Type[AttrsInstance]`:

Using this b.py:
```Python
from a import AttrsInstance, Foo
from typing import Type

thing: AttrsInstance = Foo("bar")
print(thing)
```

Results in this after a similar edit to b.py:
```
(virtualenv) thomas@thomas-xps13:~/Projects/test$ mypy a.py b.py
b.py:4: error: Incompatible types in assignment (expression has type "Foo", variable has type "AttrsInstance")  [assignment]
b.py:4: note: Protocol member AttrsInstance.__attrs_attrs__ expected class variable, got instance variable
b.py:4: note: Protocol member AttrsInstance.__attrs_attrs__ expected settable variable, got read-only attribute
Found 1 error in 1 file (checked 2 source files)
```

**Expected Behavior**

Program should still typecheck correctly with warm cache.

**Actual Behavior**

Program fails to typecheck. Deleting the cache and re-running works as a (slow) workaround.

**Your Environment**

<!-- Include as many relevant details about the environment you experienced the bug in -->

- Mypy version used: 0.991
- Mypy command-line flags: mypy a.py b.py
- Mypy configuration options from `mypy.ini` (and other config files): none
- Python version used: 3.10.6 (ubuntu 22.04, package version 3.10.6-1~22.04.1)


Discussion:

Resolving patch:
diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py
--- a/mypy/plugins/attrs.py
+++ b/mypy/plugins/attrs.py
@@ -828,7 +828,7 @@ def _add_attrs_magic_attribute(
         ctx.cls,
         MAGIC_ATTR_NAME,
         TupleType(attributes_types, fallback=attributes_type),
-        fullname=f"{ctx.cls.fullname}.{attr_name}",
+        fullname=f"{ctx.cls.fullname}.{MAGIC_ATTR_NAME}",
         override_allow_incompatible=True,
         is_classvar=True,
     )


Test patch:
diff --git a/test-data/unit/fine-grained-attr.test b/test-data/unit/fine-grained-attr.test
--- a/test-data/unit/fine-grained-attr.test
+++ b/test-data/unit/fine-grained-attr.test
@@ -46,3 +46,37 @@ A.__attrs_attrs__.b
 
 [out]
 ==
+
+[case magicAttributeConsistency2-only_when_cache]
+[file c.py]
+import attr
+
+@attr.s
+class Entry:
+    var: int = attr.ib()
+[builtins fixtures/attr.pyi]
+
+[file m.py]
+from typing import Any, ClassVar, Protocol
+from c import Entry
+
+class AttrsInstance(Protocol):
+    __attrs_attrs__: ClassVar[Any]
+
+def func(e: AttrsInstance) -> None: ...
+func(Entry(2))
+
+[file m.py.2]
+from typing import Any, ClassVar, Protocol
+from c import Entry
+
+class AttrsInstance(Protocol):
+    __attrs_attrs__: ClassVar[Any]
+
+def func(e: AttrsInstance) -> int:
+    return 2  # Change return type to force reanalysis
+
+func(Entry(2))
+
+[out]
+==