[ty] Further improve details around which expressions should be deferred in stub files (#21456)
## Summary - Always restore the previous `deferred_state` after parsing a type expression: we don't want that state leaking out into other contexts where we shouldn't be deferring expression inference - Always defer the right-hand-side of a PEP-613 type alias in a stub file, allowing for forward references on the right-hand side of `T: TypeAlias = X | Y` in a stub file Addresses @carljm's review in https://github.com/astral-sh/ruff/pull/21401#discussion_r2524260153 ## Test Plan I added a regression test for a regression that the first version of this PR introduced (we need to make sure the r.h.s. of a PEP-613 `TypeAlias`es is always deferred in a stub file)
This commit is contained in:
@@ -5486,10 +5486,23 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||
self.dataclass_field_specifiers = specifiers;
|
||||
}
|
||||
|
||||
let inferred_ty = self.infer_maybe_standalone_expression(
|
||||
value,
|
||||
TypeContext::new(Some(declared.inner_type())),
|
||||
);
|
||||
// We defer the r.h.s. of PEP-613 `TypeAlias` assignments in stub files.
|
||||
let declared_type = declared.inner_type();
|
||||
let previous_deferred_state = self.deferred_state;
|
||||
|
||||
if matches!(
|
||||
declared_type,
|
||||
Type::SpecialForm(SpecialFormType::TypeAlias)
|
||||
| Type::Dynamic(DynamicType::TodoTypeAlias)
|
||||
) && self.in_stub()
|
||||
{
|
||||
self.deferred_state = DeferredExpressionState::Deferred;
|
||||
}
|
||||
|
||||
let inferred_ty = self
|
||||
.infer_maybe_standalone_expression(value, TypeContext::new(Some(declared_type)));
|
||||
|
||||
self.deferred_state = previous_deferred_state;
|
||||
|
||||
self.dataclass_field_specifiers.clear();
|
||||
|
||||
|
||||
@@ -20,10 +20,12 @@ use crate::types::{
|
||||
impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
/// Infer the type of a type expression.
|
||||
pub(super) fn infer_type_expression(&mut self, expression: &ast::Expr) -> Type<'db> {
|
||||
let previous_deferred_state = self.deferred_state;
|
||||
|
||||
// `DeferredExpressionState::InStringAnnotation` takes precedence over other states.
|
||||
// However, if it's not a stringified annotation, we must still ensure that annotation expressions
|
||||
// are always deferred in stub files.
|
||||
match self.deferred_state {
|
||||
match previous_deferred_state {
|
||||
DeferredExpressionState::None => {
|
||||
if self.in_stub() {
|
||||
self.deferred_state = DeferredExpressionState::Deferred;
|
||||
@@ -31,8 +33,9 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
||||
}
|
||||
DeferredExpressionState::InStringAnnotation(_) | DeferredExpressionState::Deferred => {}
|
||||
}
|
||||
|
||||
let mut ty = self.infer_type_expression_no_store(expression);
|
||||
self.deferred_state = previous_deferred_state;
|
||||
|
||||
let divergent = Type::divergent(Some(self.scope()));
|
||||
if ty.has_divergent_type(self.db(), divergent) {
|
||||
ty = divergent;
|
||||
|
||||
Reference in New Issue
Block a user