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, GenericParam, HasVisibility, Impl, Label, Local, MacroDef,
10 Module, ModuleDef, Name, PathResolution, Semantics, 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)]
27 GenericParam(GenericParam),
32 pub fn module(&self, db: &RootDatabase) -> Option<Module> {
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::GenericParam(it) => Some(it.module(db)),
40 Definition::Label(it) => Some(it.module(db)),
44 pub fn visibility(&self, db: &RootDatabase) -> Option<Visibility> {
46 Definition::Macro(_) => None,
47 Definition::Field(sf) => Some(sf.visibility(db)),
48 Definition::ModuleDef(def) => def.definition_visibility(db),
49 Definition::SelfType(_) => None,
50 Definition::Local(_) => None,
51 Definition::GenericParam(_) => None,
52 Definition::Label(_) => None,
56 pub fn name(&self, db: &RootDatabase) -> Option<Name> {
57 let name = match self {
58 Definition::Macro(it) => it.name(db)?,
59 Definition::Field(it) => it.name(db),
60 Definition::ModuleDef(def) => match def {
61 hir::ModuleDef::Module(it) => it.name(db)?,
62 hir::ModuleDef::Function(it) => it.name(db),
63 hir::ModuleDef::Adt(def) => match def {
64 hir::Adt::Struct(it) => it.name(db),
65 hir::Adt::Union(it) => it.name(db),
66 hir::Adt::Enum(it) => it.name(db),
68 hir::ModuleDef::Variant(it) => it.name(db),
69 hir::ModuleDef::Const(it) => it.name(db)?,
70 hir::ModuleDef::Static(it) => it.name(db)?,
71 hir::ModuleDef::Trait(it) => it.name(db),
72 hir::ModuleDef::TypeAlias(it) => it.name(db),
73 hir::ModuleDef::BuiltinType(_) => return None,
75 Definition::SelfType(_) => return None,
76 Definition::Local(it) => it.name(db)?,
77 Definition::GenericParam(it) => it.name(db),
78 Definition::Label(it) => it.name(db),
87 Definition(Definition),
88 /// `None` in `if let None = Some(82) {}`.
89 ConstReference(Definition),
90 /// `field` in `if let Foo { field } = foo`.
93 field_ref: Definition,
98 /// `Definition` defined by this name.
99 pub fn defined(self, db: &dyn HirDatabase) -> Option<Definition> {
100 let res = match self {
101 NameClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()),
102 NameClass::Definition(it) => it,
103 NameClass::ConstReference(_) => return None,
104 NameClass::PatFieldShorthand { local_def, field_ref: _ } => {
105 Definition::Local(local_def)
111 /// `Definition` referenced or defined by this name.
112 pub fn referenced_or_defined(self, db: &dyn HirDatabase) -> Definition {
114 NameClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()),
115 NameClass::Definition(it) | NameClass::ConstReference(it) => it,
116 NameClass::PatFieldShorthand { local_def: _, field_ref } => field_ref,
120 pub fn classify(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<NameClass> {
121 let _p = profile::span("classify_name");
123 let parent = name.syntax().parent()?;
125 if let Some(bind_pat) = ast::IdentPat::cast(parent.clone()) {
126 if let Some(def) = sema.resolve_bind_pat_to_const(&bind_pat) {
127 return Some(NameClass::ConstReference(Definition::ModuleDef(def)));
134 if let Some(use_tree) = it.syntax().parent().and_then(ast::UseTree::cast) {
135 let path = use_tree.path()?;
136 let path_segment = path.segment()?;
137 let name_ref_class = path_segment
139 // The rename might be from a `self` token, so fallback to the name higher
142 if path_segment.self_token().is_none() {
146 let use_tree = use_tree
150 // Skip over UseTreeList
151 .and_then(SyntaxNode::parent)
152 .and_then(ast::UseTree::cast)?;
153 let path = use_tree.path()?;
154 let path_segment = path.segment()?;
155 path_segment.name_ref()
157 .and_then(|name_ref| NameRefClass::classify(sema, &name_ref))?;
159 Some(NameClass::Definition(name_ref_class.referenced(sema.db)))
161 let extern_crate = it.syntax().parent().and_then(ast::ExternCrate::cast)?;
162 let resolved = sema.resolve_extern_crate(&extern_crate)?;
163 Some(NameClass::ExternCrate(resolved))
166 ast::IdentPat(it) => {
167 let local = sema.to_def(&it)?;
169 if let Some(record_pat_field) = it.syntax().parent().and_then(ast::RecordPatField::cast) {
170 if record_pat_field.name_ref().is_none() {
171 if let Some(field) = sema.resolve_record_pat_field(&record_pat_field) {
172 let field = Definition::Field(field);
173 return Some(NameClass::PatFieldShorthand { local_def: local, field_ref: field });
178 Some(NameClass::Definition(Definition::Local(local)))
180 ast::RecordField(it) => {
181 let field: hir::Field = sema.to_def(&it)?;
182 Some(NameClass::Definition(Definition::Field(field)))
185 let def = sema.to_def(&it)?;
186 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
189 let def: hir::Struct = sema.to_def(&it)?;
190 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
193 let def: hir::Union = sema.to_def(&it)?;
194 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
197 let def: hir::Enum = sema.to_def(&it)?;
198 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
201 let def: hir::Trait = sema.to_def(&it)?;
202 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
205 let def: hir::Static = sema.to_def(&it)?;
206 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
208 ast::Variant(it) => {
209 let def: hir::Variant = sema.to_def(&it)?;
210 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
213 let def: hir::Function = sema.to_def(&it)?;
214 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
217 let def: hir::Const = sema.to_def(&it)?;
218 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
220 ast::TypeAlias(it) => {
221 let def: hir::TypeAlias = sema.to_def(&it)?;
222 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
224 ast::MacroRules(it) => {
225 let def = sema.to_def(&it)?;
226 Some(NameClass::Definition(Definition::Macro(def)))
228 ast::TypeParam(it) => {
229 let def = sema.to_def(&it)?;
230 Some(NameClass::Definition(Definition::GenericParam(def.into())))
232 ast::ConstParam(it) => {
233 let def = sema.to_def(&it)?;
234 Some(NameClass::Definition(Definition::GenericParam(def.into())))
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::GenericParam(def.into())))
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$0::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(GenericParam::LifetimeParam)
389 .map(Definition::GenericParam)
390 .map(NameRefClass::Definition),
391 // lifetime bounds, as in the 'b in 'a: 'b aren't wrapped in TypeBound nodes so we gotta check
392 // if our lifetime is in a LifetimeParam without being the constrained lifetime
393 _ if ast::LifetimeParam::cast(parent).and_then(|param| param.lifetime()).as_ref()
396 sema.resolve_lifetime_param(lifetime)
397 .map(GenericParam::LifetimeParam)
398 .map(Definition::GenericParam)
399 .map(NameRefClass::Definition)
406 impl From<PathResolution> for Definition {
407 fn from(path_resolution: PathResolution) -> Self {
408 match path_resolution {
409 PathResolution::Def(def) => Definition::ModuleDef(def),
410 PathResolution::AssocItem(item) => {
411 let def = match item {
412 hir::AssocItem::Function(it) => it.into(),
413 hir::AssocItem::Const(it) => it.into(),
414 hir::AssocItem::TypeAlias(it) => it.into(),
416 Definition::ModuleDef(def)
418 PathResolution::Local(local) => Definition::Local(local),
419 PathResolution::TypeParam(par) => Definition::GenericParam(par.into()),
420 PathResolution::Macro(def) => Definition::Macro(def),
421 PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def),
422 PathResolution::ConstParam(par) => Definition::GenericParam(par.into()),