From 0a0d0ff3178ed8b5e044d8e48ed02c38b13775c9 Mon Sep 17 00:00:00 2001 From: Asuka Minato Date: Sun, 22 Mar 2026 08:35:41 +0900 Subject: [PATCH] fix --- pyrefly/lib/alt/call.rs | 11 ++++++++--- pyrefly/lib/test/lsp/hover.rs | 23 +++++++++++++++++++++++ pyrefly/lib/test/lsp/signature_help.rs | 25 +++++++++++++++++++++++++ 3 files changed, 56 insertions(+), 3 deletions(-) diff --git a/pyrefly/lib/alt/call.rs b/pyrefly/lib/alt/call.rs index e151345266..0554188ff6 100644 --- a/pyrefly/lib/alt/call.rs +++ b/pyrefly/lib/alt/call.rs @@ -698,10 +698,11 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> { }; let hint = None; // discard hint let class_metadata = self.get_metadata_for_class(cls.class_object()); + let metaclass_dunder_call = self.get_metaclass_dunder_call(&cls); if let Some(ret) = self.call_metaclass(&cls, arguments_range, args, keywords, errors, context, hint) { - if let Some(metaclass_dunder_call) = self.get_metaclass_dunder_call(&cls) { + if let Some(metaclass_dunder_call) = metaclass_dunder_call.clone() { if let Some(callee_range) = callee_range && let Some(metaclass) = class_metadata.custom_metaclass() { @@ -774,7 +775,9 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> { callee_range, ); } - self.record_resolved_trace(arguments_range, new_method); + if metaclass_dunder_call.is_none() { + self.record_resolved_trace(arguments_range, new_method); + } if self.is_compatible_constructor_return(&ret, cls.class_object()) { dunder_new_ret = Some(ret); } else if !matches!(ret, Type::Any(AnyStyle::Error | AnyStyle::Implicit)) { @@ -829,7 +832,9 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> { callee_range, ); } - self.record_resolved_trace(arguments_range, init_method); + if !overrides_new && metaclass_dunder_call.is_none() { + self.record_resolved_trace(arguments_range, init_method); + } } if class_metadata.is_pydantic_model() && let Some(dataclass) = class_metadata.dataclass_metadata() diff --git a/pyrefly/lib/test/lsp/hover.rs b/pyrefly/lib/test/lsp/hover.rs index de292910b8..81a24a6dbe 100644 --- a/pyrefly/lib/test/lsp/hover.rs +++ b/pyrefly/lib/test/lsp/hover.rs @@ -1071,6 +1071,29 @@ Person("Alice", 25) ); } +#[test] +fn hover_on_namedtuple_constructor_shows_field_signature() { + let code = r#" +from typing import NamedTuple + +class Test(NamedTuple): + a: str + b: int + +Test() +#^ +"#; + let report = get_batched_lsp_operations_report_allow_error(&[("main", code)], get_test_report); + assert!( + report.contains("a: str") && report.contains("b: int") && report.contains("-> Test"), + "Expected NamedTuple constructor hover to show field parameters, got: {report}" + ); + assert!( + !report.contains("*Unknown") && !report.contains("**Unknown"), + "NamedTuple constructor hover should not fall back to variadic Unknown params, got: {report}" + ); +} + #[test] fn hover_over_in_keyword_in_for_loop() { let code = r#" diff --git a/pyrefly/lib/test/lsp/signature_help.rs b/pyrefly/lib/test/lsp/signature_help.rs index dabfc9c39b..9efa1fbfa2 100644 --- a/pyrefly/lib/test/lsp/signature_help.rs +++ b/pyrefly/lib/test/lsp/signature_help.rs @@ -934,6 +934,31 @@ Signature Help Result: active=0 ); } +#[test] +fn namedtuple_constructor_signature_shows_namedtuple_fields() { + let code = r#" +from typing import NamedTuple + +class Test(NamedTuple): + a: str + b: int + +Test() +# ^ +Test(a="", ) +# ^ +"#; + let report = get_batched_lsp_operations_report_allow_error(&[("main", code)], get_test_report); + assert!( + report.contains("a: str") && report.contains("b: int") && report.contains("-> Test"), + "Expected NamedTuple signature help to show field parameters, got: {report}" + ); + assert!( + !report.contains("*Unknown") && !report.contains("**Unknown"), + "NamedTuple signature help should not fall back to variadic Unknown params, got: {report}" + ); +} + #[test] fn direct_init_call_shows_none() { let code = r#"