mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-29 21:34:57 +00:00
[ty] Combine CallArguments and CallArgumentTypes (#19337)
We previously had separate `CallArguments` and `CallArgumentTypes` types in support of our two-phase call binding logic. `CallArguments` would store only the arity/kind of each argument (positional, keyword, variadic, etc). We then performed parameter matching using only this arity/kind information, and then infered the type of each argument, placing the result of this second phase into a new `CallArgumentTypes`. In #18996, we will need to infer the types of splatted arguments _before_ performing parameter matching, since we need to know the argument type to accurately infer its length, which informs how many parameters the splatted argument is matched against. That makes this separation of Rust types no longer useful. This PR merges everything back into a single `CallArguments`. In the case where we are performing two-phase call binding, the types will be initialized to `None`, and updated to the actual argument type during the second `check_types` phase. _[This is a refactoring in support of fixing the merge conflicts on #18996. I've pulled this out into a separate PR to make it easier to review in isolation.]_
This commit is contained in:
parent
e9cac3684a
commit
f4d0273532
7 changed files with 186 additions and 236 deletions
|
@ -35,7 +35,7 @@ use crate::semantic_index::definition::Definition;
|
||||||
use crate::semantic_index::place::{ScopeId, ScopedPlaceId};
|
use crate::semantic_index::place::{ScopeId, ScopedPlaceId};
|
||||||
use crate::semantic_index::{imported_modules, place_table, semantic_index};
|
use crate::semantic_index::{imported_modules, place_table, semantic_index};
|
||||||
use crate::suppression::check_suppressions;
|
use crate::suppression::check_suppressions;
|
||||||
use crate::types::call::{Binding, Bindings, CallArgumentTypes, CallableBinding};
|
use crate::types::call::{Binding, Bindings, CallArguments, CallableBinding};
|
||||||
pub(crate) use crate::types::class_base::ClassBase;
|
pub(crate) use crate::types::class_base::ClassBase;
|
||||||
use crate::types::context::{LintDiagnosticGuard, LintDiagnosticGuardBuilder};
|
use crate::types::context::{LintDiagnosticGuard, LintDiagnosticGuardBuilder};
|
||||||
use crate::types::diagnostic::{INVALID_TYPE_FORM, UNSUPPORTED_BOOL_CONVERSION};
|
use crate::types::diagnostic::{INVALID_TYPE_FORM, UNSUPPORTED_BOOL_CONVERSION};
|
||||||
|
@ -2683,7 +2683,7 @@ impl<'db> Type<'db> {
|
||||||
|
|
||||||
if let Place::Type(descr_get, descr_get_boundness) = descr_get {
|
if let Place::Type(descr_get, descr_get_boundness) = descr_get {
|
||||||
let return_ty = descr_get
|
let return_ty = descr_get
|
||||||
.try_call(db, &CallArgumentTypes::positional([self, instance, owner]))
|
.try_call(db, &CallArguments::positional([self, instance, owner]))
|
||||||
.map(|bindings| {
|
.map(|bindings| {
|
||||||
if descr_get_boundness == Boundness::Bound {
|
if descr_get_boundness == Boundness::Bound {
|
||||||
bindings.return_type(db)
|
bindings.return_type(db)
|
||||||
|
@ -3134,7 +3134,7 @@ impl<'db> Type<'db> {
|
||||||
self.try_call_dunder(
|
self.try_call_dunder(
|
||||||
db,
|
db,
|
||||||
"__getattr__",
|
"__getattr__",
|
||||||
CallArgumentTypes::positional([Type::string_literal(db, &name)]),
|
CallArguments::positional([Type::string_literal(db, &name)]),
|
||||||
)
|
)
|
||||||
.map(|outcome| Place::bound(outcome.return_type(db)))
|
.map(|outcome| Place::bound(outcome.return_type(db)))
|
||||||
// TODO: Handle call errors here.
|
// TODO: Handle call errors here.
|
||||||
|
@ -3153,7 +3153,7 @@ impl<'db> Type<'db> {
|
||||||
self.try_call_dunder_with_policy(
|
self.try_call_dunder_with_policy(
|
||||||
db,
|
db,
|
||||||
"__getattribute__",
|
"__getattribute__",
|
||||||
&mut CallArgumentTypes::positional([Type::string_literal(db, &name)]),
|
&mut CallArguments::positional([Type::string_literal(db, &name)]),
|
||||||
MemberLookupPolicy::MRO_NO_OBJECT_FALLBACK,
|
MemberLookupPolicy::MRO_NO_OBJECT_FALLBACK,
|
||||||
)
|
)
|
||||||
.map(|outcome| Place::bound(outcome.return_type(db)))
|
.map(|outcome| Place::bound(outcome.return_type(db)))
|
||||||
|
@ -3275,7 +3275,7 @@ impl<'db> Type<'db> {
|
||||||
// runtime there is a fallback to `__len__`, since `__bool__` takes precedence
|
// runtime there is a fallback to `__len__`, since `__bool__` takes precedence
|
||||||
// and a subclass could add a `__bool__` method.
|
// and a subclass could add a `__bool__` method.
|
||||||
|
|
||||||
match self.try_call_dunder(db, "__bool__", CallArgumentTypes::none()) {
|
match self.try_call_dunder(db, "__bool__", CallArguments::none()) {
|
||||||
Ok(outcome) => {
|
Ok(outcome) => {
|
||||||
let return_type = outcome.return_type(db);
|
let return_type = outcome.return_type(db);
|
||||||
if !return_type.is_assignable_to(db, KnownClass::Bool.to_instance(db)) {
|
if !return_type.is_assignable_to(db, KnownClass::Bool.to_instance(db)) {
|
||||||
|
@ -3474,7 +3474,7 @@ impl<'db> Type<'db> {
|
||||||
return usize_len.try_into().ok().map(Type::IntLiteral);
|
return usize_len.try_into().ok().map(Type::IntLiteral);
|
||||||
}
|
}
|
||||||
|
|
||||||
let return_ty = match self.try_call_dunder(db, "__len__", CallArgumentTypes::none()) {
|
let return_ty = match self.try_call_dunder(db, "__len__", CallArguments::none()) {
|
||||||
Ok(bindings) => bindings.return_type(db),
|
Ok(bindings) => bindings.return_type(db),
|
||||||
Err(CallDunderError::PossiblyUnbound(bindings)) => bindings.return_type(db),
|
Err(CallDunderError::PossiblyUnbound(bindings)) => bindings.return_type(db),
|
||||||
|
|
||||||
|
@ -4394,7 +4394,7 @@ impl<'db> Type<'db> {
|
||||||
fn try_call(
|
fn try_call(
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
argument_types: &CallArgumentTypes<'_, 'db>,
|
argument_types: &CallArguments<'_, 'db>,
|
||||||
) -> Result<Bindings<'db>, CallError<'db>> {
|
) -> Result<Bindings<'db>, CallError<'db>> {
|
||||||
self.bindings(db)
|
self.bindings(db)
|
||||||
.match_parameters(argument_types)
|
.match_parameters(argument_types)
|
||||||
|
@ -4409,7 +4409,7 @@ impl<'db> Type<'db> {
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
name: &str,
|
name: &str,
|
||||||
mut argument_types: CallArgumentTypes<'_, 'db>,
|
mut argument_types: CallArguments<'_, 'db>,
|
||||||
) -> Result<Bindings<'db>, CallDunderError<'db>> {
|
) -> Result<Bindings<'db>, CallDunderError<'db>> {
|
||||||
self.try_call_dunder_with_policy(
|
self.try_call_dunder_with_policy(
|
||||||
db,
|
db,
|
||||||
|
@ -4430,7 +4430,7 @@ impl<'db> Type<'db> {
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
name: &str,
|
name: &str,
|
||||||
argument_types: &mut CallArgumentTypes<'_, 'db>,
|
argument_types: &mut CallArguments<'_, 'db>,
|
||||||
policy: MemberLookupPolicy,
|
policy: MemberLookupPolicy,
|
||||||
) -> Result<Bindings<'db>, CallDunderError<'db>> {
|
) -> Result<Bindings<'db>, CallDunderError<'db>> {
|
||||||
// Implicit calls to dunder methods never access instance members, so we pass
|
// Implicit calls to dunder methods never access instance members, so we pass
|
||||||
|
@ -4492,19 +4492,19 @@ impl<'db> Type<'db> {
|
||||||
self.try_call_dunder(
|
self.try_call_dunder(
|
||||||
db,
|
db,
|
||||||
"__getitem__",
|
"__getitem__",
|
||||||
CallArgumentTypes::positional([KnownClass::Int.to_instance(db)]),
|
CallArguments::positional([KnownClass::Int.to_instance(db)]),
|
||||||
)
|
)
|
||||||
.map(|dunder_getitem_outcome| dunder_getitem_outcome.return_type(db))
|
.map(|dunder_getitem_outcome| dunder_getitem_outcome.return_type(db))
|
||||||
};
|
};
|
||||||
|
|
||||||
let try_call_dunder_next_on_iterator = |iterator: Type<'db>| {
|
let try_call_dunder_next_on_iterator = |iterator: Type<'db>| {
|
||||||
iterator
|
iterator
|
||||||
.try_call_dunder(db, "__next__", CallArgumentTypes::none())
|
.try_call_dunder(db, "__next__", CallArguments::none())
|
||||||
.map(|dunder_next_outcome| dunder_next_outcome.return_type(db))
|
.map(|dunder_next_outcome| dunder_next_outcome.return_type(db))
|
||||||
};
|
};
|
||||||
|
|
||||||
let dunder_iter_result = self
|
let dunder_iter_result = self
|
||||||
.try_call_dunder(db, "__iter__", CallArgumentTypes::none())
|
.try_call_dunder(db, "__iter__", CallArguments::none())
|
||||||
.map(|dunder_iter_outcome| dunder_iter_outcome.return_type(db));
|
.map(|dunder_iter_outcome| dunder_iter_outcome.return_type(db));
|
||||||
|
|
||||||
match dunder_iter_result {
|
match dunder_iter_result {
|
||||||
|
@ -4588,11 +4588,11 @@ impl<'db> Type<'db> {
|
||||||
/// pass
|
/// pass
|
||||||
/// ```
|
/// ```
|
||||||
fn try_enter(self, db: &'db dyn Db) -> Result<Type<'db>, ContextManagerError<'db>> {
|
fn try_enter(self, db: &'db dyn Db) -> Result<Type<'db>, ContextManagerError<'db>> {
|
||||||
let enter = self.try_call_dunder(db, "__enter__", CallArgumentTypes::none());
|
let enter = self.try_call_dunder(db, "__enter__", CallArguments::none());
|
||||||
let exit = self.try_call_dunder(
|
let exit = self.try_call_dunder(
|
||||||
db,
|
db,
|
||||||
"__exit__",
|
"__exit__",
|
||||||
CallArgumentTypes::positional([Type::none(db), Type::none(db), Type::none(db)]),
|
CallArguments::positional([Type::none(db), Type::none(db), Type::none(db)]),
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO: Make use of Protocols when we support it (the manager be assignable to `contextlib.AbstractContextManager`).
|
// TODO: Make use of Protocols when we support it (the manager be assignable to `contextlib.AbstractContextManager`).
|
||||||
|
@ -4627,7 +4627,7 @@ impl<'db> Type<'db> {
|
||||||
fn try_call_constructor(
|
fn try_call_constructor(
|
||||||
self,
|
self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
argument_types: CallArgumentTypes<'_, 'db>,
|
argument_types: CallArguments<'_, 'db>,
|
||||||
) -> Result<Type<'db>, ConstructorCallError<'db>> {
|
) -> Result<Type<'db>, ConstructorCallError<'db>> {
|
||||||
debug_assert!(matches!(
|
debug_assert!(matches!(
|
||||||
self,
|
self,
|
||||||
|
@ -6428,11 +6428,11 @@ impl<'db> ContextManagerError<'db> {
|
||||||
Ok(_) | Err(CallDunderError::CallError(..)),
|
Ok(_) | Err(CallDunderError::CallError(..)),
|
||||||
Ok(_) | Err(CallDunderError::CallError(..)),
|
Ok(_) | Err(CallDunderError::CallError(..)),
|
||||||
) = (
|
) = (
|
||||||
context_expression_type.try_call_dunder(db, "__aenter__", CallArgumentTypes::none()),
|
context_expression_type.try_call_dunder(db, "__aenter__", CallArguments::none()),
|
||||||
context_expression_type.try_call_dunder(
|
context_expression_type.try_call_dunder(
|
||||||
db,
|
db,
|
||||||
"__aexit__",
|
"__aexit__",
|
||||||
CallArgumentTypes::positional([Type::unknown(), Type::unknown(), Type::unknown()]),
|
CallArguments::positional([Type::unknown(), Type::unknown(), Type::unknown()]),
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
diag.info(format_args!(
|
diag.info(format_args!(
|
||||||
|
@ -6495,7 +6495,7 @@ impl<'db> IterationError<'db> {
|
||||||
|
|
||||||
Self::IterCallError(_, dunder_iter_bindings) => dunder_iter_bindings
|
Self::IterCallError(_, dunder_iter_bindings) => dunder_iter_bindings
|
||||||
.return_type(db)
|
.return_type(db)
|
||||||
.try_call_dunder(db, "__next__", CallArgumentTypes::none())
|
.try_call_dunder(db, "__next__", CallArguments::none())
|
||||||
.map(|dunder_next_outcome| Some(dunder_next_outcome.return_type(db)))
|
.map(|dunder_next_outcome| Some(dunder_next_outcome.return_type(db)))
|
||||||
.unwrap_or_else(|dunder_next_call_error| dunder_next_call_error.return_type(db)),
|
.unwrap_or_else(|dunder_next_call_error| dunder_next_call_error.return_type(db)),
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::Db;
|
||||||
|
|
||||||
mod arguments;
|
mod arguments;
|
||||||
pub(crate) mod bind;
|
pub(crate) mod bind;
|
||||||
pub(super) use arguments::{Argument, CallArgumentTypes, CallArguments};
|
pub(super) use arguments::{Argument, CallArguments};
|
||||||
pub(super) use bind::{Binding, Bindings, CallableBinding};
|
pub(super) use bind::{Binding, Bindings, CallableBinding};
|
||||||
|
|
||||||
/// Wraps a [`Bindings`] for an unsuccessful call with information about why the call was
|
/// Wraps a [`Bindings`] for an unsuccessful call with information about why the call was
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::ops::{Deref, DerefMut};
|
|
||||||
|
|
||||||
use itertools::{Either, Itertools};
|
use itertools::{Either, Itertools};
|
||||||
use ruff_python_ast as ast;
|
use ruff_python_ast as ast;
|
||||||
|
@ -10,60 +9,6 @@ use crate::types::tuple::{TupleSpec, TupleType};
|
||||||
|
|
||||||
use super::Type;
|
use super::Type;
|
||||||
|
|
||||||
/// Arguments for a single call, in source order.
|
|
||||||
#[derive(Clone, Debug, Default)]
|
|
||||||
pub(crate) struct CallArguments<'a>(Vec<Argument<'a>>);
|
|
||||||
|
|
||||||
impl<'a> CallArguments<'a> {
|
|
||||||
/// Create `CallArguments` from AST arguments
|
|
||||||
pub(crate) fn from_arguments(arguments: &'a ast::Arguments) -> Self {
|
|
||||||
arguments
|
|
||||||
.arguments_source_order()
|
|
||||||
.map(|arg_or_keyword| match arg_or_keyword {
|
|
||||||
ast::ArgOrKeyword::Arg(arg) => match arg {
|
|
||||||
ast::Expr::Starred(ast::ExprStarred { .. }) => Argument::Variadic,
|
|
||||||
_ => Argument::Positional,
|
|
||||||
},
|
|
||||||
ast::ArgOrKeyword::Keyword(ast::Keyword { arg, .. }) => {
|
|
||||||
if let Some(arg) = arg {
|
|
||||||
Argument::Keyword(&arg.id)
|
|
||||||
} else {
|
|
||||||
Argument::Keywords
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Prepend an optional extra synthetic argument (for a `self` or `cls` parameter) to the front
|
|
||||||
/// of this argument list. (If `bound_self` is none, we return the argument list
|
|
||||||
/// unmodified.)
|
|
||||||
pub(crate) fn with_self(&self, bound_self: Option<Type<'_>>) -> Cow<Self> {
|
|
||||||
if bound_self.is_some() {
|
|
||||||
let arguments = std::iter::once(Argument::Synthetic)
|
|
||||||
.chain(self.0.iter().copied())
|
|
||||||
.collect();
|
|
||||||
Cow::Owned(CallArguments(arguments))
|
|
||||||
} else {
|
|
||||||
Cow::Borrowed(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn len(&self) -> usize {
|
|
||||||
self.0.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn iter(&self) -> impl Iterator<Item = Argument<'a>> + '_ {
|
|
||||||
self.0.iter().copied()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> FromIterator<Argument<'a>> for CallArguments<'a> {
|
|
||||||
fn from_iter<T: IntoIterator<Item = Argument<'a>>>(iter: T) -> Self {
|
|
||||||
Self(iter.into_iter().collect())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub(crate) enum Argument<'a> {
|
pub(crate) enum Argument<'a> {
|
||||||
/// The synthetic `self` or `cls` argument, which doesn't appear explicitly at the call site.
|
/// The synthetic `self` or `cls` argument, which doesn't appear explicitly at the call site.
|
||||||
|
@ -80,64 +25,87 @@ pub(crate) enum Argument<'a> {
|
||||||
|
|
||||||
/// Arguments for a single call, in source order, along with inferred types for each argument.
|
/// Arguments for a single call, in source order, along with inferred types for each argument.
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub(crate) struct CallArgumentTypes<'a, 'db> {
|
pub(crate) struct CallArguments<'a, 'db> {
|
||||||
arguments: CallArguments<'a>,
|
arguments: Vec<Argument<'a>>,
|
||||||
types: Vec<Type<'db>>,
|
types: Vec<Option<Type<'db>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'db> CallArgumentTypes<'a, 'db> {
|
impl<'a, 'db> CallArguments<'a, 'db> {
|
||||||
/// Create a [`CallArgumentTypes`] with no arguments.
|
fn new(arguments: Vec<Argument<'a>>, types: Vec<Option<Type<'db>>>) -> Self {
|
||||||
|
debug_assert!(arguments.len() == types.len());
|
||||||
|
Self { arguments, types }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create `CallArguments` from AST arguments
|
||||||
|
pub(crate) fn from_arguments(arguments: &'a ast::Arguments) -> Self {
|
||||||
|
arguments
|
||||||
|
.arguments_source_order()
|
||||||
|
.map(|arg_or_keyword| match arg_or_keyword {
|
||||||
|
ast::ArgOrKeyword::Arg(arg) => match arg {
|
||||||
|
ast::Expr::Starred(ast::ExprStarred { .. }) => Argument::Variadic,
|
||||||
|
_ => Argument::Positional,
|
||||||
|
},
|
||||||
|
ast::ArgOrKeyword::Keyword(ast::Keyword { arg, .. }) => {
|
||||||
|
if let Some(arg) = arg {
|
||||||
|
Argument::Keyword(&arg.id)
|
||||||
|
} else {
|
||||||
|
Argument::Keywords
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(|argument| (argument, None))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a [`CallArguments`] with no arguments.
|
||||||
pub(crate) fn none() -> Self {
|
pub(crate) fn none() -> Self {
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a [`CallArgumentTypes`] from an iterator over non-variadic positional argument
|
/// Create a [`CallArguments`] from an iterator over non-variadic positional argument types.
|
||||||
/// types.
|
|
||||||
pub(crate) fn positional(positional_tys: impl IntoIterator<Item = Type<'db>>) -> Self {
|
pub(crate) fn positional(positional_tys: impl IntoIterator<Item = Type<'db>>) -> Self {
|
||||||
let types: Vec<_> = positional_tys.into_iter().collect();
|
let types: Vec<_> = positional_tys.into_iter().map(Some).collect();
|
||||||
let arguments = CallArguments(vec![Argument::Positional; types.len()]);
|
let arguments = vec![Argument::Positional; types.len()];
|
||||||
Self { arguments, types }
|
Self { arguments, types }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new [`CallArgumentTypes`] to store the inferred types of the arguments in a
|
pub(crate) fn len(&self) -> usize {
|
||||||
/// [`CallArguments`]. Uses the provided callback to infer each argument type.
|
self.arguments.len()
|
||||||
pub(crate) fn new<F>(arguments: CallArguments<'a>, mut f: F) -> Self
|
|
||||||
where
|
|
||||||
F: FnMut(usize, Argument<'a>) -> Type<'db>,
|
|
||||||
{
|
|
||||||
let types = arguments
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(idx, argument)| f(idx, argument))
|
|
||||||
.collect();
|
|
||||||
Self { arguments, types }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn types(&self) -> &[Type<'db>] {
|
pub(crate) fn types(&self) -> &[Option<Type<'db>>] {
|
||||||
&self.types
|
&self.types
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn iter_types(&self) -> impl Iterator<Item = Type<'db>> {
|
||||||
|
self.types.iter().map(|ty| ty.unwrap_or_else(Type::unknown))
|
||||||
|
}
|
||||||
|
|
||||||
/// Prepend an optional extra synthetic argument (for a `self` or `cls` parameter) to the front
|
/// Prepend an optional extra synthetic argument (for a `self` or `cls` parameter) to the front
|
||||||
/// of this argument list. (If `bound_self` is none, we return the argument list
|
/// of this argument list. (If `bound_self` is none, we return the argument list
|
||||||
/// unmodified.)
|
/// unmodified.)
|
||||||
pub(crate) fn with_self(&self, bound_self: Option<Type<'db>>) -> Cow<Self> {
|
pub(crate) fn with_self(&self, bound_self: Option<Type<'db>>) -> Cow<Self> {
|
||||||
if let Some(bound_self) = bound_self {
|
if bound_self.is_some() {
|
||||||
let arguments = CallArguments(
|
let arguments = std::iter::once(Argument::Synthetic)
|
||||||
std::iter::once(Argument::Synthetic)
|
.chain(self.arguments.iter().copied())
|
||||||
.chain(self.arguments.0.iter().copied())
|
.collect();
|
||||||
.collect(),
|
|
||||||
);
|
|
||||||
let types = std::iter::once(bound_self)
|
let types = std::iter::once(bound_self)
|
||||||
.chain(self.types.iter().copied())
|
.chain(self.types.iter().copied())
|
||||||
.collect();
|
.collect();
|
||||||
Cow::Owned(CallArgumentTypes { arguments, types })
|
Cow::Owned(CallArguments { arguments, types })
|
||||||
} else {
|
} else {
|
||||||
Cow::Borrowed(self)
|
Cow::Borrowed(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn iter(&self) -> impl Iterator<Item = (Argument<'a>, Type<'db>)> + '_ {
|
pub(crate) fn iter(&self) -> impl Iterator<Item = (Argument<'a>, Option<Type<'db>>)> + '_ {
|
||||||
self.arguments.iter().zip(self.types.iter().copied())
|
(self.arguments.iter().copied()).zip(self.types.iter().copied())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn iter_mut(
|
||||||
|
&mut self,
|
||||||
|
) -> impl Iterator<Item = (Argument<'a>, &mut Option<Type<'db>>)> + '_ {
|
||||||
|
(self.arguments.iter().copied()).zip(self.types.iter_mut())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator on performing [argument type expansion].
|
/// Returns an iterator on performing [argument type expansion].
|
||||||
|
@ -146,17 +114,20 @@ impl<'a, 'db> CallArgumentTypes<'a, 'db> {
|
||||||
/// contains the same arguments, but with one or more of the argument types expanded.
|
/// contains the same arguments, but with one or more of the argument types expanded.
|
||||||
///
|
///
|
||||||
/// [argument type expansion]: https://typing.python.org/en/latest/spec/overload.html#argument-type-expansion
|
/// [argument type expansion]: https://typing.python.org/en/latest/spec/overload.html#argument-type-expansion
|
||||||
pub(crate) fn expand(&self, db: &'db dyn Db) -> impl Iterator<Item = Vec<Vec<Type<'db>>>> + '_ {
|
pub(crate) fn expand(
|
||||||
|
&self,
|
||||||
|
db: &'db dyn Db,
|
||||||
|
) -> impl Iterator<Item = Vec<CallArguments<'a, 'db>>> + '_ {
|
||||||
/// Represents the state of the expansion process.
|
/// Represents the state of the expansion process.
|
||||||
///
|
///
|
||||||
/// This is useful to avoid cloning the initial types vector if none of the types can be
|
/// This is useful to avoid cloning the initial types vector if none of the types can be
|
||||||
/// expanded.
|
/// expanded.
|
||||||
enum State<'a, 'db> {
|
enum State<'a, 'b, 'db> {
|
||||||
Initial(&'a Vec<Type<'db>>),
|
Initial(&'b Vec<Option<Type<'db>>>),
|
||||||
Expanded(Vec<Vec<Type<'db>>>),
|
Expanded(Vec<CallArguments<'a, 'db>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'db> State<'_, 'db> {
|
impl<'db> State<'_, '_, 'db> {
|
||||||
fn len(&self) -> usize {
|
fn len(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
State::Initial(_) => 1,
|
State::Initial(_) => 1,
|
||||||
|
@ -164,10 +135,12 @@ impl<'a, 'db> CallArgumentTypes<'a, 'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn iter(&self) -> impl Iterator<Item = &Vec<Type<'db>>> + '_ {
|
fn iter(&self) -> impl Iterator<Item = &[Option<Type<'db>>]> + '_ {
|
||||||
match self {
|
match self {
|
||||||
State::Initial(types) => std::slice::from_ref(*types).iter(),
|
State::Initial(types) => Either::Left(std::iter::once(types.as_slice())),
|
||||||
State::Expanded(expanded) => expanded.iter(),
|
State::Expanded(expanded) => {
|
||||||
|
Either::Right(expanded.iter().map(CallArguments::types))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -178,26 +151,31 @@ impl<'a, 'db> CallArgumentTypes<'a, 'db> {
|
||||||
// Find the next type that can be expanded.
|
// Find the next type that can be expanded.
|
||||||
let expanded_types = loop {
|
let expanded_types = loop {
|
||||||
let arg_type = self.types.get(index)?;
|
let arg_type = self.types.get(index)?;
|
||||||
if let Some(expanded_types) = expand_type(db, *arg_type) {
|
if let Some(arg_type) = arg_type {
|
||||||
break expanded_types;
|
if let Some(expanded_types) = expand_type(db, *arg_type) {
|
||||||
|
break expanded_types;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
index += 1;
|
index += 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut expanded_arg_types = Vec::with_capacity(expanded_types.len() * previous.len());
|
let mut expanded_arguments = Vec::with_capacity(expanded_types.len() * previous.len());
|
||||||
|
|
||||||
for pre_expanded_types in previous.iter() {
|
for pre_expanded_types in previous.iter() {
|
||||||
for subtype in &expanded_types {
|
for subtype in &expanded_types {
|
||||||
let mut new_expanded_types = pre_expanded_types.clone();
|
let mut new_expanded_types = pre_expanded_types.to_vec();
|
||||||
new_expanded_types[index] = *subtype;
|
new_expanded_types[index] = Some(*subtype);
|
||||||
expanded_arg_types.push(new_expanded_types);
|
expanded_arguments.push(CallArguments::new(
|
||||||
|
self.arguments.clone(),
|
||||||
|
new_expanded_types,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Increment the index to move to the next argument type for the next iteration.
|
// Increment the index to move to the next argument type for the next iteration.
|
||||||
index += 1;
|
index += 1;
|
||||||
|
|
||||||
Some(State::Expanded(expanded_arg_types))
|
Some(State::Expanded(expanded_arguments))
|
||||||
})
|
})
|
||||||
.skip(1) // Skip the initial state, which has no expanded types.
|
.skip(1) // Skip the initial state, which has no expanded types.
|
||||||
.map(|state| match state {
|
.map(|state| match state {
|
||||||
|
@ -207,16 +185,13 @@ impl<'a, 'db> CallArgumentTypes<'a, 'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Deref for CallArgumentTypes<'a, '_> {
|
impl<'a, 'db> FromIterator<(Argument<'a>, Option<Type<'db>>)> for CallArguments<'a, 'db> {
|
||||||
type Target = CallArguments<'a>;
|
fn from_iter<T>(iter: T) -> Self
|
||||||
fn deref(&self) -> &CallArguments<'a> {
|
where
|
||||||
&self.arguments
|
T: IntoIterator<Item = (Argument<'a>, Option<Type<'db>>)>,
|
||||||
}
|
{
|
||||||
}
|
let (arguments, types) = iter.into_iter().unzip();
|
||||||
|
Self { arguments, types }
|
||||||
impl<'a> DerefMut for CallArgumentTypes<'a, '_> {
|
|
||||||
fn deref_mut(&mut self) -> &mut CallArguments<'a> {
|
|
||||||
&mut self.arguments
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,10 +10,7 @@ use itertools::Itertools;
|
||||||
use ruff_db::parsed::parsed_module;
|
use ruff_db::parsed::parsed_module;
|
||||||
use smallvec::{SmallVec, smallvec};
|
use smallvec::{SmallVec, smallvec};
|
||||||
|
|
||||||
use super::{
|
use super::{Argument, CallArguments, CallError, CallErrorKind, InferContext, Signature, Type};
|
||||||
Argument, CallArgumentTypes, CallArguments, CallError, CallErrorKind, InferContext, Signature,
|
|
||||||
Type,
|
|
||||||
};
|
|
||||||
use crate::db::Db;
|
use crate::db::Db;
|
||||||
use crate::dunder_all::dunder_all_names;
|
use crate::dunder_all::dunder_all_names;
|
||||||
use crate::place::{Boundness, Place};
|
use crate::place::{Boundness, Place};
|
||||||
|
@ -95,12 +92,11 @@ impl<'db> Bindings<'db> {
|
||||||
///
|
///
|
||||||
/// The returned bindings tell you which parameter (in each signature) each argument was
|
/// The returned bindings tell you which parameter (in each signature) each argument was
|
||||||
/// matched against. You can then perform type inference on each argument with extra context
|
/// matched against. You can then perform type inference on each argument with extra context
|
||||||
/// about the expected parameter types. (You do this by creating a [`CallArgumentTypes`] object
|
/// about the expected parameter types.
|
||||||
/// from the `arguments` that you match against.)
|
|
||||||
///
|
///
|
||||||
/// Once you have argument types available, you can call [`check_types`][Self::check_types] to
|
/// Once you have argument types available, you can call [`check_types`][Self::check_types] to
|
||||||
/// verify that each argument type is assignable to the corresponding parameter type.
|
/// verify that each argument type is assignable to the corresponding parameter type.
|
||||||
pub(crate) fn match_parameters(mut self, arguments: &CallArguments<'_>) -> Self {
|
pub(crate) fn match_parameters(mut self, arguments: &CallArguments<'_, 'db>) -> Self {
|
||||||
let mut argument_forms = vec![None; arguments.len()];
|
let mut argument_forms = vec![None; arguments.len()];
|
||||||
let mut conflicting_forms = vec![false; arguments.len()];
|
let mut conflicting_forms = vec![false; arguments.len()];
|
||||||
for binding in &mut self.elements {
|
for binding in &mut self.elements {
|
||||||
|
@ -123,7 +119,7 @@ impl<'db> Bindings<'db> {
|
||||||
pub(crate) fn check_types(
|
pub(crate) fn check_types(
|
||||||
mut self,
|
mut self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
argument_types: &CallArgumentTypes<'_, 'db>,
|
argument_types: &CallArguments<'_, 'db>,
|
||||||
) -> Result<Self, CallError<'db>> {
|
) -> Result<Self, CallError<'db>> {
|
||||||
for element in &mut self.elements {
|
for element in &mut self.elements {
|
||||||
element.check_types(db, argument_types);
|
element.check_types(db, argument_types);
|
||||||
|
@ -410,7 +406,7 @@ impl<'db> Bindings<'db> {
|
||||||
[Some(Type::PropertyInstance(property)), Some(instance), ..] => {
|
[Some(Type::PropertyInstance(property)), Some(instance), ..] => {
|
||||||
if let Some(getter) = property.getter(db) {
|
if let Some(getter) = property.getter(db) {
|
||||||
if let Ok(return_ty) = getter
|
if let Ok(return_ty) = getter
|
||||||
.try_call(db, &CallArgumentTypes::positional([*instance]))
|
.try_call(db, &CallArguments::positional([*instance]))
|
||||||
.map(|binding| binding.return_type(db))
|
.map(|binding| binding.return_type(db))
|
||||||
{
|
{
|
||||||
overload.set_return_type(return_ty);
|
overload.set_return_type(return_ty);
|
||||||
|
@ -439,7 +435,7 @@ impl<'db> Bindings<'db> {
|
||||||
[Some(instance), ..] => {
|
[Some(instance), ..] => {
|
||||||
if let Some(getter) = property.getter(db) {
|
if let Some(getter) = property.getter(db) {
|
||||||
if let Ok(return_ty) = getter
|
if let Ok(return_ty) = getter
|
||||||
.try_call(db, &CallArgumentTypes::positional([*instance]))
|
.try_call(db, &CallArguments::positional([*instance]))
|
||||||
.map(|binding| binding.return_type(db))
|
.map(|binding| binding.return_type(db))
|
||||||
{
|
{
|
||||||
overload.set_return_type(return_ty);
|
overload.set_return_type(return_ty);
|
||||||
|
@ -469,10 +465,9 @@ impl<'db> Bindings<'db> {
|
||||||
] = overload.parameter_types()
|
] = overload.parameter_types()
|
||||||
{
|
{
|
||||||
if let Some(setter) = property.setter(db) {
|
if let Some(setter) = property.setter(db) {
|
||||||
if let Err(_call_error) = setter.try_call(
|
if let Err(_call_error) = setter
|
||||||
db,
|
.try_call(db, &CallArguments::positional([*instance, *value]))
|
||||||
&CallArgumentTypes::positional([*instance, *value]),
|
{
|
||||||
) {
|
|
||||||
overload.errors.push(BindingError::InternalCallError(
|
overload.errors.push(BindingError::InternalCallError(
|
||||||
"calling the setter failed",
|
"calling the setter failed",
|
||||||
));
|
));
|
||||||
|
@ -488,10 +483,9 @@ impl<'db> Bindings<'db> {
|
||||||
Type::MethodWrapper(MethodWrapperKind::PropertyDunderSet(property)) => {
|
Type::MethodWrapper(MethodWrapperKind::PropertyDunderSet(property)) => {
|
||||||
if let [Some(instance), Some(value), ..] = overload.parameter_types() {
|
if let [Some(instance), Some(value), ..] = overload.parameter_types() {
|
||||||
if let Some(setter) = property.setter(db) {
|
if let Some(setter) = property.setter(db) {
|
||||||
if let Err(_call_error) = setter.try_call(
|
if let Err(_call_error) = setter
|
||||||
db,
|
.try_call(db, &CallArguments::positional([*instance, *value]))
|
||||||
&CallArgumentTypes::positional([*instance, *value]),
|
{
|
||||||
) {
|
|
||||||
overload.errors.push(BindingError::InternalCallError(
|
overload.errors.push(BindingError::InternalCallError(
|
||||||
"calling the setter failed",
|
"calling the setter failed",
|
||||||
));
|
));
|
||||||
|
@ -1161,7 +1155,7 @@ impl<'db> CallableBinding<'db> {
|
||||||
|
|
||||||
fn match_parameters(
|
fn match_parameters(
|
||||||
&mut self,
|
&mut self,
|
||||||
arguments: &CallArguments<'_>,
|
arguments: &CallArguments<'_, 'db>,
|
||||||
argument_forms: &mut [Option<ParameterForm>],
|
argument_forms: &mut [Option<ParameterForm>],
|
||||||
conflicting_forms: &mut [bool],
|
conflicting_forms: &mut [bool],
|
||||||
) {
|
) {
|
||||||
|
@ -1174,7 +1168,7 @@ impl<'db> CallableBinding<'db> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_types(&mut self, db: &'db dyn Db, argument_types: &CallArgumentTypes<'_, 'db>) {
|
fn check_types(&mut self, db: &'db dyn Db, argument_types: &CallArguments<'_, 'db>) {
|
||||||
// If this callable is a bound method, prepend the self instance onto the arguments list
|
// If this callable is a bound method, prepend the self instance onto the arguments list
|
||||||
// before checking.
|
// before checking.
|
||||||
let argument_types = argument_types.with_self(self.bound_type);
|
let argument_types = argument_types.with_self(self.bound_type);
|
||||||
|
@ -1186,7 +1180,7 @@ impl<'db> CallableBinding<'db> {
|
||||||
// still perform type checking for non-overloaded function to provide better user
|
// still perform type checking for non-overloaded function to provide better user
|
||||||
// experience.
|
// experience.
|
||||||
if let [overload] = self.overloads.as_mut_slice() {
|
if let [overload] = self.overloads.as_mut_slice() {
|
||||||
overload.check_types(db, argument_types.as_ref(), argument_types.types());
|
overload.check_types(db, argument_types.as_ref());
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1194,11 +1188,7 @@ impl<'db> CallableBinding<'db> {
|
||||||
// If only one candidate overload remains, it is the winning match. Evaluate it as
|
// If only one candidate overload remains, it is the winning match. Evaluate it as
|
||||||
// a regular (non-overloaded) call.
|
// a regular (non-overloaded) call.
|
||||||
self.matching_overload_index = Some(index);
|
self.matching_overload_index = Some(index);
|
||||||
self.overloads[index].check_types(
|
self.overloads[index].check_types(db, argument_types.as_ref());
|
||||||
db,
|
|
||||||
argument_types.as_ref(),
|
|
||||||
argument_types.types(),
|
|
||||||
);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
MatchingOverloadIndex::Multiple(indexes) => {
|
MatchingOverloadIndex::Multiple(indexes) => {
|
||||||
|
@ -1216,7 +1206,7 @@ impl<'db> CallableBinding<'db> {
|
||||||
// Step 2: Evaluate each remaining overload as a regular (non-overloaded) call to determine
|
// Step 2: Evaluate each remaining overload as a regular (non-overloaded) call to determine
|
||||||
// whether it is compatible with the supplied argument list.
|
// whether it is compatible with the supplied argument list.
|
||||||
for (_, overload) in self.matching_overloads_mut() {
|
for (_, overload) in self.matching_overloads_mut() {
|
||||||
overload.check_types(db, argument_types.as_ref(), argument_types.types());
|
overload.check_types(db, argument_types.as_ref());
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.matching_overload_index() {
|
match self.matching_overload_index() {
|
||||||
|
@ -1232,7 +1222,7 @@ impl<'db> CallableBinding<'db> {
|
||||||
// TODO: Step 4
|
// TODO: Step 4
|
||||||
|
|
||||||
// Step 5
|
// Step 5
|
||||||
self.filter_overloads_using_any_or_unknown(db, argument_types.types(), &indexes);
|
self.filter_overloads_using_any_or_unknown(db, argument_types.as_ref(), &indexes);
|
||||||
|
|
||||||
// We're returning here because this shouldn't lead to argument type expansion.
|
// We're returning here because this shouldn't lead to argument type expansion.
|
||||||
return;
|
return;
|
||||||
|
@ -1268,7 +1258,7 @@ impl<'db> CallableBinding<'db> {
|
||||||
let pre_evaluation_snapshot = snapshotter.take(self);
|
let pre_evaluation_snapshot = snapshotter.take(self);
|
||||||
|
|
||||||
for (_, overload) in self.matching_overloads_mut() {
|
for (_, overload) in self.matching_overloads_mut() {
|
||||||
overload.check_types(db, argument_types.as_ref(), expanded_argument_types);
|
overload.check_types(db, expanded_argument_types);
|
||||||
}
|
}
|
||||||
|
|
||||||
let return_type = match self.matching_overload_index() {
|
let return_type = match self.matching_overload_index() {
|
||||||
|
@ -1358,7 +1348,7 @@ impl<'db> CallableBinding<'db> {
|
||||||
fn filter_overloads_using_any_or_unknown(
|
fn filter_overloads_using_any_or_unknown(
|
||||||
&mut self,
|
&mut self,
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
argument_types: &[Type<'db>],
|
arguments: &CallArguments<'_, 'db>,
|
||||||
matching_overload_indexes: &[usize],
|
matching_overload_indexes: &[usize],
|
||||||
) {
|
) {
|
||||||
// These are the parameter indexes that matches the arguments that participate in the
|
// These are the parameter indexes that matches the arguments that participate in the
|
||||||
|
@ -1372,7 +1362,7 @@ impl<'db> CallableBinding<'db> {
|
||||||
// participating parameter indexes.
|
// participating parameter indexes.
|
||||||
let mut top_materialized_argument_types = vec![];
|
let mut top_materialized_argument_types = vec![];
|
||||||
|
|
||||||
for (argument_index, argument_type) in argument_types.iter().enumerate() {
|
for (argument_index, argument_type) in arguments.iter_types().enumerate() {
|
||||||
let mut first_parameter_type: Option<Type<'db>> = None;
|
let mut first_parameter_type: Option<Type<'db>> = None;
|
||||||
let mut participating_parameter_index = None;
|
let mut participating_parameter_index = None;
|
||||||
|
|
||||||
|
@ -1415,8 +1405,8 @@ impl<'db> CallableBinding<'db> {
|
||||||
self.overloads[*current_index].mark_as_unmatched_overload();
|
self.overloads[*current_index].mark_as_unmatched_overload();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let mut parameter_types = Vec::with_capacity(argument_types.len());
|
let mut parameter_types = Vec::with_capacity(arguments.len());
|
||||||
for argument_index in 0..argument_types.len() {
|
for argument_index in 0..arguments.len() {
|
||||||
// The parameter types at the current argument index.
|
// The parameter types at the current argument index.
|
||||||
let mut current_parameter_types = vec![];
|
let mut current_parameter_types = vec![];
|
||||||
for overload_index in &matching_overload_indexes[..=upto] {
|
for overload_index in &matching_overload_indexes[..=upto] {
|
||||||
|
@ -1904,8 +1894,7 @@ impl<'a, 'db> ArgumentMatcher<'a, 'db> {
|
||||||
struct ArgumentTypeChecker<'a, 'db> {
|
struct ArgumentTypeChecker<'a, 'db> {
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
signature: &'a Signature<'db>,
|
signature: &'a Signature<'db>,
|
||||||
arguments: &'a CallArguments<'a>,
|
arguments: &'a CallArguments<'a, 'db>,
|
||||||
argument_types: &'a [Type<'db>],
|
|
||||||
argument_parameters: &'a [Option<usize>],
|
argument_parameters: &'a [Option<usize>],
|
||||||
parameter_tys: &'a mut [Option<Type<'db>>],
|
parameter_tys: &'a mut [Option<Type<'db>>],
|
||||||
errors: &'a mut Vec<BindingError<'db>>,
|
errors: &'a mut Vec<BindingError<'db>>,
|
||||||
|
@ -1918,8 +1907,7 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
|
||||||
fn new(
|
fn new(
|
||||||
db: &'db dyn Db,
|
db: &'db dyn Db,
|
||||||
signature: &'a Signature<'db>,
|
signature: &'a Signature<'db>,
|
||||||
arguments: &'a CallArguments<'a>,
|
arguments: &'a CallArguments<'a, 'db>,
|
||||||
argument_types: &'a [Type<'db>],
|
|
||||||
argument_parameters: &'a [Option<usize>],
|
argument_parameters: &'a [Option<usize>],
|
||||||
parameter_tys: &'a mut [Option<Type<'db>>],
|
parameter_tys: &'a mut [Option<Type<'db>>],
|
||||||
errors: &'a mut Vec<BindingError<'db>>,
|
errors: &'a mut Vec<BindingError<'db>>,
|
||||||
|
@ -1928,7 +1916,6 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
|
||||||
db,
|
db,
|
||||||
signature,
|
signature,
|
||||||
arguments,
|
arguments,
|
||||||
argument_types,
|
|
||||||
argument_parameters,
|
argument_parameters,
|
||||||
parameter_tys,
|
parameter_tys,
|
||||||
errors,
|
errors,
|
||||||
|
@ -1940,9 +1927,7 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
|
||||||
fn enumerate_argument_types(
|
fn enumerate_argument_types(
|
||||||
&self,
|
&self,
|
||||||
) -> impl Iterator<Item = (usize, Option<usize>, Argument<'a>, Type<'db>)> + 'a {
|
) -> impl Iterator<Item = (usize, Option<usize>, Argument<'a>, Type<'db>)> + 'a {
|
||||||
let mut iter = (self.arguments.iter())
|
let mut iter = self.arguments.iter().enumerate();
|
||||||
.zip(self.argument_types.iter().copied())
|
|
||||||
.enumerate();
|
|
||||||
let mut num_synthetic_args = 0;
|
let mut num_synthetic_args = 0;
|
||||||
std::iter::from_fn(move || {
|
std::iter::from_fn(move || {
|
||||||
let (argument_index, (argument, argument_type)) = iter.next()?;
|
let (argument_index, (argument, argument_type)) = iter.next()?;
|
||||||
|
@ -1961,7 +1946,7 @@ impl<'a, 'db> ArgumentTypeChecker<'a, 'db> {
|
||||||
argument_index,
|
argument_index,
|
||||||
adjusted_argument_index,
|
adjusted_argument_index,
|
||||||
argument,
|
argument,
|
||||||
argument_type,
|
argument_type.unwrap_or_else(Type::unknown),
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -2127,7 +2112,7 @@ impl<'db> Binding<'db> {
|
||||||
|
|
||||||
pub(crate) fn match_parameters(
|
pub(crate) fn match_parameters(
|
||||||
&mut self,
|
&mut self,
|
||||||
arguments: &CallArguments<'_>,
|
arguments: &CallArguments<'_, 'db>,
|
||||||
argument_forms: &mut [Option<ParameterForm>],
|
argument_forms: &mut [Option<ParameterForm>],
|
||||||
conflicting_forms: &mut [bool],
|
conflicting_forms: &mut [bool],
|
||||||
) {
|
) {
|
||||||
|
@ -2139,7 +2124,7 @@ impl<'db> Binding<'db> {
|
||||||
conflicting_forms,
|
conflicting_forms,
|
||||||
&mut self.errors,
|
&mut self.errors,
|
||||||
);
|
);
|
||||||
for (argument_index, argument) in arguments.iter().enumerate() {
|
for (argument_index, (argument, _)) in arguments.iter().enumerate() {
|
||||||
match argument {
|
match argument {
|
||||||
Argument::Positional | Argument::Synthetic => {
|
Argument::Positional | Argument::Synthetic => {
|
||||||
let _ = matcher.match_positional(argument_index, argument);
|
let _ = matcher.match_positional(argument_index, argument);
|
||||||
|
@ -2158,17 +2143,11 @@ impl<'db> Binding<'db> {
|
||||||
self.argument_parameters = matcher.finish();
|
self.argument_parameters = matcher.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_types(
|
fn check_types(&mut self, db: &'db dyn Db, arguments: &CallArguments<'_, 'db>) {
|
||||||
&mut self,
|
|
||||||
db: &'db dyn Db,
|
|
||||||
arguments: &CallArguments<'_>,
|
|
||||||
argument_types: &[Type<'db>],
|
|
||||||
) {
|
|
||||||
let mut checker = ArgumentTypeChecker::new(
|
let mut checker = ArgumentTypeChecker::new(
|
||||||
db,
|
db,
|
||||||
&self.signature,
|
&self.signature,
|
||||||
arguments,
|
arguments,
|
||||||
argument_types,
|
|
||||||
&self.argument_parameters,
|
&self.argument_parameters,
|
||||||
&mut self.parameter_tys,
|
&mut self.parameter_tys,
|
||||||
&mut self.errors,
|
&mut self.errors,
|
||||||
|
@ -2210,7 +2189,7 @@ impl<'db> Binding<'db> {
|
||||||
|
|
||||||
pub(crate) fn arguments_for_parameter<'a>(
|
pub(crate) fn arguments_for_parameter<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
argument_types: &'a CallArgumentTypes<'a, 'db>,
|
argument_types: &'a CallArguments<'a, 'db>,
|
||||||
parameter_index: usize,
|
parameter_index: usize,
|
||||||
) -> impl Iterator<Item = (Argument<'a>, Type<'db>)> + 'a {
|
) -> impl Iterator<Item = (Argument<'a>, Type<'db>)> + 'a {
|
||||||
argument_types
|
argument_types
|
||||||
|
@ -2219,7 +2198,9 @@ impl<'db> Binding<'db> {
|
||||||
.filter(move |(_, argument_parameter)| {
|
.filter(move |(_, argument_parameter)| {
|
||||||
argument_parameter.is_some_and(|ap| ap == parameter_index)
|
argument_parameter.is_some_and(|ap| ap == parameter_index)
|
||||||
})
|
})
|
||||||
.map(|(arg_and_type, _)| arg_and_type)
|
.map(|((argument, argument_type), _)| {
|
||||||
|
(argument, argument_type.unwrap_or_else(Type::unknown))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark this overload binding as an unmatched overload.
|
/// Mark this overload binding as an unmatched overload.
|
||||||
|
|
|
@ -38,7 +38,7 @@ use crate::{
|
||||||
place_table, semantic_index, use_def_map,
|
place_table, semantic_index, use_def_map,
|
||||||
},
|
},
|
||||||
types::{
|
types::{
|
||||||
CallArgumentTypes, CallError, CallErrorKind, MetaclassCandidate, UnionBuilder, UnionType,
|
CallArguments, CallError, CallErrorKind, MetaclassCandidate, UnionBuilder, UnionType,
|
||||||
definition_expression_type,
|
definition_expression_type,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -1210,7 +1210,7 @@ impl<'db> ClassLiteral<'db> {
|
||||||
.to_specialized_instance(db, [KnownClass::Str.to_instance(db), Type::any()]);
|
.to_specialized_instance(db, [KnownClass::Str.to_instance(db), Type::any()]);
|
||||||
|
|
||||||
// TODO: Other keyword arguments?
|
// TODO: Other keyword arguments?
|
||||||
let arguments = CallArgumentTypes::positional([name, bases, namespace]);
|
let arguments = CallArguments::positional([name, bases, namespace]);
|
||||||
|
|
||||||
let return_ty_result = match metaclass.try_call(db, &arguments) {
|
let return_ty_result = match metaclass.try_call(db, &arguments) {
|
||||||
Ok(bindings) => Ok(bindings.return_type(db)),
|
Ok(bindings) => Ok(bindings.return_type(db)),
|
||||||
|
@ -3312,7 +3312,7 @@ impl KnownClass {
|
||||||
context: &InferContext<'db, '_>,
|
context: &InferContext<'db, '_>,
|
||||||
index: &SemanticIndex<'db>,
|
index: &SemanticIndex<'db>,
|
||||||
overload_binding: &Binding<'db>,
|
overload_binding: &Binding<'db>,
|
||||||
call_argument_types: &CallArgumentTypes<'_, 'db>,
|
call_argument_types: &CallArguments<'_, 'db>,
|
||||||
call_expression: &ast::ExprCall,
|
call_expression: &ast::ExprCall,
|
||||||
) -> Option<Type<'db>> {
|
) -> Option<Type<'db>> {
|
||||||
let db = context.db();
|
let db = context.db();
|
||||||
|
|
|
@ -2,7 +2,7 @@ use super::call::CallErrorKind;
|
||||||
use super::context::InferContext;
|
use super::context::InferContext;
|
||||||
use super::mro::DuplicateBaseError;
|
use super::mro::DuplicateBaseError;
|
||||||
use super::{
|
use super::{
|
||||||
CallArgumentTypes, CallDunderError, ClassBase, ClassLiteral, KnownClass,
|
CallArguments, CallDunderError, ClassBase, ClassLiteral, KnownClass,
|
||||||
add_inferred_python_version_hint_to_diagnostic,
|
add_inferred_python_version_hint_to_diagnostic,
|
||||||
};
|
};
|
||||||
use crate::lint::{Level, LintRegistryBuilder, LintStatus};
|
use crate::lint::{Level, LintRegistryBuilder, LintStatus};
|
||||||
|
@ -2325,7 +2325,7 @@ pub(crate) fn report_invalid_or_unsupported_base(
|
||||||
match base_type.try_call_dunder(
|
match base_type.try_call_dunder(
|
||||||
db,
|
db,
|
||||||
"__mro_entries__",
|
"__mro_entries__",
|
||||||
CallArgumentTypes::positional([tuple_of_types]),
|
CallArguments::positional([tuple_of_types]),
|
||||||
) {
|
) {
|
||||||
Ok(ret) => {
|
Ok(ret) => {
|
||||||
if ret.return_type(db).is_assignable_to(db, tuple_of_types) {
|
if ret.return_type(db).is_assignable_to(db, tuple_of_types) {
|
||||||
|
|
|
@ -85,7 +85,7 @@ use crate::semantic_index::place::{
|
||||||
use crate::semantic_index::{
|
use crate::semantic_index::{
|
||||||
ApplicableConstraints, EagerSnapshotResult, SemanticIndex, place_table, semantic_index,
|
ApplicableConstraints, EagerSnapshotResult, SemanticIndex, place_table, semantic_index,
|
||||||
};
|
};
|
||||||
use crate::types::call::{Binding, Bindings, CallArgumentTypes, CallArguments, CallError};
|
use crate::types::call::{Binding, Bindings, CallArguments, CallError};
|
||||||
use crate::types::class::{CodeGeneratorKind, MetaclassErrorKind, SliceLiteral};
|
use crate::types::class::{CodeGeneratorKind, MetaclassErrorKind, SliceLiteral};
|
||||||
use crate::types::diagnostic::{
|
use crate::types::diagnostic::{
|
||||||
self, CALL_NON_CALLABLE, CONFLICTING_DECLARATIONS, CONFLICTING_METACLASS,
|
self, CALL_NON_CALLABLE, CONFLICTING_DECLARATIONS, CONFLICTING_METACLASS,
|
||||||
|
@ -1965,9 +1965,9 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
self.infer_type_parameters(type_params);
|
self.infer_type_parameters(type_params);
|
||||||
|
|
||||||
if let Some(arguments) = class.arguments.as_deref() {
|
if let Some(arguments) = class.arguments.as_deref() {
|
||||||
let call_arguments = CallArguments::from_arguments(arguments);
|
let mut call_arguments = CallArguments::from_arguments(arguments);
|
||||||
let argument_forms = vec![Some(ParameterForm::Value); call_arguments.len()];
|
let argument_forms = vec![Some(ParameterForm::Value); call_arguments.len()];
|
||||||
self.infer_argument_types(arguments, call_arguments, &argument_forms);
|
self.infer_argument_types(arguments, &mut call_arguments, &argument_forms);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2374,7 +2374,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
|
|
||||||
for (decorator_ty, decorator_node) in decorator_types_and_nodes.iter().rev() {
|
for (decorator_ty, decorator_node) in decorator_types_and_nodes.iter().rev() {
|
||||||
inferred_ty = match decorator_ty
|
inferred_ty = match decorator_ty
|
||||||
.try_call(self.db(), &CallArgumentTypes::positional([inferred_ty]))
|
.try_call(self.db(), &CallArguments::positional([inferred_ty]))
|
||||||
.map(|bindings| bindings.return_type(self.db()))
|
.map(|bindings| bindings.return_type(self.db()))
|
||||||
{
|
{
|
||||||
Ok(return_ty) => return_ty,
|
Ok(return_ty) => return_ty,
|
||||||
|
@ -3460,10 +3460,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
let setattr_dunder_call_result = object_ty.try_call_dunder_with_policy(
|
let setattr_dunder_call_result = object_ty.try_call_dunder_with_policy(
|
||||||
db,
|
db,
|
||||||
"__setattr__",
|
"__setattr__",
|
||||||
&mut CallArgumentTypes::positional([
|
&mut CallArguments::positional([Type::string_literal(db, attribute), value_ty]),
|
||||||
Type::string_literal(db, attribute),
|
|
||||||
value_ty,
|
|
||||||
]),
|
|
||||||
MemberLookupPolicy::MRO_NO_OBJECT_FALLBACK,
|
MemberLookupPolicy::MRO_NO_OBJECT_FALLBACK,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -3548,7 +3545,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
let successful_call = meta_dunder_set
|
let successful_call = meta_dunder_set
|
||||||
.try_call(
|
.try_call(
|
||||||
db,
|
db,
|
||||||
&CallArgumentTypes::positional([
|
&CallArguments::positional([
|
||||||
meta_attr_ty,
|
meta_attr_ty,
|
||||||
object_ty,
|
object_ty,
|
||||||
value_ty,
|
value_ty,
|
||||||
|
@ -3674,11 +3671,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
let successful_call = meta_dunder_set
|
let successful_call = meta_dunder_set
|
||||||
.try_call(
|
.try_call(
|
||||||
db,
|
db,
|
||||||
&CallArgumentTypes::positional([
|
&CallArguments::positional([meta_attr_ty, object_ty, value_ty]),
|
||||||
meta_attr_ty,
|
|
||||||
object_ty,
|
|
||||||
value_ty,
|
|
||||||
]),
|
|
||||||
)
|
)
|
||||||
.is_ok();
|
.is_ok();
|
||||||
|
|
||||||
|
@ -4099,7 +4092,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
let call = target_type.try_call_dunder(
|
let call = target_type.try_call_dunder(
|
||||||
db,
|
db,
|
||||||
op.in_place_dunder(),
|
op.in_place_dunder(),
|
||||||
CallArgumentTypes::positional([value_type]),
|
CallArguments::positional([value_type]),
|
||||||
);
|
);
|
||||||
|
|
||||||
match call {
|
match call {
|
||||||
|
@ -4740,28 +4733,31 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
fn infer_argument_types<'a>(
|
fn infer_argument_types<'a>(
|
||||||
&mut self,
|
&mut self,
|
||||||
ast_arguments: &ast::Arguments,
|
ast_arguments: &ast::Arguments,
|
||||||
arguments: CallArguments<'a>,
|
arguments: &mut CallArguments<'a, 'db>,
|
||||||
argument_forms: &[Option<ParameterForm>],
|
argument_forms: &[Option<ParameterForm>],
|
||||||
) -> CallArgumentTypes<'a, 'db> {
|
) {
|
||||||
let mut ast_arguments = ast_arguments.arguments_source_order();
|
debug_assert!(
|
||||||
CallArgumentTypes::new(arguments, |index, _| {
|
ast_arguments.len() == arguments.len() && arguments.len() == argument_forms.len()
|
||||||
let arg_or_keyword = ast_arguments
|
);
|
||||||
.next()
|
let iter = (arguments.iter_mut())
|
||||||
.expect("argument lists should have consistent lengths");
|
.zip(argument_forms.iter().copied())
|
||||||
match arg_or_keyword {
|
.zip(ast_arguments.arguments_source_order());
|
||||||
|
for (((_, argument_type), form), arg_or_keyword) in iter {
|
||||||
|
let ty = match arg_or_keyword {
|
||||||
ast::ArgOrKeyword::Arg(arg) => match arg {
|
ast::ArgOrKeyword::Arg(arg) => match arg {
|
||||||
ast::Expr::Starred(ast::ExprStarred { value, .. }) => {
|
ast::Expr::Starred(ast::ExprStarred { value, .. }) => {
|
||||||
let ty = self.infer_argument_type(value, argument_forms[index]);
|
let ty = self.infer_argument_type(value, form);
|
||||||
self.store_expression_type(arg, ty);
|
self.store_expression_type(arg, ty);
|
||||||
ty
|
ty
|
||||||
}
|
}
|
||||||
_ => self.infer_argument_type(arg, argument_forms[index]),
|
_ => self.infer_argument_type(arg, form),
|
||||||
},
|
},
|
||||||
ast::ArgOrKeyword::Keyword(ast::Keyword { value, .. }) => {
|
ast::ArgOrKeyword::Keyword(ast::Keyword { value, .. }) => {
|
||||||
self.infer_argument_type(value, argument_forms[index])
|
self.infer_argument_type(value, form)
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
})
|
*argument_type = Some(ty);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_argument_type(
|
fn infer_argument_type(
|
||||||
|
@ -5450,7 +5446,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
// We don't call `Type::try_call`, because we want to perform type inference on the
|
// We don't call `Type::try_call`, because we want to perform type inference on the
|
||||||
// arguments after matching them to parameters, but before checking that the argument types
|
// arguments after matching them to parameters, but before checking that the argument types
|
||||||
// are assignable to any parameter annotations.
|
// are assignable to any parameter annotations.
|
||||||
let call_arguments = CallArguments::from_arguments(arguments);
|
let mut call_arguments = CallArguments::from_arguments(arguments);
|
||||||
|
|
||||||
let callable_type = self.infer_maybe_standalone_expression(func);
|
let callable_type = self.infer_maybe_standalone_expression(func);
|
||||||
|
|
||||||
|
@ -5523,11 +5519,10 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
.is_none_or(|enum_class| !class.is_subclass_of(self.db(), enum_class))
|
.is_none_or(|enum_class| !class.is_subclass_of(self.db(), enum_class))
|
||||||
{
|
{
|
||||||
let argument_forms = vec![Some(ParameterForm::Value); call_arguments.len()];
|
let argument_forms = vec![Some(ParameterForm::Value); call_arguments.len()];
|
||||||
let call_argument_types =
|
self.infer_argument_types(arguments, &mut call_arguments, &argument_forms);
|
||||||
self.infer_argument_types(arguments, call_arguments, &argument_forms);
|
|
||||||
|
|
||||||
return callable_type
|
return callable_type
|
||||||
.try_call_constructor(self.db(), call_argument_types)
|
.try_call_constructor(self.db(), call_arguments)
|
||||||
.unwrap_or_else(|err| {
|
.unwrap_or_else(|err| {
|
||||||
err.report_diagnostic(&self.context, callable_type, call_expression.into());
|
err.report_diagnostic(&self.context, callable_type, call_expression.into());
|
||||||
err.return_type()
|
err.return_type()
|
||||||
|
@ -5538,10 +5533,9 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
let bindings = callable_type
|
let bindings = callable_type
|
||||||
.bindings(self.db())
|
.bindings(self.db())
|
||||||
.match_parameters(&call_arguments);
|
.match_parameters(&call_arguments);
|
||||||
let call_argument_types =
|
self.infer_argument_types(arguments, &mut call_arguments, &bindings.argument_forms);
|
||||||
self.infer_argument_types(arguments, call_arguments, &bindings.argument_forms);
|
|
||||||
|
|
||||||
let mut bindings = match bindings.check_types(self.db(), &call_argument_types) {
|
let mut bindings = match bindings.check_types(self.db(), &call_arguments) {
|
||||||
Ok(bindings) => bindings,
|
Ok(bindings) => bindings,
|
||||||
Err(CallError(_, bindings)) => {
|
Err(CallError(_, bindings)) => {
|
||||||
bindings.report_diagnostics(&self.context, call_expression.into());
|
bindings.report_diagnostics(&self.context, call_expression.into());
|
||||||
|
@ -5574,7 +5568,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
&self.context,
|
&self.context,
|
||||||
self.index,
|
self.index,
|
||||||
overload,
|
overload,
|
||||||
&call_argument_types,
|
&call_arguments,
|
||||||
call_expression,
|
call_expression,
|
||||||
);
|
);
|
||||||
if let Some(overridden_return) = overridden_return {
|
if let Some(overridden_return) = overridden_return {
|
||||||
|
@ -6399,7 +6393,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
match operand_type.try_call_dunder(
|
match operand_type.try_call_dunder(
|
||||||
self.db(),
|
self.db(),
|
||||||
unary_dunder_method,
|
unary_dunder_method,
|
||||||
CallArgumentTypes::none(),
|
CallArguments::none(),
|
||||||
) {
|
) {
|
||||||
Ok(outcome) => outcome.return_type(self.db()),
|
Ok(outcome) => outcome.return_type(self.db()),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -6773,7 +6767,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
.try_call_dunder(
|
.try_call_dunder(
|
||||||
self.db(),
|
self.db(),
|
||||||
reflected_dunder,
|
reflected_dunder,
|
||||||
CallArgumentTypes::positional([left_ty]),
|
CallArguments::positional([left_ty]),
|
||||||
)
|
)
|
||||||
.map(|outcome| outcome.return_type(self.db()))
|
.map(|outcome| outcome.return_type(self.db()))
|
||||||
.or_else(|_| {
|
.or_else(|_| {
|
||||||
|
@ -6781,7 +6775,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
.try_call_dunder(
|
.try_call_dunder(
|
||||||
self.db(),
|
self.db(),
|
||||||
op.dunder(),
|
op.dunder(),
|
||||||
CallArgumentTypes::positional([right_ty]),
|
CallArguments::positional([right_ty]),
|
||||||
)
|
)
|
||||||
.map(|outcome| outcome.return_type(self.db()))
|
.map(|outcome| outcome.return_type(self.db()))
|
||||||
})
|
})
|
||||||
|
@ -6793,7 +6787,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
.try_call_dunder(
|
.try_call_dunder(
|
||||||
self.db(),
|
self.db(),
|
||||||
op.dunder(),
|
op.dunder(),
|
||||||
CallArgumentTypes::positional([right_ty]),
|
CallArguments::positional([right_ty]),
|
||||||
)
|
)
|
||||||
.map(|outcome| outcome.return_type(self.db()))
|
.map(|outcome| outcome.return_type(self.db()))
|
||||||
.ok();
|
.ok();
|
||||||
|
@ -6806,7 +6800,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
.try_call_dunder(
|
.try_call_dunder(
|
||||||
self.db(),
|
self.db(),
|
||||||
op.reflected_dunder(),
|
op.reflected_dunder(),
|
||||||
CallArgumentTypes::positional([left_ty]),
|
CallArguments::positional([left_ty]),
|
||||||
)
|
)
|
||||||
.map(|outcome| outcome.return_type(self.db()))
|
.map(|outcome| outcome.return_type(self.db()))
|
||||||
.ok()
|
.ok()
|
||||||
|
@ -7538,7 +7532,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
// The following resource has details about the rich comparison algorithm:
|
// The following resource has details about the rich comparison algorithm:
|
||||||
// https://snarky.ca/unravelling-rich-comparison-operators/
|
// https://snarky.ca/unravelling-rich-comparison-operators/
|
||||||
let call_dunder = |op: RichCompareOperator, left: Type<'db>, right: Type<'db>| {
|
let call_dunder = |op: RichCompareOperator, left: Type<'db>, right: Type<'db>| {
|
||||||
left.try_call_dunder(db, op.dunder(), CallArgumentTypes::positional([right]))
|
left.try_call_dunder(db, op.dunder(), CallArguments::positional([right]))
|
||||||
.map(|outcome| outcome.return_type(db))
|
.map(|outcome| outcome.return_type(db))
|
||||||
.ok()
|
.ok()
|
||||||
};
|
};
|
||||||
|
@ -7584,7 +7578,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
Place::Type(contains_dunder, Boundness::Bound) => {
|
Place::Type(contains_dunder, Boundness::Bound) => {
|
||||||
// If `__contains__` is available, it is used directly for the membership test.
|
// If `__contains__` is available, it is used directly for the membership test.
|
||||||
contains_dunder
|
contains_dunder
|
||||||
.try_call(db, &CallArgumentTypes::positional([right, left]))
|
.try_call(db, &CallArguments::positional([right, left]))
|
||||||
.map(|bindings| bindings.return_type(db))
|
.map(|bindings| bindings.return_type(db))
|
||||||
.ok()
|
.ok()
|
||||||
}
|
}
|
||||||
|
@ -7807,16 +7801,16 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
let slice_node = subscript.slice.as_ref();
|
let slice_node = subscript.slice.as_ref();
|
||||||
let call_argument_types = match slice_node {
|
let call_argument_types = match slice_node {
|
||||||
ast::Expr::Tuple(tuple) => {
|
ast::Expr::Tuple(tuple) => {
|
||||||
let arguments = CallArgumentTypes::positional(
|
let arguments = CallArguments::positional(
|
||||||
tuple.elts.iter().map(|elt| self.infer_type_expression(elt)),
|
tuple.elts.iter().map(|elt| self.infer_type_expression(elt)),
|
||||||
);
|
);
|
||||||
self.store_expression_type(
|
self.store_expression_type(
|
||||||
slice_node,
|
slice_node,
|
||||||
TupleType::from_elements(self.db(), arguments.iter().map(|(_, ty)| ty)),
|
TupleType::from_elements(self.db(), arguments.iter_types()),
|
||||||
);
|
);
|
||||||
arguments
|
arguments
|
||||||
}
|
}
|
||||||
_ => CallArgumentTypes::positional([self.infer_type_expression(slice_node)]),
|
_ => CallArguments::positional([self.infer_type_expression(slice_node)]),
|
||||||
};
|
};
|
||||||
let binding = Binding::single(value_ty, generic_context.signature(self.db()));
|
let binding = Binding::single(value_ty, generic_context.signature(self.db()));
|
||||||
let bindings = match Bindings::from(binding)
|
let bindings = match Bindings::from(binding)
|
||||||
|
@ -8066,7 +8060,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
match value_ty.try_call_dunder(
|
match value_ty.try_call_dunder(
|
||||||
self.db(),
|
self.db(),
|
||||||
"__getitem__",
|
"__getitem__",
|
||||||
CallArgumentTypes::positional([slice_ty]),
|
CallArguments::positional([slice_ty]),
|
||||||
) {
|
) {
|
||||||
Ok(outcome) => return outcome.return_type(self.db()),
|
Ok(outcome) => return outcome.return_type(self.db()),
|
||||||
Err(err @ CallDunderError::PossiblyUnbound { .. }) => {
|
Err(err @ CallDunderError::PossiblyUnbound { .. }) => {
|
||||||
|
@ -8132,7 +8126,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
|
|
||||||
match ty.try_call(
|
match ty.try_call(
|
||||||
self.db(),
|
self.db(),
|
||||||
&CallArgumentTypes::positional([value_ty, slice_ty]),
|
&CallArguments::positional([value_ty, slice_ty]),
|
||||||
) {
|
) {
|
||||||
Ok(bindings) => return bindings.return_type(self.db()),
|
Ok(bindings) => return bindings.return_type(self.db()),
|
||||||
Err(CallError(_, bindings)) => {
|
Err(CallError(_, bindings)) => {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue