Skip to content

Commit bcb3ca9

Browse files
committed
Fix collections.abc integration test and add special-casing for Sized to reduce false positives
1 parent a6f4152 commit bcb3ca9

File tree

7 files changed

+36
-15
lines changed

7 files changed

+36
-15
lines changed

crates/red_knot_python_semantic/resources/mdtest/annotations/stdlib_typing_aliases.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,7 @@ reveal_type(DictSubclass.__mro__)
8181

8282
class SetSubclass(typing.Set): ...
8383

84-
# TODO: should have `Generic`, should not have `Unknown`
85-
# revealed: tuple[Literal[SetSubclass], Literal[set], Unknown, Literal[object]]
84+
# revealed: tuple[Literal[SetSubclass], Literal[set], Literal[MutableSet], Literal[AbstractSet], Literal[Collection], Literal[Iterable], Literal[Container], @Todo(protocol), Literal[object]]
8685
reveal_type(SetSubclass.__mro__)
8786

8887
class FrozenSetSubclass(typing.FrozenSet): ...
@@ -114,8 +113,7 @@ reveal_type(DefaultDictSubclass.__mro__)
114113

115114
class DequeSubclass(typing.Deque): ...
116115

117-
# TODO: Should be (DequeSubclass, deque, MutableSequence, Sequence, Reversible, Collection, Sized, Iterable, Container, Generic, object)
118-
# revealed: tuple[Literal[DequeSubclass], Literal[deque], Unknown, Literal[object]]
116+
# revealed: tuple[Literal[DequeSubclass], Literal[deque], Literal[MutableSequence], Literal[Sequence], Literal[Reversible], Literal[Collection], Literal[Iterable], Literal[Container], @Todo(protocol), Literal[object]]
119117
reveal_type(DequeSubclass.__mro__)
120118

121119
class OrderedDictSubclass(typing.OrderedDict): ...

crates/red_knot_python_semantic/resources/mdtest/attributes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,7 @@ reveal_type(C().x) # revealed: str
551551
class C:
552552
def __init__(self) -> None:
553553
# error: [too-many-positional-arguments]
554+
# error: [invalid-argument-type]
554555
self.x: int = len(1, 2, 3)
555556
```
556557

crates/red_knot_python_semantic/resources/mdtest/expression/len.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,6 @@ reveal_type(len(SecondRequiredArgument())) # revealed: Literal[1]
229229
```py
230230
class NoDunderLen: ...
231231

232-
# TODO: Emit a diagnostic
232+
# error: [invalid-argument-type]
233233
reveal_type(len(NoDunderLen())) # revealed: int
234234
```

crates/red_knot_python_semantic/resources/mdtest/import/star.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -306,12 +306,13 @@ For `.pyi` files, we should consider all imports private to the stub unless they
306306

307307
```pyi
308308
X: bool = True
309+
Y: bool = True
309310
```
310311

311312
`b.pyi`:
312313

313314
```pyi
314-
from a import X
315+
from a import X, Y as Y
315316
```
316317

317318
`c.py`:
@@ -323,6 +324,8 @@ from b import *
323324
#
324325
# error: [unresolved-reference]
325326
reveal_type(X) # revealed: Unknown
327+
328+
reveal_type(Y) # revealed: bool
326329
```
327330

328331
### Symbols in statically known branches
@@ -717,11 +720,8 @@ are present due to `*` imports.
717720
import typing
718721
import collections.abc
719722

720-
# TODO these should not error, should not reveal `Unknown`
721-
# error: [unresolved-attribute]
722-
reveal_type(collections.abc.Sequence) # revealed: Unknown
723-
# error: [unresolved-attribute]
724-
reveal_type(collections.abc.Callable) # revealed: Unknown
723+
reveal_type(collections.abc.Sequence) # revealed: Literal[Sequence]
724+
reveal_type(collections.abc.Callable) # revealed: typing.Callable
725725
```
726726

727727
## Invalid `*` imports

crates/red_knot_python_semantic/src/semantic_index/re_exports.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ impl<'db> Visitor<'db> for ExportFinder<'db> {
7373
if self.file.is_stub(self.db.upcast()) {
7474
// If the source is a stub, names defined by imports are only exported
7575
// if they use the explicit `foo as foo` syntax:
76-
if asname.as_ref() == Some(name) {
76+
if asname.as_ref().is_some_and(|asname| asname.id == name.id) {
7777
self.possibly_add_export(&name.id);
7878
}
7979
} else {

crates/red_knot_python_semantic/src/types.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -887,6 +887,19 @@ impl<'db> Type<'db> {
887887
}
888888
}
889889

890+
// TODO: ditto for avoiding false positives when checking function calls with `Sized` parameters.
891+
(lhs, Type::Instance(InstanceType { class }))
892+
if class.is_known(db, KnownClass::Sized) =>
893+
{
894+
matches!(
895+
lhs.to_meta_type(db).member(db, "__len__"),
896+
SymbolAndQualifiers {
897+
symbol: Symbol::Type(..),
898+
..
899+
}
900+
)
901+
}
902+
890903
// TODO other types containing gradual forms (e.g. generics containing Any/Unknown)
891904
_ => self.is_subtype_of(db, target),
892905
}

crates/red_knot_python_semantic/src/types/class.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,7 @@ pub enum KnownClass {
846846
TypeAliasType,
847847
NoDefaultType,
848848
NewType,
849+
Sized,
849850
// TODO: This can probably be removed when we have support for protocols
850851
SupportsIndex,
851852
// Collections
@@ -923,6 +924,7 @@ impl<'db> KnownClass {
923924
| Self::DefaultDict
924925
| Self::Deque
925926
| Self::Float
927+
| Self::Sized
926928
| Self::Classmethod => Truthiness::Ambiguous,
927929
}
928930
}
@@ -967,6 +969,7 @@ impl<'db> KnownClass {
967969
Self::Counter => "Counter",
968970
Self::DefaultDict => "defaultdict",
969971
Self::Deque => "deque",
972+
Self::Sized => "Sized",
970973
Self::OrderedDict => "OrderedDict",
971974
// For example, `typing.List` is defined as `List = _Alias()` in typeshed
972975
Self::StdlibAlias => "_Alias",
@@ -1127,9 +1130,11 @@ impl<'db> KnownClass {
11271130
| Self::MethodWrapperType
11281131
| Self::WrapperDescriptorType => KnownModule::Types,
11291132
Self::NoneType => KnownModule::Typeshed,
1130-
Self::SpecialForm | Self::TypeVar | Self::StdlibAlias | Self::SupportsIndex => {
1131-
KnownModule::Typing
1132-
}
1133+
Self::SpecialForm
1134+
| Self::TypeVar
1135+
| Self::StdlibAlias
1136+
| Self::SupportsIndex
1137+
| Self::Sized => KnownModule::Typing,
11331138
Self::TypeAliasType | Self::TypeVarTuple | Self::ParamSpec | Self::NewType => {
11341139
KnownModule::TypingExtensions
11351140
}
@@ -1207,6 +1212,7 @@ impl<'db> KnownClass {
12071212
| Self::TypeVar
12081213
| Self::ParamSpec
12091214
| Self::TypeVarTuple
1215+
| Self::Sized
12101216
| Self::NewType => false,
12111217
}
12121218
}
@@ -1259,6 +1265,7 @@ impl<'db> KnownClass {
12591265
| Self::TypeVar
12601266
| Self::ParamSpec
12611267
| Self::TypeVarTuple
1268+
| Self::Sized
12621269
| Self::NewType => false,
12631270
}
12641271
}
@@ -1311,6 +1318,7 @@ impl<'db> KnownClass {
13111318
"_SpecialForm" => Self::SpecialForm,
13121319
"_NoDefaultType" => Self::NoDefaultType,
13131320
"SupportsIndex" => Self::SupportsIndex,
1321+
"Sized" => Self::Sized,
13141322
"_version_info" => Self::VersionInfo,
13151323
"ellipsis" if Program::get(db).python_version(db) <= PythonVersion::PY39 => {
13161324
Self::EllipsisType
@@ -1370,6 +1378,7 @@ impl<'db> KnownClass {
13701378
| Self::SupportsIndex
13711379
| Self::ParamSpec
13721380
| Self::TypeVarTuple
1381+
| Self::Sized
13731382
| Self::NewType => matches!(module, KnownModule::Typing | KnownModule::TypingExtensions),
13741383
}
13751384
}

0 commit comments

Comments
 (0)