From 2d3f5a65f8de61adf0bb8801ce8fb862c9f27dc6 Mon Sep 17 00:00:00 2001 From: aadi-novice Date: Wed, 27 May 2026 12:15:35 +0530 Subject: [PATCH] fix(lsp): display ** prefix for ParamSpec in hover type parameter lists When hovering over a generic function that uses ParamSpec (e.g., **P), the type parameter list in the hover was showing [T, P, R] instead of [T, **P, R]. The fix adds a check in Quantified::display_with_bounds() to prepend ** for ParamSpec type parameters in the type parameter list display. Fixes #3568 --- crates/pyrefly_types/src/display.rs | 28 ++++++++++++++++++++++++++ crates/pyrefly_types/src/quantified.rs | 3 +++ 2 files changed, 31 insertions(+) diff --git a/crates/pyrefly_types/src/display.rs b/crates/pyrefly_types/src/display.rs index 812203426e..e9385b064e 100644 --- a/crates/pyrefly_types/src/display.rs +++ b/crates/pyrefly_types/src/display.rs @@ -2103,6 +2103,34 @@ pub mod tests { ); } +n #[test] + fn test_display_param_spec() { + let tparams = fake_tparams(vec![ + fake_tparam(0, "T", QuantifiedKind::TypeVar), + fake_tparam(1, "P", QuantifiedKind::ParamSpec), + fake_tparam(2, "R", QuantifiedKind::TypeVar), + ]); + let method = fake_generic_bound_method( + "foo", + "MyClass", + "my.module", + tparams, + ); + let mut ctx = TypeDisplayContext::new(&[&method]); + assert_eq!( + ctx.display(&method).to_string(), + "[T, **P, R](self: Any, x: Any, y: Any) -> None" + ); + ctx.set_lsp_display_mode(LspDisplayMode::Hover); + assert_eq!( + ctx.display(&method).to_string(), + r#"def foo[T, **P, R]( + self: Any, + x: Any, + y: Any +) -> None: ..."# + ); + } #[test] fn test_display_overload() { let class = fake_class("TestClass", "test", 0); diff --git a/crates/pyrefly_types/src/quantified.rs b/crates/pyrefly_types/src/quantified.rs index 8349d667b9..8df14b9300 100644 --- a/crates/pyrefly_types/src/quantified.rs +++ b/crates/pyrefly_types/src/quantified.rs @@ -368,6 +368,9 @@ impl Quantified { /// in the format used for type parameter lists (e.g. `T: int = str`). pub fn display_with_bounds(&self) -> impl Display + '_ { Fmt(move |f| { + if self.is_param_spec() { + write!(f, "**")?; + } write!(f, "{}", self.name)?; match self.restriction() { Restriction::Bound(t) => write!(f, ": {}", t)?,