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, ConstParam, Crate, Field, HasVisibility, Impl, Label, LifetimeParam, Local,
10 MacroDef, 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),
29 ConstParam(ConstParam),
34 pub fn module(&self, db: &RootDatabase) -> Option<Module> {
36 Definition::Macro(it) => it.module(db),
37 Definition::Field(it) => Some(it.parent_def(db).module(db)),
38 Definition::ModuleDef(it) => it.module(db),
39 Definition::SelfType(it) => Some(it.module(db)),
40 Definition::Local(it) => Some(it.module(db)),
41 Definition::TypeParam(it) => Some(it.module(db)),
42 Definition::LifetimeParam(it) => Some(it.module(db)),
43 Definition::ConstParam(it) => Some(it.module(db)),
44 Definition::Label(it) => Some(it.module(db)),
48 pub fn visibility(&self, db: &RootDatabase) -> Option<Visibility> {
50 Definition::Macro(_) => None,
51 Definition::Field(sf) => Some(sf.visibility(db)),
52 Definition::ModuleDef(def) => def.definition_visibility(db),
53 Definition::SelfType(_) => None,
54 Definition::Local(_) => None,
55 Definition::TypeParam(_) => None,
56 Definition::LifetimeParam(_) => None,
57 Definition::ConstParam(_) => None,
58 Definition::Label(_) => None,
62 pub fn name(&self, db: &RootDatabase) -> Option<Name> {
63 let name = match self {
64 Definition::Macro(it) => it.name(db)?,
65 Definition::Field(it) => it.name(db),
66 Definition::ModuleDef(def) => match def {
67 hir::ModuleDef::Module(it) => it.name(db)?,
68 hir::ModuleDef::Function(it) => it.name(db),
69 hir::ModuleDef::Adt(def) => match def {
70 hir::Adt::Struct(it) => it.name(db),
71 hir::Adt::Union(it) => it.name(db),
72 hir::Adt::Enum(it) => it.name(db),
74 hir::ModuleDef::Variant(it) => it.name(db),
75 hir::ModuleDef::Const(it) => it.name(db)?,
76 hir::ModuleDef::Static(it) => it.name(db)?,
77 hir::ModuleDef::Trait(it) => it.name(db),
78 hir::ModuleDef::TypeAlias(it) => it.name(db),
79 hir::ModuleDef::BuiltinType(_) => return None,
81 Definition::SelfType(_) => return None,
82 Definition::Local(it) => it.name(db)?,
83 Definition::TypeParam(it) => it.name(db),
84 Definition::LifetimeParam(it) => it.name(db),
85 Definition::ConstParam(it) => it.name(db),
86 Definition::Label(it) => it.name(db),
95 Definition(Definition),
96 /// `None` in `if let None = Some(82) {}`.
97 ConstReference(Definition),
98 /// `field` in `if let Foo { field } = foo`.
101 field_ref: Definition,
106 /// `Definition` defined by this name.
107 pub fn defined(self, db: &dyn HirDatabase) -> Option<Definition> {
108 let res = match self {
109 NameClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()),
110 NameClass::Definition(it) => it,
111 NameClass::ConstReference(_) => return None,
112 NameClass::PatFieldShorthand { local_def, field_ref: _ } => {
113 Definition::Local(local_def)
119 /// `Definition` referenced or defined by this name.
120 pub fn referenced_or_defined(self, db: &dyn HirDatabase) -> Definition {
122 NameClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()),
123 NameClass::Definition(it) | NameClass::ConstReference(it) => it,
124 NameClass::PatFieldShorthand { local_def: _, field_ref } => field_ref,
128 pub fn classify(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<NameClass> {
129 let _p = profile::span("classify_name");
131 let parent = name.syntax().parent()?;
133 if let Some(bind_pat) = ast::IdentPat::cast(parent.clone()) {
134 if let Some(def) = sema.resolve_bind_pat_to_const(&bind_pat) {
135 return Some(NameClass::ConstReference(Definition::ModuleDef(def)));
142 if let Some(use_tree) = it.syntax().parent().and_then(ast::UseTree::cast) {
143 let path = use_tree.path()?;
144 let path_segment = path.segment()?;
145 let name_ref_class = path_segment
147 // The rename might be from a `self` token, so fallback to the name higher
150 if path_segment.self_token().is_none() {
154 let use_tree = use_tree
158 // Skip over UseTreeList
159 .and_then(SyntaxNode::parent)
160 .and_then(ast::UseTree::cast)?;
161 let path = use_tree.path()?;
162 let path_segment = path.segment()?;
163 path_segment.name_ref()
165 .and_then(|name_ref| NameRefClass::classify(sema, &name_ref))?;
167 Some(NameClass::Definition(name_ref_class.referenced(sema.db)))
169 let extern_crate = it.syntax().parent().and_then(ast::ExternCrate::cast)?;
170 let resolved = sema.resolve_extern_crate(&extern_crate)?;
171 Some(NameClass::ExternCrate(resolved))
174 ast::IdentPat(it) => {
175 let local = sema.to_def(&it)?;
177 if let Some(record_pat_field) = it.syntax().parent().and_then(ast::RecordPatField::cast) {
178 if record_pat_field.name_ref().is_none() {
179 if let Some(field) = sema.resolve_record_pat_field(&record_pat_field) {
180 let field = Definition::Field(field);
181 return Some(NameClass::PatFieldShorthand { local_def: local, field_ref: field });
186 Some(NameClass::Definition(Definition::Local(local)))
188 ast::RecordField(it) => {
189 let field: hir::Field = sema.to_def(&it)?;
190 Some(NameClass::Definition(Definition::Field(field)))
193 let def = sema.to_def(&it)?;
194 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
197 let def: hir::Struct = sema.to_def(&it)?;
198 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
201 let def: hir::Union = sema.to_def(&it)?;
202 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
205 let def: hir::Enum = sema.to_def(&it)?;
206 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
209 let def: hir::Trait = sema.to_def(&it)?;
210 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
213 let def: hir::Static = sema.to_def(&it)?;
214 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
216 ast::Variant(it) => {
217 let def: hir::Variant = sema.to_def(&it)?;
218 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
221 let def: hir::Function = sema.to_def(&it)?;
222 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
225 let def: hir::Const = sema.to_def(&it)?;
226 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
228 ast::TypeAlias(it) => {
229 let def: hir::TypeAlias = sema.to_def(&it)?;
230 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
232 ast::MacroRules(it) => {
233 let def = sema.to_def(&it)?;
234 Some(NameClass::Definition(Definition::Macro(def)))
236 ast::TypeParam(it) => {
237 let def = sema.to_def(&it)?;
238 Some(NameClass::Definition(Definition::TypeParam(def)))
240 ast::ConstParam(it) => {
241 let def = sema.to_def(&it)?;
242 Some(NameClass::Definition(Definition::ConstParam(def)))
249 pub fn classify_lifetime(
250 sema: &Semantics<RootDatabase>,
251 lifetime: &ast::Lifetime,
252 ) -> Option<NameClass> {
253 let _p = profile::span("classify_lifetime").detail(|| lifetime.to_string());
254 let parent = lifetime.syntax().parent()?;
258 ast::LifetimeParam(it) => {
259 let def = sema.to_def(&it)?;
260 Some(NameClass::Definition(Definition::LifetimeParam(def)))
263 let def = sema.to_def(&it)?;
264 Some(NameClass::Definition(Definition::Label(def)))
273 pub enum NameRefClass {
275 Definition(Definition),
276 FieldShorthand { local_ref: Local, field_ref: Definition },
280 /// `Definition`, which this name refers to.
281 pub fn referenced(self, db: &dyn HirDatabase) -> Definition {
283 NameRefClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()),
284 NameRefClass::Definition(def) => def,
285 NameRefClass::FieldShorthand { local_ref, field_ref: _ } => {
286 // FIXME: this is inherently ambiguous -- this name refers to
287 // two different defs....
288 Definition::Local(local_ref)
293 // Note: we don't have unit-tests for this rather important function.
294 // It is primarily exercised via goto definition tests in `ide`.
296 sema: &Semantics<RootDatabase>,
297 name_ref: &ast::NameRef,
298 ) -> Option<NameRefClass> {
299 let _p = profile::span("classify_name_ref").detail(|| name_ref.to_string());
301 let parent = name_ref.syntax().parent()?;
303 if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) {
304 if let Some(func) = sema.resolve_method_call(&method_call) {
305 return Some(NameRefClass::Definition(Definition::ModuleDef(func.into())));
309 if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
310 if let Some(field) = sema.resolve_field(&field_expr) {
311 return Some(NameRefClass::Definition(Definition::Field(field)));
315 if let Some(record_field) = ast::RecordExprField::for_field_name(name_ref) {
316 if let Some((field, local)) = sema.resolve_record_field(&record_field) {
317 let field = Definition::Field(field);
318 let res = match local {
319 None => NameRefClass::Definition(field),
321 NameRefClass::FieldShorthand { field_ref: field, local_ref: local }
328 if let Some(record_pat_field) = ast::RecordPatField::cast(parent.clone()) {
329 if let Some(field) = sema.resolve_record_pat_field(&record_pat_field) {
330 let field = Definition::Field(field);
331 return Some(NameRefClass::Definition(field));
335 if ast::AssocTypeArg::cast(parent.clone()).is_some() {
336 // `Trait<Assoc = Ty>`
338 let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?;
339 let resolved = sema.resolve_path(&path)?;
340 if let PathResolution::Def(ModuleDef::Trait(tr)) = resolved {
344 .filter_map(|assoc| match assoc {
345 hir::AssocItem::TypeAlias(it) => Some(*it),
348 .find(|alias| alias.name(sema.db).to_string() == **name_ref.text())
350 return Some(NameRefClass::Definition(Definition::ModuleDef(
351 ModuleDef::TypeAlias(ty),
357 if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) {
358 if let Some(path) = macro_call.path() {
359 if path.qualifier().is_none() {
360 // Only use this to resolve single-segment macro calls like `foo!()`. Multi-segment
361 // paths are handled below (allowing `log<|>::info!` to resolve to the log crate).
362 if let Some(macro_def) = sema.resolve_macro_call(¯o_call) {
363 return Some(NameRefClass::Definition(Definition::Macro(macro_def)));
369 if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) {
370 if let Some(resolved) = sema.resolve_path(&path) {
371 return Some(NameRefClass::Definition(resolved.into()));
375 let extern_crate = ast::ExternCrate::cast(parent)?;
376 let resolved = sema.resolve_extern_crate(&extern_crate)?;
377 Some(NameRefClass::ExternCrate(resolved))
380 pub fn classify_lifetime(
381 sema: &Semantics<RootDatabase>,
382 lifetime: &ast::Lifetime,
383 ) -> Option<NameRefClass> {
384 let _p = profile::span("classify_lifetime_ref").detail(|| lifetime.to_string());
385 let parent = lifetime.syntax().parent()?;
386 match parent.kind() {
387 SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => {
388 sema.resolve_label(lifetime).map(Definition::Label).map(NameRefClass::Definition)
390 SyntaxKind::LIFETIME_ARG
391 | SyntaxKind::SELF_PARAM
392 | SyntaxKind::TYPE_BOUND
393 | SyntaxKind::WHERE_PRED
394 | SyntaxKind::REF_TYPE => sema
395 .resolve_lifetime_param(lifetime)
396 .map(Definition::LifetimeParam)
397 .map(NameRefClass::Definition),
398 // lifetime bounds, as in the 'b in 'a: 'b aren't wrapped in TypeBound nodes so we gotta check
399 // if our lifetime is in a LifetimeParam without being the constrained lifetime
400 _ if ast::LifetimeParam::cast(parent).and_then(|param| param.lifetime()).as_ref()
403 sema.resolve_lifetime_param(lifetime)
404 .map(Definition::LifetimeParam)
405 .map(NameRefClass::Definition)
412 impl From<PathResolution> for Definition {
413 fn from(path_resolution: PathResolution) -> Self {
414 match path_resolution {
415 PathResolution::Def(def) => Definition::ModuleDef(def),
416 PathResolution::AssocItem(item) => {
417 let def = match item {
418 hir::AssocItem::Function(it) => it.into(),
419 hir::AssocItem::Const(it) => it.into(),
420 hir::AssocItem::TypeAlias(it) => it.into(),
422 Definition::ModuleDef(def)
424 PathResolution::Local(local) => Definition::Local(local),
425 PathResolution::TypeParam(par) => Definition::TypeParam(par),
426 PathResolution::Macro(def) => Definition::Macro(def),
427 PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def),
428 PathResolution::ConstParam(par) => Definition::ConstParam(par),