]> git.lizzy.rs Git - rust.git/blob - crates/ra_ide_db/src/defs.rs
add support of use alias semantic in definition #4202
[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 use test_utils::tested_by;
18
19 use crate::RootDatabase;
20
21 // FIXME: a more precise name would probably be `Symbol`?
22 #[derive(Debug, PartialEq, Eq)]
23 pub enum Definition {
24     Macro(MacroDef),
25     Field(Field),
26     ModuleDef(ModuleDef),
27     SelfType(ImplDef),
28     Local(Local),
29     TypeParam(TypeParam),
30 }
31
32 impl Definition {
33     pub fn module(&self, db: &RootDatabase) -> Option<Module> {
34         match self {
35             Definition::Macro(it) => it.module(db),
36             Definition::Field(it) => Some(it.parent_def(db).module(db)),
37             Definition::ModuleDef(it) => it.module(db),
38             Definition::SelfType(it) => Some(it.module(db)),
39             Definition::Local(it) => Some(it.module(db)),
40             Definition::TypeParam(it) => Some(it.module(db)),
41         }
42     }
43
44     pub fn visibility(&self, db: &RootDatabase) -> Option<Visibility> {
45         let module = self.module(db);
46
47         match self {
48             Definition::Macro(_) => None,
49             Definition::Field(sf) => Some(sf.visibility(db)),
50             Definition::ModuleDef(def) => module?.visibility_of(db, def),
51             Definition::SelfType(_) => None,
52             Definition::Local(_) => None,
53             Definition::TypeParam(_) => None,
54         }
55     }
56
57     pub fn name(&self, db: &RootDatabase) -> Option<Name> {
58         let name = match self {
59             Definition::Macro(it) => it.name(db)?,
60             Definition::Field(it) => it.name(db),
61             Definition::ModuleDef(def) => match def {
62                 hir::ModuleDef::Module(it) => it.name(db)?,
63                 hir::ModuleDef::Function(it) => it.name(db),
64                 hir::ModuleDef::Adt(def) => match def {
65                     hir::Adt::Struct(it) => it.name(db),
66                     hir::Adt::Union(it) => it.name(db),
67                     hir::Adt::Enum(it) => it.name(db),
68                 },
69                 hir::ModuleDef::EnumVariant(it) => it.name(db),
70                 hir::ModuleDef::Const(it) => it.name(db)?,
71                 hir::ModuleDef::Static(it) => it.name(db)?,
72                 hir::ModuleDef::Trait(it) => it.name(db),
73                 hir::ModuleDef::TypeAlias(it) => it.name(db),
74                 hir::ModuleDef::BuiltinType(_) => return None,
75             },
76             Definition::SelfType(_) => return None,
77             Definition::Local(it) => it.name(db)?,
78             Definition::TypeParam(it) => it.name(db),
79         };
80         Some(name)
81     }
82 }
83
84 pub enum NameClass {
85     Definition(Definition),
86     /// `None` in `if let None = Some(82) {}`
87     ConstReference(Definition),
88 }
89
90 impl NameClass {
91     pub fn into_definition(self) -> Option<Definition> {
92         match self {
93             NameClass::Definition(it) => Some(it),
94             NameClass::ConstReference(_) => None,
95         }
96     }
97
98     pub fn definition(self) -> Definition {
99         match self {
100             NameClass::Definition(it) | NameClass::ConstReference(it) => it,
101         }
102     }
103 }
104
105 pub fn classify_name(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<NameClass> {
106     let _p = profile("classify_name");
107
108     if let Some(bind_pat) = name.syntax().parent().and_then(ast::BindPat::cast) {
109         if let Some(def) = sema.resolve_bind_pat_to_const(&bind_pat) {
110             return Some(NameClass::ConstReference(Definition::ModuleDef(def)));
111         }
112     }
113
114     classify_name_inner(sema, name).map(NameClass::Definition)
115 }
116
117 fn classify_name_inner(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<Definition> {
118     let parent = name.syntax().parent()?;
119
120     match_ast! {
121         match parent {
122             ast::Alias(it) => {
123                 tested_by!(goto_def_for_use_alias; force);
124                 let use_tree = it.syntax().ancestors().find_map(ast::UseTree::cast)?;
125                 let path = use_tree.path()?;
126                 let path_segment = path.segment()?;
127                 let name_ref = path_segment.name_ref()?;
128                 let name_ref_class = classify_name_ref(sema, &name_ref)?;
129
130                 Some(name_ref_class.definition())
131             },
132             ast::BindPat(it) => {
133                 let local = sema.to_def(&it)?;
134                 Some(Definition::Local(local))
135             },
136             ast::RecordFieldDef(it) => {
137                 let field: hir::Field = sema.to_def(&it)?;
138                 Some(Definition::Field(field))
139             },
140             ast::Module(it) => {
141                 let def = sema.to_def(&it)?;
142                 Some(Definition::ModuleDef(def.into()))
143             },
144             ast::StructDef(it) => {
145                 let def: hir::Struct = sema.to_def(&it)?;
146                 Some(Definition::ModuleDef(def.into()))
147             },
148             ast::UnionDef(it) => {
149                 let def: hir::Union = sema.to_def(&it)?;
150                 Some(Definition::ModuleDef(def.into()))
151             },
152             ast::EnumDef(it) => {
153                 let def: hir::Enum = sema.to_def(&it)?;
154                 Some(Definition::ModuleDef(def.into()))
155             },
156             ast::TraitDef(it) => {
157                 let def: hir::Trait = sema.to_def(&it)?;
158                 Some(Definition::ModuleDef(def.into()))
159             },
160             ast::StaticDef(it) => {
161                 let def: hir::Static = sema.to_def(&it)?;
162                 Some(Definition::ModuleDef(def.into()))
163             },
164             ast::EnumVariant(it) => {
165                 let def: hir::EnumVariant = sema.to_def(&it)?;
166                 Some(Definition::ModuleDef(def.into()))
167             },
168             ast::FnDef(it) => {
169                 let def: hir::Function = sema.to_def(&it)?;
170                 Some(Definition::ModuleDef(def.into()))
171             },
172             ast::ConstDef(it) => {
173                 let def: hir::Const = sema.to_def(&it)?;
174                 Some(Definition::ModuleDef(def.into()))
175             },
176             ast::TypeAliasDef(it) => {
177                 let def: hir::TypeAlias = sema.to_def(&it)?;
178                 Some(Definition::ModuleDef(def.into()))
179             },
180             ast::MacroCall(it) => {
181                 let def = sema.to_def(&it)?;
182                 Some(Definition::Macro(def))
183             },
184             ast::TypeParam(it) => {
185                 let def = sema.to_def(&it)?;
186                 Some(Definition::TypeParam(def))
187             },
188             _ => None,
189         }
190     }
191 }
192
193 #[derive(Debug)]
194 pub enum NameRefClass {
195     Definition(Definition),
196     FieldShorthand { local: Local, field: Definition },
197 }
198
199 impl NameRefClass {
200     pub fn definition(self) -> Definition {
201         match self {
202             NameRefClass::Definition(def) => def,
203             NameRefClass::FieldShorthand { local, field: _ } => Definition::Local(local),
204         }
205     }
206 }
207
208 pub fn classify_name_ref(
209     sema: &Semantics<RootDatabase>,
210     name_ref: &ast::NameRef,
211 ) -> Option<NameRefClass> {
212     let _p = profile("classify_name_ref");
213
214     let parent = name_ref.syntax().parent()?;
215
216     if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) {
217         tested_by!(goto_def_for_methods; force);
218         if let Some(func) = sema.resolve_method_call(&method_call) {
219             return Some(NameRefClass::Definition(Definition::ModuleDef(func.into())));
220         }
221     }
222
223     if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
224         tested_by!(goto_def_for_fields; force);
225         if let Some(field) = sema.resolve_field(&field_expr) {
226             return Some(NameRefClass::Definition(Definition::Field(field)));
227         }
228     }
229
230     if let Some(record_field) = ast::RecordField::for_field_name(name_ref) {
231         tested_by!(goto_def_for_record_fields; force);
232         tested_by!(goto_def_for_field_init_shorthand; force);
233         if let Some((field, local)) = sema.resolve_record_field(&record_field) {
234             let field = Definition::Field(field);
235             let res = match local {
236                 None => NameRefClass::Definition(field),
237                 Some(local) => NameRefClass::FieldShorthand { field, local },
238             };
239             return Some(res);
240         }
241     }
242
243     if let Some(record_field_pat) = ast::RecordFieldPat::cast(parent.clone()) {
244         tested_by!(goto_def_for_record_field_pats; force);
245         if let Some(field) = sema.resolve_record_field_pat(&record_field_pat) {
246             let field = Definition::Field(field);
247             return Some(NameRefClass::Definition(field));
248         }
249     }
250
251     if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) {
252         tested_by!(goto_def_for_macros; force);
253         if let Some(macro_def) = sema.resolve_macro_call(&macro_call) {
254             return Some(NameRefClass::Definition(Definition::Macro(macro_def)));
255         }
256     }
257
258     let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?;
259     let resolved = sema.resolve_path(&path)?;
260     let res = match resolved {
261         PathResolution::Def(def) => Definition::ModuleDef(def),
262         PathResolution::AssocItem(item) => {
263             let def = match item {
264                 hir::AssocItem::Function(it) => it.into(),
265                 hir::AssocItem::Const(it) => it.into(),
266                 hir::AssocItem::TypeAlias(it) => it.into(),
267             };
268             Definition::ModuleDef(def)
269         }
270         PathResolution::Local(local) => Definition::Local(local),
271         PathResolution::TypeParam(par) => Definition::TypeParam(par),
272         PathResolution::Macro(def) => Definition::Macro(def),
273         PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def),
274     };
275     Some(NameRefClass::Definition(res))
276 }