diff --git a/pyrefly/lib/alt/call.rs b/pyrefly/lib/alt/call.rs index 4fde9f203..c65577a13 100644 --- a/pyrefly/lib/alt/call.rs +++ b/pyrefly/lib/alt/call.rs @@ -32,6 +32,7 @@ use crate::types::callable::FunctionKind; use crate::types::callable::Params; use crate::types::class::ClassType; use crate::types::literal::Lit; +use crate::types::tuple::Tuple; use crate::types::type_var::Restriction; use crate::types::typed_dict::TypedDict; use crate::types::types::AnyStyle; @@ -182,6 +183,28 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> { Type::Type(box Type::ClassType(cls)) | Type::Type(box Type::SelfType(cls)) => { Some(CallTarget::new(Target::Class(cls))) } + Type::Type(box Type::Tuple(Tuple::Unbounded(element))) => { + Some(CallTarget::new(Target::Class(self.stdlib.tuple(*element)))) + } + Type::Type(box Type::Tuple(Tuple::Concrete(elements))) => { + Some(CallTarget::new(Target::Class(if elements.is_empty() { + self.stdlib.tuple(Type::Any(AnyStyle::Implicit)) + } else { + self.stdlib.tuple(self.unions(elements)) + }))) + } + Type::Type(box Type::Tuple(Tuple::Unpacked(box ( + prefix, + Type::Tuple(Tuple::Unbounded(middle)), + suffix, + )))) => { + let mut elements = prefix; + elements.push(*middle); + elements.extend(suffix); + Some(CallTarget::new(Target::Class( + self.stdlib.tuple(self.unions(elements)), + ))) + } Type::Type(box Type::Quantified(quantified)) => { Some(CallTarget::new(Target::Callable(Callable { // TODO: use upper bound to determine input parameters diff --git a/pyrefly/lib/test/tuple.rs b/pyrefly/lib/test/tuple.rs index ac9bd2e40..9bc7ba679 100644 --- a/pyrefly/lib/test/tuple.rs +++ b/pyrefly/lib/test/tuple.rs @@ -360,3 +360,13 @@ def g(x): assert_type(x, tuple) "#, ); + +testcase!( + test_tuple_class_type, + r#" +from typing import Any + +def f(x: type[tuple[Any, ...]]): + return x() # Ok + "#, +);