It is hard to explain exactly what I mean by contextual go-to-implementation, so take the following example code in Rust:
struct A {}
struct B {}
impl From<A> for B {
fn from(a: A) -> Self {
B{}
}
}
fn fun() -> B {
let a = A{};
a.into()
}
It seems useful to me to be able to place the cursor on the call to into()
in the last line of fun
and expect to be able to easily go to the definition of from()
in From<A> for B
(expecting to see how a
(of type A
) becomes something of type B
).
What really happens is go-to-implementation request takes me to the generic implementation of into in the standard library:
// From implies Into
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_convert", issue = "88674")]
impl<T, U> const Into<U> for T
where
U: ~const From<T>,
{
/// Calls `U::from(self)`.
///
/// That is, this conversion is whatever the implementation of
/// <code>[From]<T> for U</code> chooses to do.
fn into(self) -> U { // <- I'm taken here
U::from(self)
}
}
And this is correct. However, the context is lost and there is no way to now follow from the line below to the actual destination back in the source code, because there are many implementations of From
. The implementation of LSP could know exactly, that in the context of the recent jump, T
= A
and U
= B
, so:
- the editor could temporarily show this context to the user as inline hints (until the context is reset),
- on the next go-to-implementation request, the context could be used to know that there is exactly one implementation of
From
for given context (T
andU
) and jump specifically to the line:fn from(a: A) -> Self {
.
Would an implementation of this feature require changes in the protocol definition itself? Are there any features already in the protocol for handling generic code that could point me towards the correct approach?