Remove nested PossiblyUndefined variants
This commit is contained in:
@@ -1907,9 +1907,6 @@ impl<'db> Type<'db> {
|
||||
match self.call_dunder(db, "__len__", &CallArguments::positional([*self])) {
|
||||
CallDunderOutcome::MethodNotAvailable => CallDunderLenOutcome::MethodNotAvailable,
|
||||
CallDunderOutcome::Call(outcome) => CallDunderLenOutcome::Call(outcome),
|
||||
CallDunderOutcome::PossiblyUnbound(outcome) => {
|
||||
CallDunderLenOutcome::PossiblyUnbound(outcome)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1918,9 +1915,8 @@ impl<'db> Type<'db> {
|
||||
///
|
||||
/// In the second case, the return type of `len()` in `typeshed` (`int`)
|
||||
/// is used as a fallback.
|
||||
#[must_use]
|
||||
fn len(&self, db: &'db dyn Db) -> Option<Type<'db>> {
|
||||
let len = self.__len__(db).return_type(db)?;
|
||||
|
||||
fn non_negative_int_literal<'db>(db: &'db dyn Db, ty: Type<'db>) -> Option<Type<'db>> {
|
||||
match ty {
|
||||
Type::IntLiteral(value) => (value >= 0).then_some(ty),
|
||||
@@ -1936,11 +1932,12 @@ impl<'db> Type<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
let len = self.__len__(db).return_type(db)?;
|
||||
|
||||
non_negative_int_literal(db, len)
|
||||
}
|
||||
|
||||
/// Return the outcome of calling an object of this type.
|
||||
#[must_use]
|
||||
fn call(self, db: &'db dyn Db, arguments: &CallArguments<'_, 'db>) -> CallOutcome<'db> {
|
||||
match self {
|
||||
Type::FunctionLiteral(function_type) => {
|
||||
@@ -2068,15 +2065,18 @@ impl<'db> Type<'db> {
|
||||
not_callable_ty: self,
|
||||
}
|
||||
}
|
||||
// Turn "possibly unbound object of type `Literal['__call__']`"
|
||||
// into "`X` not callable (possibly unbound `__call__` method)"
|
||||
CallDunderOutcome::Call(CallOutcome::PossiblyUnboundDunderCall {
|
||||
called_ty: _,
|
||||
call_outcome,
|
||||
}) => CallOutcome::PossiblyUnboundDunderCall {
|
||||
called_ty: self,
|
||||
call_outcome,
|
||||
},
|
||||
|
||||
CallDunderOutcome::Call(outcome) => outcome,
|
||||
CallDunderOutcome::PossiblyUnbound(call_outcome) => {
|
||||
// Turn "possibly unbound object of type `Literal['__call__']`"
|
||||
// into "`X` not callable (possibly unbound `__call__` method)"
|
||||
CallOutcome::PossiblyUnboundDunderCall {
|
||||
called_ty: self,
|
||||
call_outcome: Box::new(call_outcome),
|
||||
}
|
||||
}
|
||||
|
||||
CallDunderOutcome::MethodNotAvailable => {
|
||||
// Turn "`X.__call__` unbound" into "`X` not callable"
|
||||
CallOutcome::NotCallable {
|
||||
@@ -2111,7 +2111,6 @@ impl<'db> Type<'db> {
|
||||
/// `receiver_ty` must be `Type::Instance(_)` or `Type::ClassLiteral`.
|
||||
///
|
||||
/// TODO: handle `super()` objects properly
|
||||
#[must_use]
|
||||
fn call_bound(
|
||||
self,
|
||||
db: &'db dyn Db,
|
||||
@@ -2163,7 +2162,10 @@ impl<'db> Type<'db> {
|
||||
CallDunderOutcome::Call(callable_ty.call(db, arguments))
|
||||
}
|
||||
Symbol::Type(callable_ty, Boundness::PossiblyUnbound) => {
|
||||
CallDunderOutcome::PossiblyUnbound(callable_ty.call(db, arguments))
|
||||
CallDunderOutcome::Call(CallOutcome::PossiblyUnboundDunderCall {
|
||||
call_outcome: Box::new(callable_ty.call(db, arguments)),
|
||||
called_ty: callable_ty,
|
||||
})
|
||||
}
|
||||
Symbol::Unbound => CallDunderOutcome::MethodNotAvailable,
|
||||
}
|
||||
@@ -2187,8 +2189,7 @@ impl<'db> Type<'db> {
|
||||
let dunder_iter_result =
|
||||
self.call_dunder(db, "__iter__", &CallArguments::positional([self]));
|
||||
match dunder_iter_result {
|
||||
CallDunderOutcome::Call(ref call_outcome)
|
||||
| CallDunderOutcome::PossiblyUnbound(ref call_outcome) => {
|
||||
CallDunderOutcome::Call(ref call_outcome) => {
|
||||
let Some(iterator_ty) = call_outcome.return_type(db) else {
|
||||
return IterationOutcome::NotIterable {
|
||||
not_iterable_ty: self,
|
||||
@@ -2199,7 +2200,7 @@ impl<'db> Type<'db> {
|
||||
.call_dunder(db, "__next__", &CallArguments::positional([iterator_ty]))
|
||||
.return_type(db)
|
||||
{
|
||||
if matches!(dunder_iter_result, CallDunderOutcome::PossiblyUnbound(..)) {
|
||||
if call_outcome.is_possibly_unbound() {
|
||||
IterationOutcome::PossiblyUnboundDunderIter {
|
||||
iterable_ty: self,
|
||||
element_ty,
|
||||
|
||||
@@ -10,6 +10,7 @@ mod bind;
|
||||
pub(super) use arguments::{Argument, CallArguments};
|
||||
pub(super) use bind::{bind_call, CallBinding};
|
||||
|
||||
#[must_use]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub(crate) enum CallOutcome<'db> {
|
||||
Callable {
|
||||
@@ -50,6 +51,10 @@ impl<'db> CallOutcome<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn is_possibly_unbound(&self) -> bool {
|
||||
matches!(self, Self::PossiblyUnboundDunderCall { .. })
|
||||
}
|
||||
|
||||
/// Get the return type of the call, or `None` if not callable.
|
||||
pub(super) fn return_type(&self, db: &'db dyn Db) -> Option<Type<'db>> {
|
||||
match self {
|
||||
@@ -215,24 +220,23 @@ impl<'db> CallOutcome<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[derive(Debug)]
|
||||
pub(super) enum CallDunderOutcome<'db> {
|
||||
Call(CallOutcome<'db>),
|
||||
PossiblyUnbound(CallOutcome<'db>),
|
||||
MethodNotAvailable,
|
||||
}
|
||||
|
||||
impl<'db> CallDunderOutcome<'db> {
|
||||
pub(super) fn call_outcome(&self) -> Option<&CallOutcome<'db>> {
|
||||
match self {
|
||||
Self::Call(outcome) | Self::PossiblyUnbound(outcome) => Some(outcome),
|
||||
Self::MethodNotAvailable => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn return_type(&self, db: &'db dyn Db) -> Option<Type<'db>> {
|
||||
match self {
|
||||
Self::Call(outcome) => outcome.return_type(db),
|
||||
Self::PossiblyUnbound { .. } => None,
|
||||
Self::Call(outcome) => {
|
||||
if outcome.is_possibly_unbound() {
|
||||
None
|
||||
} else {
|
||||
outcome.return_type(db)
|
||||
}
|
||||
}
|
||||
Self::MethodNotAvailable => None,
|
||||
}
|
||||
}
|
||||
@@ -293,6 +297,7 @@ impl<'db> NotCallableError<'db> {
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[derive(Debug)]
|
||||
pub(super) enum CallDunderLenOutcome<'db> {
|
||||
/// The length is statically known.
|
||||
@@ -301,9 +306,6 @@ pub(super) enum CallDunderLenOutcome<'db> {
|
||||
/// The length is determined by calling `__len__`.
|
||||
Call(CallOutcome<'db>),
|
||||
|
||||
/// The length is determined by calling `__len__` but it isn't always bound.
|
||||
PossiblyUnbound(CallOutcome<'db>),
|
||||
|
||||
/// The object doesn't have a `__len__` method and, thus, doesn't implement sized.
|
||||
MethodNotAvailable,
|
||||
}
|
||||
@@ -315,8 +317,7 @@ impl<'db> CallDunderLenOutcome<'db> {
|
||||
// TODO: Fall back to `int` if value is too large?
|
||||
i64::try_from(*len).ok().map(Type::IntLiteral)
|
||||
}
|
||||
CallDunderLenOutcome::Call(call_outcome)
|
||||
| CallDunderLenOutcome::PossiblyUnbound(call_outcome) => call_outcome.return_type(db),
|
||||
CallDunderLenOutcome::Call(call_outcome) => call_outcome.return_type(db),
|
||||
CallDunderLenOutcome::MethodNotAvailable => None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,9 @@ use crate::semantic_index::semantic_index;
|
||||
use crate::semantic_index::symbol::{NodeWithScopeKind, NodeWithScopeRef, ScopeId};
|
||||
use crate::semantic_index::SemanticIndex;
|
||||
use crate::stdlib::builtins_module_scope;
|
||||
use crate::types::call::{Argument, CallArguments, CallOutcome};
|
||||
use crate::types::call::{
|
||||
Argument, CallArguments, CallDunderLenOutcome, CallDunderOutcome, CallOutcome,
|
||||
};
|
||||
use crate::types::diagnostic::{
|
||||
report_invalid_arguments_to_annotated, report_invalid_assignment,
|
||||
report_invalid_attribute_assignment, report_unresolved_module, TypeCheckDiagnostics,
|
||||
@@ -3346,7 +3348,13 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
}
|
||||
|
||||
KnownFunction::Len => {
|
||||
// TODO: Assert that the argument implements the `Sized` protocol (correctly).
|
||||
let Some(sized) = binding.one_parameter_type() else {
|
||||
return call;
|
||||
};
|
||||
|
||||
if let CallDunderLenOutcome::Call(_) = sized.__len__(self.db()) {
|
||||
// TODO: Assert that the argument implements the `Sized` protocol (correctly).
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -3671,14 +3679,11 @@ impl<'db> TypeInferenceBuilder<'db> {
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(call) = operand_type
|
||||
.call_dunder(
|
||||
self.db(),
|
||||
unary_dunder_method,
|
||||
&CallArguments::positional([operand_type]),
|
||||
)
|
||||
.call_outcome()
|
||||
{
|
||||
if let CallDunderOutcome::Call(call) = operand_type.call_dunder(
|
||||
self.db(),
|
||||
unary_dunder_method,
|
||||
&CallArguments::positional([operand_type]),
|
||||
) {
|
||||
match call.return_type_result(&self.context, AnyNodeRef::ExprUnaryOp(unary)) {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
|
||||
Reference in New Issue
Block a user