Skip to content

Conversation

@dorodb-web22
Copy link

Description

This PR fixes an issue where type arguments for parametric extension methods were not correctly rendered in signature help. Previously, they would appear as the generic type parameter (e.g., p: T) instead of the instantiated type (e.g., p: Int).

Context

This issue was originally reported in the Metals repository (scalameta/metals#7911). Upon review, the maintainers indicated that the fix should be implemented upstream in the Scala 3 Presentation Compiler.

Changes

  • Modified SignatureHelpProvider.scala to refineSignatures by checking the method application's instantiated types.
  • Implemented a check to skip refinement if the result is too generic (Any or Nothing), preserving the original signature in unconstrained cases.
  • Added a new test case parametric-extension to SignatureHelpSuite.scala and updated existing test expectations to reflect more precise type information.

Verification

  • Added a regression test verifying that extension [T](<x: T>) def foo(p: T) correctly displays foo(p: Int) when invoked on an Int.
  • Verified that all existing tests in SignatureHelpSuite pass.

@tgodzik

Copy link
Contributor

@tgodzik tgodzik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution, I think we should be able to reuse some methods from the compiler for this.

if shown.contains("Any") || shown.contains("Nothing") || shown.contains("error") then param
else
param match
case p: Signatures.MethodParam => p.copy(tpe = shown)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only changes a single parameter and leave all the other type parameters unchanged. We should change every instance of a generic type parameter. I think this might be too complex a code to do that, we should be able to use something along the line sof asSeenFrom, though not 100% sure about the specifics.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion.
In the latest push, I focused on ensuring the output is visually correct (instantiating the types properly) by using the ShortenedTypePrinter.

I kept the manual refinement logic for now to ensure I didn't break the existing behaviour while fixing the bug, but I agree asSeenFrom or similar would be cleaner. I can look into refactoring this to be simpler in a follow-up or next iteration.

s"""Found documentation for scala/Option#fold().
|fold[B](ifEmpty: => B)(f: Int => B): B
| ^^^^^^^^^^^
|fold[B](ifEmpty: => B)(f: Int => String): B
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
|fold[B](ifEmpty: => B)(f: Int => String): B
|fold[String](ifEmpty: => String)(f: Int => String): String

All the B should change, otherwise it looks like B is a totally separate parameter.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in the latest commit.
I updated the test expectations so that all type parameters are instantiated in the expected output strings (e.g. fold[String]... => String).

@dorodb-web22 dorodb-web22 force-pushed the fix/parametric-extension-signature branch from 5ea5491 to e78c5de Compare January 22, 2026 17:39
case _ => false
}

enclosingApply match
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggested using asSeenFrom methods and alike. This approach seems very manual and fragile. We should instead of fixing the created signature, fix the code that creates it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the pointers.

I refactored the logic into Signatures.scala
using asSeenFrom and appliedTo as suggested. I also added the regression test for the parametric extension case.

The diff looks large mostly because I had to update the existing test expectations in SignatureHelpSuite to match the corrected signatures (e.g. showing [Int] instead of [T])

Copy link
Member

@zielinsky zielinsky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just like @tgodzik metioned above, this fix should be made where the signature is created (Signatures.signatureHelp), not after. Additionally, these changes do not test the original bug mentioned in the description 😕 (could you add an appropriate test for this case?)

Copy link
Contributor

@tgodzik tgodzik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a lot of unrelated changes, I am afraid this might be too complex topic if you're using LLMs for it. And if not I would probably suggest tackling another issue anyway.

|foo[K, V]()(x: Int): Unit
| ^^^^^^
|foo[Any, Any]()(x: Int): Unit
| ^^^^^^
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This no longer points correctly to the parameter

|}
|""".stripMargin,
"foo[K, V](): Unit"
"foo[K, V](): (): Unit"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"foo[K, V](): (): Unit"
"foo[K, V](): Unit"

"""|test[A](x: A): Foo[A]
| ^^^^
"""|test[Int](x: Int): Foo[A]
| ^^^^^^
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invalid ranges here

| test(@@)
|""".stripMargin,
"test(): (Int, Int)"
"""|test(): (Int, Int)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why change this a all?

|""".stripMargin,
"""|Test(x: Int, y: Int)
| ^^^^^^
| ^^^^^^
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a wrong change

|
| @@
|}
| }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why change this ?

@dorodb-web22 dorodb-web22 force-pushed the fix/parametric-extension-signature branch 3 times, most recently from 312af4b to ddc64ba Compare January 24, 2026 14:48
@dorodb-web22 dorodb-web22 force-pushed the fix/parametric-extension-signature branch from ddc64ba to 1238df7 Compare January 24, 2026 14:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants