Skip to content

Commit 1cf1973

Browse files
authored
[ty] Use fully qualified names to distinguish ambiguous protocols in diagnostics (#20627)
1 parent 803d61e commit 1cf1973

File tree

2 files changed

+39
-2
lines changed

2 files changed

+39
-2
lines changed

crates/ty_python_semantic/resources/mdtest/diagnostics/same_names.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,36 @@ class Container(Generic[T]):
170170

171171
## Protocols
172172

173+
### Differing members
174+
175+
`bad.py`:
176+
177+
```py
178+
from typing import Protocol, TypeVar
179+
180+
T_co = TypeVar("T_co", covariant=True)
181+
182+
class Iterator(Protocol[T_co]):
183+
def __nexxt__(self) -> T_co: ...
184+
185+
def bad() -> Iterator[str]:
186+
raise NotImplementedError
187+
```
188+
189+
`main.py`:
190+
191+
```py
192+
from typing import Iterator
193+
194+
def f() -> Iterator[str]:
195+
import bad
196+
197+
# error: [invalid-return-type] "Return type does not match returned value: expected `typing.Iterator[str]`, found `bad.Iterator[str]"
198+
return bad.bad()
199+
```
200+
201+
### Same members but with different types
202+
173203
```py
174204
from typing import Protocol
175205
import proto_a

crates/ty_python_semantic/src/types/display.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ use crate::types::tuple::TupleSpec;
2222
use crate::types::visitor::TypeVisitor;
2323
use crate::types::{
2424
BoundTypeVarInstance, CallableType, IntersectionType, KnownBoundMethodType, KnownClass,
25-
MaterializationKind, Protocol, StringLiteralType, SubclassOfInner, Type, UnionType,
26-
WrapperDescriptorKind, visitor,
25+
MaterializationKind, Protocol, ProtocolInstanceType, StringLiteralType, SubclassOfInner, Type,
26+
UnionType, WrapperDescriptorKind, visitor,
2727
};
2828
use ruff_db::parsed::parsed_module;
2929

@@ -128,6 +128,13 @@ impl<'db> super::visitor::TypeVisitor<'db> for AmbiguousClassCollector<'db> {
128128
Type::ClassLiteral(class) => self.record_class(db, class),
129129
Type::EnumLiteral(literal) => self.record_class(db, literal.enum_class(db)),
130130
Type::GenericAlias(alias) => self.record_class(db, alias.origin(db)),
131+
// Visit the class (as if it were a nominal-instance type)
132+
// rather than the protocol members, if it is a class-based protocol.
133+
// (For the purposes of displaying the type, we'll use the class name.)
134+
Type::ProtocolInstance(ProtocolInstanceType {
135+
inner: Protocol::FromClass(class),
136+
..
137+
}) => return self.visit_type(db, Type::from(class)),
131138
_ => {}
132139
}
133140

0 commit comments

Comments
 (0)