[red-knot] fix scope inference with deferred types (#13204)

Test coverage for #13131 wasn't as good as I thought it was, because
although we infer a lot of types in stubs in typeshed, we don't check
typeshed, and therefore we don't do scope-level inference and pull all
types for a scope. So we didn't really have good test coverage for
scope-level inference in a stub. And because of this, I got the code for
supporting that wrong, meaning that if we did scope-level inference with
deferred types, we'd end up never populating the deferred types in the
scope's `TypeInference`, which causes panics like #13160.

Here I both add test coverage by running the corpus tests both as `.py`
and as `.pyi` (which reveals the panic), and I fix the code to support
deferred types in scope inference.

This also revealed a problem with deferred types in generic functions,
which effectively span two scopes. That problem will require a bit more
thought, and I don't want to block this PR on it, so for now I just
don't defer annotations on generic functions.

Fixes #13160.
This commit is contained in:
Carl Meyer 2024-09-03 11:20:43 -07:00 committed by GitHub
parent dfee65882b
commit 29c36a56b2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 27 additions and 18 deletions

View file

@ -468,11 +468,8 @@ impl<'db> TypeInferenceBuilder<'db> {
.as_deref()
.expect("function type params scope without type params");
// TODO: this should also be applied to parameter annotations.
if !self.is_stub() {
self.infer_optional_expression(function.returns.as_deref());
}
// TODO: defer annotation resolution in stubs, with __future__.annotations, or stringified
self.infer_optional_expression(function.returns.as_deref());
self.infer_type_parameters(type_params);
self.infer_parameters(&function.parameters);
}
@ -567,7 +564,9 @@ impl<'db> TypeInferenceBuilder<'db> {
self.infer_parameters(parameters);
// TODO: this should also be applied to parameter annotations.
if !self.is_stub() {
if self.is_stub() {
self.types.has_deferred = true;
} else {
self.infer_optional_annotation_expression(returns.as_deref());
}
}
@ -684,7 +683,9 @@ impl<'db> TypeInferenceBuilder<'db> {
// inference of bases deferred in stubs
// TODO also defer stringified generic type parameters
if !self.is_stub() {
if self.is_stub() {
self.types.has_deferred = true;
} else {
for base in class.bases() {
self.infer_expression(base);
}
@ -693,14 +694,12 @@ impl<'db> TypeInferenceBuilder<'db> {
fn infer_function_deferred(&mut self, function: &ast::StmtFunctionDef) {
if self.is_stub() {
self.types.has_deferred = true;
self.infer_optional_annotation_expression(function.returns.as_deref());
}
}
fn infer_class_deferred(&mut self, class: &ast::StmtClassDef) {
if self.is_stub() {
self.types.has_deferred = true;
for base in class.bases() {
self.infer_expression(base);
}