]> git.lizzy.rs Git - rust.git/blob - crates/ide/src/goto_declaration.rs
Explicitly check for reference locals or fields in Name classification
[rust.git] / crates / ide / src / goto_declaration.rs
1 use hir::Semantics;
2 use ide_db::{
3     defs::{Definition, NameClass, NameRefClass},
4     RootDatabase,
5 };
6 use syntax::{ast, match_ast, AstNode, SyntaxKind::*, T};
7
8 use crate::{FilePosition, NavigationTarget, RangeInfo};
9
10 // Feature: Go to Declaration
11 //
12 // Navigates to the declaration of an identifier.
13 pub(crate) fn goto_declaration(
14     db: &RootDatabase,
15     position: FilePosition,
16 ) -> Option<RangeInfo<Vec<NavigationTarget>>> {
17     let sema = Semantics::new(db);
18     let file = sema.parse(position.file_id).syntax().clone();
19     let original_token = file
20         .token_at_offset(position.offset)
21         .find(|it| matches!(it.kind(), IDENT | T![self] | T![super] | T![crate]))?;
22     let token = sema.descend_into_macros(original_token.clone());
23     let parent = token.parent()?;
24     let def = match_ast! {
25         match parent {
26             ast::NameRef(name_ref) => {
27                 let name_kind = NameRefClass::classify(&sema, &name_ref)?;
28                 name_kind.referenced_local()
29             },
30             ast::Name(name) => {
31                 NameClass::classify(&sema, &name)?.defined_or_referenced_local()
32             },
33             _ => return None,
34         }
35     };
36     match def {
37         Definition::ModuleDef(hir::ModuleDef::Module(module)) => Some(RangeInfo::new(
38             original_token.text_range(),
39             vec![NavigationTarget::from_module_to_decl(db, module)],
40         )),
41         _ => return None,
42     }
43 }
44
45 #[cfg(test)]
46 mod tests {
47     use ide_db::base_db::FileRange;
48
49     use crate::fixture;
50
51     fn check(ra_fixture: &str) {
52         let (analysis, position, expected) = fixture::nav_target_annotation(ra_fixture);
53         let mut navs = analysis
54             .goto_declaration(position)
55             .unwrap()
56             .expect("no declaration or definition found")
57             .info;
58         if navs.len() == 0 {
59             panic!("unresolved reference")
60         }
61         assert_eq!(navs.len(), 1);
62
63         let nav = navs.pop().unwrap();
64         assert_eq!(expected, FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() });
65     }
66
67     #[test]
68     fn goto_decl_module_outline() {
69         check(
70             r#"
71 //- /main.rs
72 mod foo;
73  // ^^^
74 //- /foo.rs
75 use self$0;
76 "#,
77         )
78     }
79
80     #[test]
81     fn goto_decl_module_inline() {
82         check(
83             r#"
84 mod foo {
85  // ^^^
86     use self$0;
87 }
88 "#,
89         )
90     }
91 }