Compare commits

...

2 Commits

Author SHA1 Message Date
Micha Reiser
df9847cce4 Rename CallDunderResult to CallDunderOutcome 2025-02-13 10:05:53 +01:00
Micha Reiser
49302911c4 [red-knot] Remove CallDunderResult::PossiblyUnbound variant 2025-02-13 10:01:41 +01:00
3 changed files with 47 additions and 34 deletions

View File

@@ -36,7 +36,7 @@ use crate::stdlib::{builtins_symbol, known_module_symbol, typing_extensions_symb
use crate::suppression::check_suppressions;
use crate::symbol::{Boundness, Symbol};
use crate::types::call::{
bind_call, CallArguments, CallBinding, CallDunderResult, CallOutcome, StaticAssertionErrorKind,
bind_call, CallArguments, CallBinding, CallDunderOutcome, CallOutcome, StaticAssertionErrorKind,
};
use crate::types::class_base::ClassBase;
use crate::types::diagnostic::INVALID_TYPE_FORM;
@@ -1927,11 +1927,9 @@ impl<'db> Type<'db> {
let return_ty = match self.call_dunder(db, "__len__", &CallArguments::positional([*self])) {
// TODO: emit a diagnostic
CallDunderResult::MethodNotAvailable => return None,
CallDunderOutcome::MethodNotAvailable => return None,
CallDunderResult::CallOutcome(outcome) | CallDunderResult::PossiblyUnbound(outcome) => {
outcome.return_type(db)?
}
CallDunderOutcome::Call(outcome) => outcome.return_type(db)?,
};
non_negative_int_literal(db, return_ty)
@@ -2101,23 +2099,26 @@ impl<'db> Type<'db> {
instance_ty @ Type::Instance(_) => {
match instance_ty.call_dunder(db, "__call__", &arguments.with_self(instance_ty)) {
CallDunderResult::CallOutcome(CallOutcome::NotCallable { .. }) => {
CallDunderOutcome::Call(CallOutcome::NotCallable { .. }) => {
// Turn "`<type of illegal '__call__'>` not callable" into
// "`X` not callable"
CallOutcome::NotCallable {
not_callable_ty: self,
}
}
CallDunderResult::CallOutcome(outcome) => outcome,
CallDunderResult::PossiblyUnbound(call_outcome) => {
CallDunderOutcome::Call(outcome) => match 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),
CallOutcome::PossiblyUnboundDunder { call_outcome, .. } => {
CallOutcome::PossiblyUnboundDunder {
called_ty: self,
call_outcome,
}
}
}
CallDunderResult::MethodNotAvailable => {
outcome => outcome,
},
CallDunderOutcome::MethodNotAvailable => {
// Turn "`X.__call__` unbound" into "`X` not callable"
CallOutcome::NotCallable {
not_callable_ty: self,
@@ -2197,15 +2198,20 @@ impl<'db> Type<'db> {
db: &'db dyn Db,
name: &str,
arguments: &CallArguments<'_, 'db>,
) -> CallDunderResult<'db> {
) -> CallDunderOutcome<'db> {
match self.to_meta_type(db).member(db, name) {
Symbol::Type(callable_ty, Boundness::Bound) => {
CallDunderResult::CallOutcome(callable_ty.call(db, arguments))
CallDunderOutcome::Call(callable_ty.call(db, arguments))
}
Symbol::Type(callable_ty, Boundness::PossiblyUnbound) => {
CallDunderResult::PossiblyUnbound(callable_ty.call(db, arguments))
CallDunderOutcome::Call(CallOutcome::PossiblyUnboundDunder {
called_ty: self,
call_outcome: Box::new(callable_ty.call(db, arguments)),
})
}
Symbol::Unbound => CallDunderResult::MethodNotAvailable,
Symbol::Unbound => CallDunderOutcome::MethodNotAvailable,
}
}
@@ -2227,8 +2233,7 @@ impl<'db> Type<'db> {
let dunder_iter_result =
self.call_dunder(db, "__iter__", &CallArguments::positional([self]));
match dunder_iter_result {
CallDunderResult::CallOutcome(ref call_outcome)
| CallDunderResult::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,
@@ -2239,7 +2244,7 @@ impl<'db> Type<'db> {
.call_dunder(db, "__next__", &CallArguments::positional([iterator_ty]))
.return_type(db)
{
if matches!(dunder_iter_result, CallDunderResult::PossiblyUnbound(..)) {
if call_outcome.is_possibly_unbound_dunder() {
IterationOutcome::PossiblyUnboundDunderIter {
iterable_ty: self,
element_ty,
@@ -2253,7 +2258,7 @@ impl<'db> Type<'db> {
}
};
}
CallDunderResult::MethodNotAvailable => {}
CallDunderOutcome::MethodNotAvailable => {}
}
// Although it's not considered great practice,
@@ -4068,7 +4073,7 @@ impl<'db> Class<'db> {
}
}
CallOutcome::PossiblyUnboundDunderCall { called_ty, .. } => Err(MetaclassError {
CallOutcome::PossiblyUnboundDunder { called_ty, .. } => Err(MetaclassError {
kind: MetaclassErrorKind::PartlyNotCallable(called_ty),
}),

View File

@@ -36,7 +36,7 @@ pub(super) enum CallOutcome<'db> {
called_ty: Type<'db>,
outcomes: Box<[CallOutcome<'db>]>,
},
PossiblyUnboundDunderCall {
PossiblyUnboundDunder {
called_ty: Type<'db>,
call_outcome: Box<CallOutcome<'db>>,
},
@@ -88,6 +88,10 @@ impl<'db> CallOutcome<'db> {
}
}
pub(super) const fn is_possibly_unbound_dunder(&self) -> bool {
matches!(self, Self::PossiblyUnboundDunder { .. })
}
/// 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 {
@@ -113,7 +117,7 @@ impl<'db> CallOutcome<'db> {
}
})
.map(UnionBuilder::build),
Self::PossiblyUnboundDunderCall { call_outcome, .. } => call_outcome.return_type(db),
Self::PossiblyUnboundDunder { call_outcome, .. } => call_outcome.return_type(db),
Self::StaticAssertionError { .. } => Some(Type::none(db)),
Self::AssertType {
binding,
@@ -224,7 +228,7 @@ impl<'db> CallOutcome<'db> {
not_callable_ty: *not_callable_ty,
return_ty: Type::unknown(),
}),
Self::PossiblyUnboundDunderCall {
Self::PossiblyUnboundDunder {
called_ty,
call_outcome,
} => Err(NotCallableError::PossiblyUnboundDunderCall {
@@ -351,17 +355,22 @@ impl<'db> CallOutcome<'db> {
}
}
pub(super) enum CallDunderResult<'db> {
CallOutcome(CallOutcome<'db>),
PossiblyUnbound(CallOutcome<'db>),
pub(super) enum CallDunderOutcome<'db> {
Call(CallOutcome<'db>),
MethodNotAvailable,
}
impl<'db> CallDunderResult<'db> {
impl<'db> CallDunderOutcome<'db> {
pub(super) fn return_type(&self, db: &'db dyn Db) -> Option<Type<'db>> {
match self {
Self::CallOutcome(outcome) => outcome.return_type(db),
Self::PossiblyUnbound { .. } => None,
Self::Call(outcome) => {
// TODO: Always call `outcome.return_type`, see https://github.com/astral-sh/ruff/issues/16123
if outcome.is_possibly_unbound_dunder() {
None
} else {
outcome.return_type(db)
}
}
Self::MethodNotAvailable => None,
}
}

View File

@@ -64,7 +64,7 @@ use crate::types::mro::MroErrorKind;
use crate::types::unpacker::{UnpackResult, Unpacker};
use crate::types::{
builtins_symbol, global_symbol, symbol, symbol_from_bindings, symbol_from_declarations,
todo_type, typing_extensions_symbol, Boundness, CallDunderResult, Class, ClassLiteralType,
todo_type, typing_extensions_symbol, Boundness, CallDunderOutcome, Class, ClassLiteralType,
DynamicType, FunctionType, InstanceType, IntersectionBuilder, IntersectionType,
IterationOutcome, KnownClass, KnownFunction, KnownInstanceType, MetaclassCandidate,
MetaclassErrorKind, SliceLiteralType, SubclassOfType, Symbol, SymbolAndQualifiers, Truthiness,
@@ -3567,8 +3567,7 @@ impl<'db> TypeInferenceBuilder<'db> {
}
};
if let CallDunderResult::CallOutcome(call)
| CallDunderResult::PossiblyUnbound(call) = operand_type.call_dunder(
if let CallDunderOutcome::Call(call) = operand_type.call_dunder(
self.db(),
unary_dunder_method,
&CallArguments::positional([operand_type]),