Compare commits

...

3 Commits

Author SHA1 Message Date
Carl Meyer
3140beb6a4 WIP: start on passing context to call 2024-09-17 14:13:50 -07:00
Carl Meyer
09812b3c23 WIP: convert IterationOutcome to use InferenceContext 2024-09-17 14:10:52 -07:00
Carl Meyer
1cb570cdd2 WIP: extracted TypeInferenceContext 2024-09-17 14:10:51 -07:00
2 changed files with 383 additions and 350 deletions

View File

@@ -1,4 +1,4 @@
use infer::TypeInferenceBuilder; use infer::TypeInferenceContext;
use ruff_db::files::File; use ruff_db::files::File;
use ruff_python_ast as ast; use ruff_python_ast as ast;
@@ -482,14 +482,14 @@ impl<'db> Type<'db> {
/// ///
/// Returns `None` if `self` is not a callable type. /// Returns `None` if `self` is not a callable type.
#[must_use] #[must_use]
pub fn call(&self, db: &'db dyn Db) -> Option<Type<'db>> { fn call(&self, db: &'db dyn Db, _context: &mut TypeInferenceContext<'db>) -> Option<Type<'db>> {
match self { match self {
Type::Function(function_type) => Some(function_type.return_type(db)), Type::Function(function_type) => Some(function_type.return_type(db)),
// TODO annotated return type on `__new__` or metaclass `__call__` // TODO annotated return type on `__new__` or metaclass `__call__`
Type::Class(class) => Some(Type::Instance(*class)), Type::Class(class) => Some(Type::Instance(*class)),
// TODO: handle classes which implement the Callable protocol // TODO: handle classes which implement `__call__`
Type::Instance(_instance_ty) => Some(Type::Unknown), Type::Instance(_instance_ty) => Some(Type::Unknown),
// `Any` is callable, and its return type is also `Any`. // `Any` is callable, and its return type is also `Any`.
@@ -497,7 +497,7 @@ impl<'db> Type<'db> {
Type::Unknown => Some(Type::Unknown), Type::Unknown => Some(Type::Unknown),
// TODO: union and intersection types, if they reduce to `Callable` // TODO: union and intersection types
Type::Union(_) => Some(Type::Unknown), Type::Union(_) => Some(Type::Unknown),
Type::Intersection(_) => Some(Type::Unknown), Type::Intersection(_) => Some(Type::Unknown),
@@ -513,11 +513,14 @@ impl<'db> Type<'db> {
/// for y in x: /// for y in x:
/// pass /// pass
/// ``` /// ```
fn iterate(&self, db: &'db dyn Db) -> IterationOutcome<'db> { /// Return None and emit a diagnostic if this type is not iterable.
fn iterate(
&self,
db: &'db dyn Db,
context: &mut TypeInferenceContext<'db>,
) -> Option<Type<'db>> {
if let Type::Tuple(tuple_type) = self { if let Type::Tuple(tuple_type) = self {
return IterationOutcome::Iterable { return Some(UnionType::from_elements(db, &**tuple_type.elements(db)));
element_ty: UnionType::from_elements(db, &**tuple_type.elements(db)),
};
} }
// `self` represents the type of the iterable; // `self` represents the type of the iterable;
@@ -526,18 +529,15 @@ impl<'db> Type<'db> {
let dunder_iter_method = iterable_meta_type.member(db, "__iter__"); let dunder_iter_method = iterable_meta_type.member(db, "__iter__");
if !dunder_iter_method.is_unbound() { if !dunder_iter_method.is_unbound() {
let Some(iterator_ty) = dunder_iter_method.call(db) else { let Some(iterator_ty) = dunder_iter_method.call(db, context) else {
return IterationOutcome::NotIterable { context.not_iterable_diagnostic(*self);
not_iterable_ty: *self, return None;
};
}; };
let dunder_next_method = iterator_ty.to_meta_type(db).member(db, "__next__"); let dunder_next_method = iterator_ty.to_meta_type(db).member(db, "__next__");
return dunder_next_method return dunder_next_method.call(db, context).or_else(|| {
.call(db) context.not_iterable_diagnostic(*self);
.map(|element_ty| IterationOutcome::Iterable { element_ty }) None
.unwrap_or(IterationOutcome::NotIterable {
not_iterable_ty: *self,
}); });
} }
@@ -549,11 +549,9 @@ impl<'db> Type<'db> {
// accepting `int` or `SupportsIndex` // accepting `int` or `SupportsIndex`
let dunder_get_item_method = iterable_meta_type.member(db, "__getitem__"); let dunder_get_item_method = iterable_meta_type.member(db, "__getitem__");
dunder_get_item_method dunder_get_item_method.call(db, context).or_else(|| {
.call(db) context.not_iterable_diagnostic(*self);
.map(|element_ty| IterationOutcome::Iterable { element_ty }) None
.unwrap_or(IterationOutcome::NotIterable {
not_iterable_ty: *self,
}) })
} }
@@ -619,28 +617,6 @@ impl<'db> From<&Type<'db>> for Type<'db> {
} }
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum IterationOutcome<'db> {
Iterable { element_ty: Type<'db> },
NotIterable { not_iterable_ty: Type<'db> },
}
impl<'db> IterationOutcome<'db> {
fn unwrap_with_diagnostic(
self,
iterable_node: ast::AnyNodeRef,
inference_builder: &mut TypeInferenceBuilder<'db>,
) -> Type<'db> {
match self {
Self::Iterable { element_ty } => element_ty,
Self::NotIterable { not_iterable_ty } => {
inference_builder.not_iterable_diagnostic(iterable_node, not_iterable_ty);
Type::Unknown
}
}
}
}
#[salsa::interned] #[salsa::interned]
pub struct FunctionType<'db> { pub struct FunctionType<'db> {
/// name of the function at definition /// name of the function at definition

File diff suppressed because it is too large Load Diff