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