Auto merge of #13671 - Veykril:goto-decl, r=Veykril

Improve goto declaration

Closes https://github.com/rust-lang/rust-analyzer/issues/13599

- goto decl now goes to assoc items of trait declarations over the items of trait implementations
- goto decl now goes to the field declaration (opposed to goto def which shows both the field decl and binding created/local being used)
- also adds back the goto definition fallback that seems to have been dropped at some point.
This commit is contained in:
bors 2022-11-25 09:28:39 +00:00
commit 99daf23e11

View File

@ -1,18 +1,22 @@
use hir::Semantics; use hir::{AsAssocItem, Semantics};
use ide_db::{ use ide_db::{
defs::{Definition, NameClass, NameRefClass}, defs::{Definition, NameClass, NameRefClass},
RootDatabase, RootDatabase,
}; };
use syntax::{ast, match_ast, AstNode, SyntaxKind::*, T}; use syntax::{ast, match_ast, AstNode, SyntaxKind::*, T};
use crate::{FilePosition, NavigationTarget, RangeInfo}; use crate::{
goto_definition::goto_definition, navigation_target::TryToNav, FilePosition, NavigationTarget,
RangeInfo,
};
// Feature: Go to Declaration // Feature: Go to Declaration
// //
// Navigates to the declaration of an identifier. // Navigates to the declaration of an identifier.
// //
// This is currently the same as `Go to Definition` with the exception of outline modules where it // This is the same as `Go to Definition` with the following exceptions:
// will navigate to the `mod name;` item declaration. // - outline modules will navigate to the `mod name;` item declaration
// - trait assoc items will navigate to the assoc item of the trait declaration opposed to the trait impl
pub(crate) fn goto_declaration( pub(crate) fn goto_declaration(
db: &RootDatabase, db: &RootDatabase,
position: FilePosition, position: FilePosition,
@ -32,26 +36,38 @@ pub(crate) fn goto_declaration(
match parent { match parent {
ast::NameRef(name_ref) => match NameRefClass::classify(&sema, &name_ref)? { ast::NameRef(name_ref) => match NameRefClass::classify(&sema, &name_ref)? {
NameRefClass::Definition(it) => Some(it), NameRefClass::Definition(it) => Some(it),
_ => None NameRefClass::FieldShorthand { field_ref, .. } => return field_ref.try_to_nav(db),
}, },
ast::Name(name) => match NameClass::classify(&sema, &name)? { ast::Name(name) => match NameClass::classify(&sema, &name)? {
NameClass::Definition(it) => Some(it), NameClass::Definition(it) | NameClass::ConstReference(it) => Some(it),
_ => None NameClass::PatFieldShorthand { field_ref, .. } => return field_ref.try_to_nav(db),
}, },
_ => None _ => None
} }
}; };
match def? { let assoc = match def? {
Definition::Module(module) => { Definition::Module(module) => {
Some(NavigationTarget::from_module_to_decl(db, module)) return Some(NavigationTarget::from_module_to_decl(db, module))
} }
Definition::Const(c) => c.as_assoc_item(db),
Definition::TypeAlias(ta) => ta.as_assoc_item(db),
Definition::Function(f) => f.as_assoc_item(db),
_ => None, _ => None,
} }?;
let trait_ = assoc.containing_trait_impl(db)?;
let name = Some(assoc.name(db)?);
let item = trait_.items(db).into_iter().find(|it| it.name(db) == name)?;
item.try_to_nav(db)
}) })
.collect(); .collect();
if info.is_empty() {
goto_definition(db, position)
} else {
Some(RangeInfo::new(range, info)) Some(RangeInfo::new(range, info))
} }
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
@ -109,4 +125,89 @@ mod foo {
"#, "#,
) )
} }
#[test]
fn goto_decl_goto_def_fallback() {
check(
r#"
struct Foo;
// ^^^
impl Foo$0 {}
"#,
);
}
#[test]
fn goto_decl_assoc_item_no_impl_item() {
check(
r#"
trait Trait {
const C: () = ();
// ^
}
impl Trait for () {}
fn main() {
<()>::C$0;
}
"#,
);
}
#[test]
fn goto_decl_assoc_item() {
check(
r#"
trait Trait {
const C: () = ();
// ^
}
impl Trait for () {
const C: () = ();
}
fn main() {
<()>::C$0;
}
"#,
);
check(
r#"
trait Trait {
const C: () = ();
// ^
}
impl Trait for () {
const C$0: () = ();
}
"#,
);
}
#[test]
fn goto_decl_field_pat_shorthand() {
check(
r#"
struct Foo { field: u32 }
//^^^^^
fn main() {
let Foo { field$0 };
}
"#,
);
}
#[test]
fn goto_decl_constructor_shorthand() {
check(
r#"
struct Foo { field: u32 }
//^^^^^
fn main() {
let field = 0;
Foo { field$0 };
}
"#,
);
}
} }