File tree 2 files changed +45
-2
lines changed
crates/red_knot_python_semantic/src
2 files changed +45
-2
lines changed Original file line number Diff line number Diff line change @@ -609,8 +609,20 @@ impl<'db> Type<'db> {
609
609
} )
610
610
}
611
611
612
- // TODO: handle classes which implement the `__call__` protocol
613
- Type :: Instance ( _instance_ty) => CallOutcome :: callable ( Type :: Todo ) ,
612
+ Type :: Instance ( class) => {
613
+ // Since `__call__` is a dunder, we need to access it as an attribute on the class
614
+ // rather than the instance (matching runtime semantics).
615
+ let meta_ty = Type :: Class ( class) ;
616
+ let dunder_call_method = meta_ty. member ( db, "__call__" ) ;
617
+ if dunder_call_method. is_unbound ( ) {
618
+ CallOutcome :: not_callable ( self )
619
+ } else {
620
+ let args = std:: iter:: once ( self )
621
+ . chain ( arg_types. iter ( ) . copied ( ) )
622
+ . collect :: < Vec < _ > > ( ) ;
623
+ dunder_call_method. call ( db, & args)
624
+ }
625
+ }
614
626
615
627
// `Any` is callable, and its return type is also `Any`.
616
628
Type :: Any => CallOutcome :: callable ( Type :: Any ) ,
Original file line number Diff line number Diff line change @@ -6723,6 +6723,37 @@ mod tests {
6723
6723
Ok ( ( ) )
6724
6724
}
6725
6725
6726
+ #[ test]
6727
+ fn dunder_call ( ) -> anyhow:: Result < ( ) > {
6728
+ let mut db = setup_db ( ) ;
6729
+
6730
+ db. write_dedented (
6731
+ "/src/a.py" ,
6732
+ "
6733
+ class Multiplier:
6734
+ def __init__(self, factor: float):
6735
+ self.factor = factor
6736
+
6737
+ def __call__(self, number: float) -> float:
6738
+ return number * self.factor
6739
+
6740
+ a = Multiplier(2.0)(3.0)
6741
+
6742
+ class Unit:
6743
+ ...
6744
+
6745
+ b = Unit()(3.0)
6746
+ " ,
6747
+ ) ?;
6748
+
6749
+ assert_public_ty ( & db, "/src/a.py" , "a" , "float" ) ;
6750
+ assert_public_ty ( & db, "/src/a.py" , "b" , "Unknown" ) ;
6751
+
6752
+ assert_file_diagnostics ( & db, "src/a.py" , & [ "Object of type 'Unit' is not callable." ] ) ;
6753
+
6754
+ Ok ( ( ) )
6755
+ }
6756
+
6726
6757
#[ test]
6727
6758
fn boolean_or_expression ( ) -> anyhow:: Result < ( ) > {
6728
6759
let mut db = setup_db ( ) ;
You can’t perform that action at this time.
0 commit comments