]> git.lizzy.rs Git - rust.git/blob - crates/ra_ide_db/src/defs.rs
Swap `into_definition` and `definition` semantics for `FieldShorthand` variant
[rust.git] / crates / ra_ide_db / src / defs.rs
1 //! `NameDefinition` keeps information about the element we want to search references for.
2 //! The element is represented by `NameKind`. It's located inside some `container` and
3 //! has a `visibility`, which defines a search scope.
4 //! Note that the reference search is possible for not all of the classified items.
5
6 // FIXME: this badly needs rename/rewrite (matklad, 2020-02-06).
7
8 use hir::{
9     Field, HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, Name, PathResolution,
10     Semantics, TypeParam, Visibility,
11 };
12 use ra_prof::profile;
13 use ra_syntax::{
14     ast::{self, AstNode},
15     match_ast,
16 };
17
18 use crate::RootDatabase;
19
20 // FIXME: a more precise name would probably be `Symbol`?
21 #[derive(Debug, PartialEq, Eq, Copy, Clone)]
22 pub enum Definition {
23     Macro(MacroDef),
24     Field(Field),
25     ModuleDef(ModuleDef),
26     SelfType(ImplDef),
27     Local(Local),
28     TypeParam(TypeParam),
29 }
30
31 impl Definition {
32     pub fn module(&self, db: &RootDatabase) -> Option<Module> {
33         match self {
34             Definition::Macro(it) => it.module(db),
35             Definition::Field(it) => Some(it.parent_def(db).module(db)),
36             Definition::ModuleDef(it) => it.module(db),
37             Definition::SelfType(it) => Some(it.module(db)),
38             Definition::Local(it) => Some(it.module(db)),
39             Definition::TypeParam(it) => Some(it.module(db)),
40         }
41     }
42
43     pub fn visibility(&self, db: &RootDatabase) -> Option<Visibility> {
44         match self {
45             Definition::Macro(_) => None,
46             Definition::Field(sf) => Some(sf.visibility(db)),
47             Definition::ModuleDef(def) => def.definition_visibility(db),
48             Definition::SelfType(_) => None,
49             Definition::Local(_) => None,
50             Definition::TypeParam(_) => None,
51         }
52     }
53
54     pub fn name(&self, db: &RootDatabase) -> Option<Name> {
55         let name = match self {
56             Definition::Macro(it) => it.name(db)?,
57             Definition::Field(it) => it.name(db),
58             Definition::ModuleDef(def) => match def {
59                 hir::ModuleDef::Module(it) => it.name(db)?,
60                 hir::ModuleDef::Function(it) => it.name(db),
61                 hir::ModuleDef::Adt(def) => match def {
62                     hir::Adt::Struct(it) => it.name(db),
63                     hir::Adt::Union(it) => it.name(db),
64                     hir::Adt::Enum(it) => it.name(db),
65                 },
66                 hir::ModuleDef::EnumVariant(it) => it.name(db),
67                 hir::ModuleDef::Const(it) => it.name(db)?,
68                 hir::ModuleDef::Static(it) => it.name(db)?,
69                 hir::ModuleDef::Trait(it) => it.name(db),
70                 hir::ModuleDef::TypeAlias(it) => it.name(db),
71                 hir::ModuleDef::BuiltinType(_) => return None,
72             },
73             Definition::SelfType(_) => return None,
74             Definition::Local(it) => it.name(db)?,
75             Definition::TypeParam(it) => it.name(db),
76         };
77         Some(name)
78     }
79 }
80
81 pub enum NameClass {
82     Definition(Definition),
83     /// `None` in `if let None = Some(82) {}`
84     ConstReference(Definition),
85     FieldShorthand {
86         local: Local,
87         field: Definition,
88     },
89 }
90
91 impl NameClass {
92     pub fn into_definition(self) -> Option<Definition> {
93         match self {
94             NameClass::Definition(it) => Some(it),
95             NameClass::ConstReference(_) => None,
96             NameClass::FieldShorthand { local, field: _ } => Some(Definition::Local(local)),
97         }
98     }
99
100     pub fn definition(self) -> Definition {
101         match self {
102             NameClass::Definition(it) | NameClass::ConstReference(it) => it,
103             NameClass::FieldShorthand { local: _, field } => field,
104         }
105     }
106 }
107
108 pub fn classify_name(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<NameClass> {
109     let _p = profile("classify_name");
110
111     let parent = name.syntax().parent()?;
112
113     if let Some(bind_pat) = ast::BindPat::cast(parent.clone()) {
114         if let Some(def) = sema.resolve_bind_pat_to_const(&bind_pat) {
115             return Some(NameClass::ConstReference(Definition::ModuleDef(def)));
116         }
117     }
118
119     match_ast! {
120         match parent {
121             ast::Alias(it) => {
122                 let use_tree = it.syntax().parent().and_then(ast::UseTree::cast)?;
123                 let path = use_tree.path()?;
124                 let path_segment = path.segment()?;
125                 let name_ref = path_segment.name_ref()?;
126                 let name_ref_class = classify_name_ref(sema, &name_ref)?;
127
128                 Some(NameClass::Definition(name_ref_class.definition()))
129             },
130             ast::BindPat(it) => {
131                 let local = sema.to_def(&it)?;
132
133                 if let Some(record_field_pat) = it.syntax().parent().and_then(ast::RecordFieldPat::cast) {
134                     if let Some(field) = sema.resolve_record_field_pat(&record_field_pat) {
135                         let field = Definition::Field(field);
136                         return Some(NameClass::FieldShorthand { local, field });
137                     }
138                 }
139
140                 Some(NameClass::Definition(Definition::Local(local)))
141             },
142             ast::RecordFieldDef(it) => {
143                 let field: hir::Field = sema.to_def(&it)?;
144                 Some(NameClass::Definition(Definition::Field(field)))
145             },
146             ast::Module(it) => {
147                 let def = sema.to_def(&it)?;
148                 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
149             },
150             ast::StructDef(it) => {
151                 let def: hir::Struct = sema.to_def(&it)?;
152                 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
153             },
154             ast::UnionDef(it) => {
155                 let def: hir::Union = sema.to_def(&it)?;
156                 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
157             },
158             ast::EnumDef(it) => {
159                 let def: hir::Enum = sema.to_def(&it)?;
160                 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
161             },
162             ast::TraitDef(it) => {
163                 let def: hir::Trait = sema.to_def(&it)?;
164                 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
165             },
166             ast::StaticDef(it) => {
167                 let def: hir::Static = sema.to_def(&it)?;
168                 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
169             },
170             ast::EnumVariant(it) => {
171                 let def: hir::EnumVariant = sema.to_def(&it)?;
172                 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
173             },
174             ast::FnDef(it) => {
175                 let def: hir::Function = sema.to_def(&it)?;
176                 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
177             },
178             ast::ConstDef(it) => {
179                 let def: hir::Const = sema.to_def(&it)?;
180                 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
181             },
182             ast::TypeAliasDef(it) => {
183                 let def: hir::TypeAlias = sema.to_def(&it)?;
184                 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
185             },
186             ast::MacroCall(it) => {
187                 let def = sema.to_def(&it)?;
188                 Some(NameClass::Definition(Definition::Macro(def)))
189             },
190             ast::TypeParam(it) => {
191                 let def = sema.to_def(&it)?;
192                 Some(NameClass::Definition(Definition::TypeParam(def)))
193             },
194             _ => None,
195         }
196     }
197 }
198
199 #[derive(Debug)]
200 pub enum NameRefClass {
201     Definition(Definition),
202     FieldShorthand { local: Local, field: Definition },
203 }
204
205 impl NameRefClass {
206     pub fn definition(self) -> Definition {
207         match self {
208             NameRefClass::Definition(def) => def,
209             NameRefClass::FieldShorthand { local, field: _ } => Definition::Local(local),
210         }
211     }
212 }
213
214 // Note: we don't have unit-tests for this rather important function.
215 // It is primarily exercised via goto definition tests in `ra_ide`.
216 pub fn classify_name_ref(
217     sema: &Semantics<RootDatabase>,
218     name_ref: &ast::NameRef,
219 ) -> Option<NameRefClass> {
220     let _p = profile("classify_name_ref");
221
222     let parent = name_ref.syntax().parent()?;
223
224     if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) {
225         if let Some(func) = sema.resolve_method_call(&method_call) {
226             return Some(NameRefClass::Definition(Definition::ModuleDef(func.into())));
227         }
228     }
229
230     if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
231         if let Some(field) = sema.resolve_field(&field_expr) {
232             return Some(NameRefClass::Definition(Definition::Field(field)));
233         }
234     }
235
236     if let Some(record_field) = ast::RecordField::for_field_name(name_ref) {
237         if let Some((field, local)) = sema.resolve_record_field(&record_field) {
238             let field = Definition::Field(field);
239             let res = match local {
240                 None => NameRefClass::Definition(field),
241                 Some(local) => NameRefClass::FieldShorthand { field, local },
242             };
243             return Some(res);
244         }
245     }
246
247     if let Some(record_field_pat) = ast::RecordFieldPat::cast(parent.clone()) {
248         if let Some(field) = sema.resolve_record_field_pat(&record_field_pat) {
249             let field = Definition::Field(field);
250             return Some(NameRefClass::Definition(field));
251         }
252     }
253
254     if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) {
255         if let Some(macro_def) = sema.resolve_macro_call(&macro_call) {
256             return Some(NameRefClass::Definition(Definition::Macro(macro_def)));
257         }
258     }
259
260     let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?;
261     let resolved = sema.resolve_path(&path)?;
262     let res = match resolved {
263         PathResolution::Def(def) => Definition::ModuleDef(def),
264         PathResolution::AssocItem(item) => {
265             let def = match item {
266                 hir::AssocItem::Function(it) => it.into(),
267                 hir::AssocItem::Const(it) => it.into(),
268                 hir::AssocItem::TypeAlias(it) => it.into(),
269             };
270             Definition::ModuleDef(def)
271         }
272         PathResolution::Local(local) => Definition::Local(local),
273         PathResolution::TypeParam(par) => Definition::TypeParam(par),
274         PathResolution::Macro(def) => Definition::Macro(def),
275         PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def),
276     };
277     Some(NameRefClass::Definition(res))
278 }