aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/references/classify.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/references/classify.rs')
-rw-r--r--crates/ra_ide/src/references/classify.rs186
1 files changed, 186 insertions, 0 deletions
diff --git a/crates/ra_ide/src/references/classify.rs b/crates/ra_ide/src/references/classify.rs
new file mode 100644
index 000000000..5cea805ec
--- /dev/null
+++ b/crates/ra_ide/src/references/classify.rs
@@ -0,0 +1,186 @@
1//! Functions that are used to classify an element from its definition or reference.
2
3use hir::{FromSource, Module, ModuleSource, PathResolution, Source, SourceAnalyzer};
4use ra_prof::profile;
5use ra_syntax::{ast, match_ast, AstNode};
6use test_utils::tested_by;
7
8use super::{
9 name_definition::{from_assoc_item, from_module_def, from_struct_field},
10 NameDefinition, NameKind,
11};
12use crate::db::RootDatabase;
13
14pub(crate) fn classify_name(db: &RootDatabase, name: Source<&ast::Name>) -> Option<NameDefinition> {
15 let _p = profile("classify_name");
16 let parent = name.value.syntax().parent()?;
17
18 match_ast! {
19 match parent {
20 ast::BindPat(it) => {
21 let src = name.with_value(it);
22 let local = hir::Local::from_source(db, src)?;
23 Some(NameDefinition {
24 visibility: None,
25 container: local.module(db),
26 kind: NameKind::Local(local),
27 })
28 },
29 ast::RecordFieldDef(it) => {
30 let ast = hir::FieldSource::Named(it);
31 let src = name.with_value(ast);
32 let field = hir::StructField::from_source(db, src)?;
33 Some(from_struct_field(db, field))
34 },
35 ast::Module(it) => {
36 let def = {
37 if !it.has_semi() {
38 let ast = hir::ModuleSource::Module(it);
39 let src = name.with_value(ast);
40 hir::Module::from_definition(db, src)
41 } else {
42 let src = name.with_value(it);
43 hir::Module::from_declaration(db, src)
44 }
45 }?;
46 Some(from_module_def(db, def.into(), None))
47 },
48 ast::StructDef(it) => {
49 let src = name.with_value(it);
50 let def = hir::Struct::from_source(db, src)?;
51 Some(from_module_def(db, def.into(), None))
52 },
53 ast::EnumDef(it) => {
54 let src = name.with_value(it);
55 let def = hir::Enum::from_source(db, src)?;
56 Some(from_module_def(db, def.into(), None))
57 },
58 ast::TraitDef(it) => {
59 let src = name.with_value(it);
60 let def = hir::Trait::from_source(db, src)?;
61 Some(from_module_def(db, def.into(), None))
62 },
63 ast::StaticDef(it) => {
64 let src = name.with_value(it);
65 let def = hir::Static::from_source(db, src)?;
66 Some(from_module_def(db, def.into(), None))
67 },
68 ast::EnumVariant(it) => {
69 let src = name.with_value(it);
70 let def = hir::EnumVariant::from_source(db, src)?;
71 Some(from_module_def(db, def.into(), None))
72 },
73 ast::FnDef(it) => {
74 let src = name.with_value(it);
75 let def = hir::Function::from_source(db, src)?;
76 if parent.parent().and_then(ast::ItemList::cast).is_some() {
77 Some(from_assoc_item(db, def.into()))
78 } else {
79 Some(from_module_def(db, def.into(), None))
80 }
81 },
82 ast::ConstDef(it) => {
83 let src = name.with_value(it);
84 let def = hir::Const::from_source(db, src)?;
85 if parent.parent().and_then(ast::ItemList::cast).is_some() {
86 Some(from_assoc_item(db, def.into()))
87 } else {
88 Some(from_module_def(db, def.into(), None))
89 }
90 },
91 ast::TypeAliasDef(it) => {
92 let src = name.with_value(it);
93 let def = hir::TypeAlias::from_source(db, src)?;
94 if parent.parent().and_then(ast::ItemList::cast).is_some() {
95 Some(from_assoc_item(db, def.into()))
96 } else {
97 Some(from_module_def(db, def.into(), None))
98 }
99 },
100 ast::MacroCall(it) => {
101 let src = name.with_value(it);
102 let def = hir::MacroDef::from_source(db, src.clone())?;
103
104 let module_src = ModuleSource::from_child_node(db, src.as_ref().map(|it| it.syntax()));
105 let module = Module::from_definition(db, src.with_value(module_src))?;
106
107 Some(NameDefinition {
108 visibility: None,
109 container: module,
110 kind: NameKind::Macro(def),
111 })
112 },
113 _ => None,
114 }
115 }
116}
117
118pub(crate) fn classify_name_ref(
119 db: &RootDatabase,
120 name_ref: Source<&ast::NameRef>,
121) -> Option<NameDefinition> {
122 let _p = profile("classify_name_ref");
123
124 let parent = name_ref.value.syntax().parent()?;
125 let analyzer = SourceAnalyzer::new(db, name_ref.map(|it| it.syntax()), None);
126
127 if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) {
128 tested_by!(goto_definition_works_for_methods);
129 if let Some(func) = analyzer.resolve_method_call(&method_call) {
130 return Some(from_assoc_item(db, func.into()));
131 }
132 }
133
134 if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
135 tested_by!(goto_definition_works_for_fields);
136 if let Some(field) = analyzer.resolve_field(&field_expr) {
137 return Some(from_struct_field(db, field));
138 }
139 }
140
141 if let Some(record_field) = ast::RecordField::cast(parent.clone()) {
142 tested_by!(goto_definition_works_for_record_fields);
143 if let Some(field_def) = analyzer.resolve_record_field(&record_field) {
144 return Some(from_struct_field(db, field_def));
145 }
146 }
147
148 let ast = ModuleSource::from_child_node(db, name_ref.with_value(&parent));
149 // FIXME: find correct container and visibility for each case
150 let container = Module::from_definition(db, name_ref.with_value(ast))?;
151 let visibility = None;
152
153 if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) {
154 tested_by!(goto_definition_works_for_macros);
155 if let Some(macro_def) = analyzer.resolve_macro_call(db, name_ref.with_value(&macro_call)) {
156 let kind = NameKind::Macro(macro_def);
157 return Some(NameDefinition { kind, container, visibility });
158 }
159 }
160
161 let path = name_ref.value.syntax().ancestors().find_map(ast::Path::cast)?;
162 let resolved = analyzer.resolve_path(db, &path)?;
163 match resolved {
164 PathResolution::Def(def) => Some(from_module_def(db, def, Some(container))),
165 PathResolution::AssocItem(item) => Some(from_assoc_item(db, item)),
166 PathResolution::Local(local) => {
167 let container = local.module(db);
168 let kind = NameKind::Local(local);
169 Some(NameDefinition { kind, container, visibility: None })
170 }
171 PathResolution::GenericParam(par) => {
172 // FIXME: get generic param def
173 let kind = NameKind::GenericParam(par);
174 Some(NameDefinition { kind, container, visibility })
175 }
176 PathResolution::Macro(def) => {
177 let kind = NameKind::Macro(def);
178 Some(NameDefinition { kind, container, visibility })
179 }
180 PathResolution::SelfType(impl_block) => {
181 let kind = NameKind::SelfType(impl_block);
182 let container = impl_block.module(db);
183 Some(NameDefinition { kind, container, visibility })
184 }
185 }
186}