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.
6 // FIXME: this badly needs rename/rewrite (matklad, 2020-02-06).
9 db::HirDatabase, Crate, Field, HasVisibility, Impl, Label, LifetimeParam, Local, MacroDef,
10 Module, ModuleDef, Name, PathResolution, Semantics, TypeParam, Visibility,
14 match_ast, SyntaxKind, SyntaxNode,
17 use crate::RootDatabase;
19 // FIXME: a more precise name would probably be `Symbol`?
20 #[derive(Debug, PartialEq, Eq, Copy, Clone)]
28 LifetimeParam(LifetimeParam),
33 pub fn module(&self, db: &RootDatabase) -> Option<Module> {
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 Definition::LifetimeParam(it) => Some(it.module(db)),
42 Definition::Label(it) => Some(it.module(db)),
46 pub fn visibility(&self, db: &RootDatabase) -> Option<Visibility> {
48 Definition::Macro(_) => None,
49 Definition::Field(sf) => Some(sf.visibility(db)),
50 Definition::ModuleDef(def) => def.definition_visibility(db),
51 Definition::SelfType(_) => None,
52 Definition::Local(_) => None,
53 Definition::TypeParam(_) => None,
54 Definition::LifetimeParam(_) => None,
55 Definition::Label(_) => None,
59 pub fn name(&self, db: &RootDatabase) -> Option<Name> {
60 let name = match self {
61 Definition::Macro(it) => it.name(db)?,
62 Definition::Field(it) => it.name(db),
63 Definition::ModuleDef(def) => match def {
64 hir::ModuleDef::Module(it) => it.name(db)?,
65 hir::ModuleDef::Function(it) => it.name(db),
66 hir::ModuleDef::Adt(def) => match def {
67 hir::Adt::Struct(it) => it.name(db),
68 hir::Adt::Union(it) => it.name(db),
69 hir::Adt::Enum(it) => it.name(db),
71 hir::ModuleDef::Variant(it) => it.name(db),
72 hir::ModuleDef::Const(it) => it.name(db)?,
73 hir::ModuleDef::Static(it) => it.name(db)?,
74 hir::ModuleDef::Trait(it) => it.name(db),
75 hir::ModuleDef::TypeAlias(it) => it.name(db),
76 hir::ModuleDef::BuiltinType(_) => return None,
78 Definition::SelfType(_) => return None,
79 Definition::Local(it) => it.name(db)?,
80 Definition::TypeParam(it) => it.name(db),
81 Definition::LifetimeParam(it) => it.name(db),
82 Definition::Label(it) => it.name(db),
91 Definition(Definition),
92 /// `None` in `if let None = Some(82) {}`.
93 ConstReference(Definition),
94 /// `field` in `if let Foo { field } = foo`.
97 field_ref: Definition,
102 /// `Definition` defined by this name.
103 pub fn defined(self, db: &dyn HirDatabase) -> Option<Definition> {
104 let res = match self {
105 NameClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()),
106 NameClass::Definition(it) => it,
107 NameClass::ConstReference(_) => return None,
108 NameClass::PatFieldShorthand { local_def, field_ref: _ } => {
109 Definition::Local(local_def)
115 /// `Definition` referenced or defined by this name.
116 pub fn referenced_or_defined(self, db: &dyn HirDatabase) -> Definition {
118 NameClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()),
119 NameClass::Definition(it) | NameClass::ConstReference(it) => it,
120 NameClass::PatFieldShorthand { local_def: _, field_ref } => field_ref,
124 pub fn classify(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<NameClass> {
125 let _p = profile::span("classify_name");
127 let parent = name.syntax().parent()?;
129 if let Some(bind_pat) = ast::IdentPat::cast(parent.clone()) {
130 if let Some(def) = sema.resolve_bind_pat_to_const(&bind_pat) {
131 return Some(NameClass::ConstReference(Definition::ModuleDef(def)));
138 if let Some(use_tree) = it.syntax().parent().and_then(ast::UseTree::cast) {
139 let path = use_tree.path()?;
140 let path_segment = path.segment()?;
141 let name_ref_class = path_segment
143 // The rename might be from a `self` token, so fallback to the name higher
146 if path_segment.self_token().is_none() {
150 let use_tree = use_tree
154 // Skip over UseTreeList
155 .and_then(SyntaxNode::parent)
156 .and_then(ast::UseTree::cast)?;
157 let path = use_tree.path()?;
158 let path_segment = path.segment()?;
159 path_segment.name_ref()
161 .and_then(|name_ref| NameRefClass::classify(sema, &name_ref))?;
163 Some(NameClass::Definition(name_ref_class.referenced(sema.db)))
165 let extern_crate = it.syntax().parent().and_then(ast::ExternCrate::cast)?;
166 let resolved = sema.resolve_extern_crate(&extern_crate)?;
167 Some(NameClass::ExternCrate(resolved))
170 ast::IdentPat(it) => {
171 let local = sema.to_def(&it)?;
173 if let Some(record_pat_field) = it.syntax().parent().and_then(ast::RecordPatField::cast) {
174 if record_pat_field.name_ref().is_none() {
175 if let Some(field) = sema.resolve_record_pat_field(&record_pat_field) {
176 let field = Definition::Field(field);
177 return Some(NameClass::PatFieldShorthand { local_def: local, field_ref: field });
182 Some(NameClass::Definition(Definition::Local(local)))
184 ast::RecordField(it) => {
185 let field: hir::Field = sema.to_def(&it)?;
186 Some(NameClass::Definition(Definition::Field(field)))
189 let def = sema.to_def(&it)?;
190 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
193 let def: hir::Struct = sema.to_def(&it)?;
194 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
197 let def: hir::Union = sema.to_def(&it)?;
198 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
201 let def: hir::Enum = sema.to_def(&it)?;
202 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
205 let def: hir::Trait = sema.to_def(&it)?;
206 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
209 let def: hir::Static = sema.to_def(&it)?;
210 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
212 ast::Variant(it) => {
213 let def: hir::Variant = sema.to_def(&it)?;
214 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
217 let def: hir::Function = sema.to_def(&it)?;
218 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
221 let def: hir::Const = sema.to_def(&it)?;
222 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
224 ast::TypeAlias(it) => {
225 let def: hir::TypeAlias = sema.to_def(&it)?;
226 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
228 ast::MacroRules(it) => {
229 let def = sema.to_def(&it)?;
230 Some(NameClass::Definition(Definition::Macro(def)))
232 ast::TypeParam(it) => {
233 let def = sema.to_def(&it)?;
234 Some(NameClass::Definition(Definition::TypeParam(def)))
241 pub fn classify_lifetime(
242 sema: &Semantics<RootDatabase>,
243 lifetime: &ast::Lifetime,
244 ) -> Option<NameClass> {
245 let _p = profile::span("classify_lifetime").detail(|| lifetime.to_string());
246 let parent = lifetime.syntax().parent()?;
250 ast::LifetimeParam(it) => {
251 let def = sema.to_def(&it)?;
252 Some(NameClass::Definition(Definition::LifetimeParam(def)))
255 let def = sema.to_def(&it)?;
256 Some(NameClass::Definition(Definition::Label(def)))
265 pub enum NameRefClass {
267 Definition(Definition),
268 FieldShorthand { local_ref: Local, field_ref: Definition },
272 /// `Definition`, which this name refers to.
273 pub fn referenced(self, db: &dyn HirDatabase) -> Definition {
275 NameRefClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()),
276 NameRefClass::Definition(def) => def,
277 NameRefClass::FieldShorthand { local_ref, field_ref: _ } => {
278 // FIXME: this is inherently ambiguous -- this name refers to
279 // two different defs....
280 Definition::Local(local_ref)
285 // Note: we don't have unit-tests for this rather important function.
286 // It is primarily exercised via goto definition tests in `ide`.
288 sema: &Semantics<RootDatabase>,
289 name_ref: &ast::NameRef,
290 ) -> Option<NameRefClass> {
291 let _p = profile::span("classify_name_ref").detail(|| name_ref.to_string());
293 let parent = name_ref.syntax().parent()?;
295 if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) {
296 if let Some(func) = sema.resolve_method_call(&method_call) {
297 return Some(NameRefClass::Definition(Definition::ModuleDef(func.into())));
301 if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
302 if let Some(field) = sema.resolve_field(&field_expr) {
303 return Some(NameRefClass::Definition(Definition::Field(field)));
307 if let Some(record_field) = ast::RecordExprField::for_field_name(name_ref) {
308 if let Some((field, local)) = sema.resolve_record_field(&record_field) {
309 let field = Definition::Field(field);
310 let res = match local {
311 None => NameRefClass::Definition(field),
313 NameRefClass::FieldShorthand { field_ref: field, local_ref: local }
320 if let Some(record_pat_field) = ast::RecordPatField::cast(parent.clone()) {
321 if let Some(field) = sema.resolve_record_pat_field(&record_pat_field) {
322 let field = Definition::Field(field);
323 return Some(NameRefClass::Definition(field));
327 if ast::AssocTypeArg::cast(parent.clone()).is_some() {
328 // `Trait<Assoc = Ty>`
330 let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?;
331 let resolved = sema.resolve_path(&path)?;
332 if let PathResolution::Def(ModuleDef::Trait(tr)) = resolved {
336 .filter_map(|assoc| match assoc {
337 hir::AssocItem::TypeAlias(it) => Some(*it),
340 .find(|alias| alias.name(sema.db).to_string() == **name_ref.text())
342 return Some(NameRefClass::Definition(Definition::ModuleDef(
343 ModuleDef::TypeAlias(ty),
349 if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) {
350 if let Some(path) = macro_call.path() {
351 if path.qualifier().is_none() {
352 // Only use this to resolve single-segment macro calls like `foo!()`. Multi-segment
353 // paths are handled below (allowing `log<|>::info!` to resolve to the log crate).
354 if let Some(macro_def) = sema.resolve_macro_call(¯o_call) {
355 return Some(NameRefClass::Definition(Definition::Macro(macro_def)));
361 if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) {
362 if let Some(resolved) = sema.resolve_path(&path) {
363 return Some(NameRefClass::Definition(resolved.into()));
367 let extern_crate = ast::ExternCrate::cast(parent)?;
368 let resolved = sema.resolve_extern_crate(&extern_crate)?;
369 Some(NameRefClass::ExternCrate(resolved))
372 pub fn classify_lifetime(
373 sema: &Semantics<RootDatabase>,
374 lifetime: &ast::Lifetime,
375 ) -> Option<NameRefClass> {
376 let _p = profile::span("classify_lifetime_ref").detail(|| lifetime.to_string());
377 let parent = lifetime.syntax().parent()?;
378 match parent.kind() {
379 SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => {
380 sema.resolve_label(lifetime).map(Definition::Label).map(NameRefClass::Definition)
382 SyntaxKind::LIFETIME_ARG
383 | SyntaxKind::SELF_PARAM
384 | SyntaxKind::TYPE_BOUND
385 | SyntaxKind::WHERE_PRED
386 | SyntaxKind::REF_TYPE => sema
387 .resolve_lifetime_param(lifetime)
388 .map(Definition::LifetimeParam)
389 .map(NameRefClass::Definition),
390 // lifetime bounds, as in the 'b in 'a: 'b aren't wrapped in TypeBound nodes so we gotta check
391 // if our lifetime is in a LifetimeParam without being the constrained lifetime
392 _ if ast::LifetimeParam::cast(parent).and_then(|param| param.lifetime()).as_ref()
395 sema.resolve_lifetime_param(lifetime)
396 .map(Definition::LifetimeParam)
397 .map(NameRefClass::Definition)
404 impl From<PathResolution> for Definition {
405 fn from(path_resolution: PathResolution) -> Self {
406 match path_resolution {
407 PathResolution::Def(def) => Definition::ModuleDef(def),
408 PathResolution::AssocItem(item) => {
409 let def = match item {
410 hir::AssocItem::Function(it) => it.into(),
411 hir::AssocItem::Const(it) => it.into(),
412 hir::AssocItem::TypeAlias(it) => it.into(),
414 Definition::ModuleDef(def)
416 PathResolution::Local(local) => Definition::Local(local),
417 PathResolution::TypeParam(par) => Definition::TypeParam(par),
418 PathResolution::Macro(def) => Definition::Macro(def),
419 PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def),