goto where trait method impl

This commit is contained in:
bitgaoshu 2022-06-15 23:13:15 +08:00 committed by Florian Diebold
parent 6fc5c3cd21
commit 1ef5e14c2c
5 changed files with 366 additions and 117 deletions

View file

@ -1,7 +1,7 @@
use std::convert::TryInto;
use crate::{doc_links::token_as_doc_comment, FilePosition, NavigationTarget, RangeInfo, TryToNav};
use hir::{AsAssocItem, Semantics};
use hir::{AsAssocItem, AssocItem, Semantics};
use ide_db::{
base_db::{AnchoredPath, FileId, FileLoader},
defs::{Definition, IdentClass},
@ -65,7 +65,7 @@ pub(crate) fn goto_definition(
.definitions()
.into_iter()
.flat_map(|def| {
try_find_trait_item_definition(sema.db, &def)
try_filter_trait_item_definition(sema, &def, &token)
.unwrap_or_else(|| def_to_nav(sema.db, def))
})
.collect(),
@ -104,32 +104,38 @@ fn try_lookup_include_path(
docs: None,
})
}
/// finds the trait definition of an impl'd item
/// finds the trait definition of an impl'd item, except function
/// e.g.
/// ```rust
/// trait A { fn a(); }
/// trait A { type a; }
/// struct S;
/// impl A for S { fn a(); } // <-- on this function, will get the location of a() in the trait
/// impl A for S { type a = i32; } // <-- on this associate type, will get the location of a in the trait
/// ```
fn try_find_trait_item_definition(
db: &RootDatabase,
fn try_filter_trait_item_definition(
sema: &Semantics<RootDatabase>,
def: &Definition,
token: &SyntaxToken,
) -> Option<Vec<NavigationTarget>> {
let name = def.name(db)?;
let db = sema.db;
let assoc = def.as_assoc_item(db)?;
let imp = match assoc.container(db) {
hir::AssocItemContainer::Impl(imp) => imp,
_ => return None,
};
let trait_ = imp.trait_(db)?;
trait_
.items(db)
.iter()
.find_map(|itm| (itm.name(db)? == name).then(|| itm.try_to_nav(db)).flatten())
.map(|it| vec![it])
match assoc {
AssocItem::Function(..) => {
IdentClass::classify_token_to_impl(sema, &token).map(|def| def_to_nav(db, def))
}
AssocItem::Const(..) | AssocItem::TypeAlias(..) => {
let imp = match assoc.container(db) {
hir::AssocItemContainer::Impl(imp) => imp,
_ => return None,
};
let trait_ = imp.trait_(db)?;
let name = def.name(db)?;
trait_
.items(db)
.iter()
.find_map(|itm| (itm.name(db)? == name).then(|| itm.try_to_nav(db)).flatten())
.map(|it| vec![it])
}
}
}
fn def_to_nav(db: &RootDatabase, def: Definition) -> Vec<NavigationTarget> {
@ -1331,23 +1337,98 @@ fn main() {
"#,
);
}
#[test]
fn goto_def_of_trait_impl_fn() {
check(
r#"
#[cfg(test)]
mod goto_impl_of_trait_fn {
use super::check;
#[test]
fn cursor_on_impl() {
check(
r#"
trait Twait {
fn a();
// ^
}
struct Stwuct;
impl Twait for Stwuct {
fn a$0();
//^
}
"#,
);
"#,
);
}
#[test]
fn method_call() {
check(
r#"
trait Twait {
fn a(&self);
}
struct Stwuct;
impl Twait for Stwuct {
fn a(&self){};
//^
}
fn f() {
let s = Stwuct;
s.a$0();
}
"#,
);
}
#[test]
fn path_call() {
check(
r#"
trait Twait {
fn a(&self);
}
struct Stwuct;
impl Twait for Stwuct {
fn a(&self){};
//^
}
fn f() {
let s = Stwuct;
Stwuct::a$0(&s);
}
"#,
);
}
#[test]
fn where_clause_can_work() {
check(
r#"
trait G {
fn g(&self);
}
trait Bound{}
trait EA{}
struct Gen<T>(T);
impl <T:EA> G for Gen<T> {
fn g(&self) {
}
}
impl <T> G for Gen<T>
where T : Bound
{
fn g(&self){
//^
}
}
struct A;
impl Bound for A{}
fn f() {
let gen = Gen::<A>(A);
gen.g$0();
}
"#,
);
}
}
#[test]