diff --git a/pyrefly/lib/alt/overload.rs b/pyrefly/lib/alt/overload.rs index 637786bc6e..f1e21a21e3 100644 --- a/pyrefly/lib/alt/overload.rs +++ b/pyrefly/lib/alt/overload.rs @@ -647,6 +647,7 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> { let mut arg_counts = closest_overload_signature.arg_counts(); let nposargs = args.len(); let nkwargs = keywords.len(); + let missing_self_param = self_obj.is_some() && arg_counts.positional.max == Some(0); if self_obj.is_some() { arg_counts.positional.min = arg_counts.positional.min.saturating_sub(1); if let Some(max) = arg_counts.positional.max { @@ -675,13 +676,19 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> { None } }; - let arity_mismatch = + let arity_mismatch = if missing_self_param { + format!( + "Expected 0 positional arguments, got {} (including implicit `self`)", + nposargs + 1 + ) + } else { check(nposargs + nkwargs, arg_counts.overall, "").unwrap_or_else(|| { check(nposargs, arg_counts.positional, "positional ").unwrap_or_else(|| { check(nkwargs, arg_counts.keyword, "keyword ") .expect("Overload evaluation: expected arity mismatch not found") }) - }); + }) + }; builder = builder.with_detail(arity_mismatch); } else { builder = builder.with_errors_as_details(closest_overload_call_errors); diff --git a/pyrefly/lib/test/constructors.rs b/pyrefly/lib/test/constructors.rs index 7502f9f92f..8074e1cc8a 100644 --- a/pyrefly/lib/test/constructors.rs +++ b/pyrefly/lib/test/constructors.rs @@ -703,6 +703,22 @@ assert_type(C(False), C[B]) "#, ); +// Regression test for https://github.com/facebook/pyrefly/issues/4192 +testcase!( + test_overloaded_init_missing_self, + r#" +from typing import overload + +class C: + @overload + def __init__(): ... # E: Overloaded function must have an implementation + @overload + def __init__(): ... + +C() # E: Expected 0 positional arguments, got 1 (including implicit `self`) + "#, +); + testcase!( test_init_bad_receiver_annotation, r#"