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,
13 ast::{self, AstNode, PathSegmentKind},
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_self_param(
121 sema: &Semantics<RootDatabase>,
122 self_param: &ast::SelfParam,
123 ) -> Option<NameClass> {
124 sema.to_def(self_param).map(Definition::Local).map(NameClass::Definition)
127 pub fn classify(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<NameClass> {
128 let _p = profile::span("classify_name");
130 let parent = name.syntax().parent()?;
132 if let Some(bind_pat) = ast::IdentPat::cast(parent.clone()) {
133 if let Some(def) = sema.resolve_bind_pat_to_const(&bind_pat) {
134 return Some(NameClass::ConstReference(Definition::ModuleDef(def)));
141 if let Some(use_tree) = it.syntax().parent().and_then(ast::UseTree::cast) {
142 let path = use_tree.path()?;
143 let path_segment = path.segment()?;
144 let name_ref_class = path_segment
148 // The rename might be from a `self` token, so fallback to the name higher
150 PathSegmentKind::SelfKw => {
151 let use_tree = use_tree
155 // Skip over UseTreeList
156 .and_then(SyntaxNode::parent)
157 .and_then(ast::UseTree::cast)?;
158 let path = use_tree.path()?;
159 let path_segment = path.segment()?;
160 path_segment.name_ref()
162 PathSegmentKind::Name(name_ref) => Some(name_ref),
166 .and_then(|name_ref| NameRefClass::classify(sema, &name_ref))?;
168 Some(NameClass::Definition(name_ref_class.referenced(sema.db)))
170 let extern_crate = it.syntax().parent().and_then(ast::ExternCrate::cast)?;
171 let resolved = sema.resolve_extern_crate(&extern_crate)?;
172 Some(NameClass::ExternCrate(resolved))
175 ast::IdentPat(it) => {
176 let local = sema.to_def(&it)?;
178 if let Some(record_pat_field) = it.syntax().parent().and_then(ast::RecordPatField::cast) {
179 if record_pat_field.name_ref().is_none() {
180 if let Some(field) = sema.resolve_record_pat_field(&record_pat_field) {
181 let field = Definition::Field(field);
182 return Some(NameClass::PatFieldShorthand { local_def: local, field_ref: field });
187 Some(NameClass::Definition(Definition::Local(local)))
189 ast::RecordField(it) => {
190 let field: hir::Field = sema.to_def(&it)?;
191 Some(NameClass::Definition(Definition::Field(field)))
194 let def = sema.to_def(&it)?;
195 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
198 let def: hir::Struct = sema.to_def(&it)?;
199 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
202 let def: hir::Union = sema.to_def(&it)?;
203 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
206 let def: hir::Enum = sema.to_def(&it)?;
207 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
210 let def: hir::Trait = sema.to_def(&it)?;
211 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
214 let def: hir::Static = sema.to_def(&it)?;
215 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
217 ast::Variant(it) => {
218 let def: hir::Variant = sema.to_def(&it)?;
219 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
222 let def: hir::Function = sema.to_def(&it)?;
223 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
226 let def: hir::Const = sema.to_def(&it)?;
227 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
229 ast::TypeAlias(it) => {
230 let def: hir::TypeAlias = sema.to_def(&it)?;
231 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
233 ast::MacroRules(it) => {
234 let def = sema.to_def(&it)?;
235 Some(NameClass::Definition(Definition::Macro(def)))
237 ast::TypeParam(it) => {
238 let def = sema.to_def(&it)?;
239 Some(NameClass::Definition(Definition::GenericParam(def.into())))
241 ast::ConstParam(it) => {
242 let def = sema.to_def(&it)?;
243 Some(NameClass::Definition(Definition::GenericParam(def.into())))
250 pub fn classify_lifetime(
251 sema: &Semantics<RootDatabase>,
252 lifetime: &ast::Lifetime,
253 ) -> Option<NameClass> {
254 let _p = profile::span("classify_lifetime").detail(|| lifetime.to_string());
255 let parent = lifetime.syntax().parent()?;
259 ast::LifetimeParam(it) => {
260 let def = sema.to_def(&it)?;
261 Some(NameClass::Definition(Definition::GenericParam(def.into())))
264 let def = sema.to_def(&it)?;
265 Some(NameClass::Definition(Definition::Label(def)))
274 pub enum NameRefClass {
276 Definition(Definition),
277 FieldShorthand { local_ref: Local, field_ref: Definition },
281 /// `Definition`, which this name refers to.
282 pub fn referenced(self, db: &dyn HirDatabase) -> Definition {
284 NameRefClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()),
285 NameRefClass::Definition(def) => def,
286 NameRefClass::FieldShorthand { local_ref, field_ref: _ } => {
287 // FIXME: this is inherently ambiguous -- this name refers to
288 // two different defs....
289 Definition::Local(local_ref)
294 // Note: we don't have unit-tests for this rather important function.
295 // It is primarily exercised via goto definition tests in `ide`.
297 sema: &Semantics<RootDatabase>,
298 name_ref: &ast::NameRef,
299 ) -> Option<NameRefClass> {
300 let _p = profile::span("classify_name_ref").detail(|| name_ref.to_string());
302 let parent = name_ref.syntax().parent()?;
304 if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) {
305 if let Some(func) = sema.resolve_method_call(&method_call) {
306 return Some(NameRefClass::Definition(Definition::ModuleDef(func.into())));
310 if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
311 if let Some(field) = sema.resolve_field(&field_expr) {
312 return Some(NameRefClass::Definition(Definition::Field(field)));
316 if let Some(record_field) = ast::RecordExprField::for_field_name(name_ref) {
317 if let Some((field, local)) = sema.resolve_record_field(&record_field) {
318 let field = Definition::Field(field);
319 let res = match local {
320 None => NameRefClass::Definition(field),
322 NameRefClass::FieldShorthand { field_ref: field, local_ref: local }
329 if let Some(record_pat_field) = ast::RecordPatField::cast(parent.clone()) {
330 if let Some(field) = sema.resolve_record_pat_field(&record_pat_field) {
331 let field = Definition::Field(field);
332 return Some(NameRefClass::Definition(field));
336 if ast::AssocTypeArg::cast(parent.clone()).is_some() {
337 // `Trait<Assoc = Ty>`
339 let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?;
340 let resolved = sema.resolve_path(&path)?;
341 if let PathResolution::Def(ModuleDef::Trait(tr)) = resolved {
345 .filter_map(|assoc| match assoc {
346 hir::AssocItem::TypeAlias(it) => Some(*it),
349 .find(|alias| alias.name(sema.db).to_string() == **name_ref.text())
351 return Some(NameRefClass::Definition(Definition::ModuleDef(
352 ModuleDef::TypeAlias(ty),
358 if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) {
359 if let Some(path) = macro_call.path() {
360 if path.qualifier().is_none() {
361 // Only use this to resolve single-segment macro calls like `foo!()`. Multi-segment
362 // paths are handled below (allowing `log$0::info!` to resolve to the log crate).
363 if let Some(macro_def) = sema.resolve_macro_call(¯o_call) {
364 return Some(NameRefClass::Definition(Definition::Macro(macro_def)));
370 if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) {
371 if let Some(resolved) = sema.resolve_path(&path) {
372 return Some(NameRefClass::Definition(resolved.into()));
376 let extern_crate = ast::ExternCrate::cast(parent)?;
377 let resolved = sema.resolve_extern_crate(&extern_crate)?;
378 Some(NameRefClass::ExternCrate(resolved))
381 pub fn classify_lifetime(
382 sema: &Semantics<RootDatabase>,
383 lifetime: &ast::Lifetime,
384 ) -> Option<NameRefClass> {
385 let _p = profile::span("classify_lifetime_ref").detail(|| lifetime.to_string());
386 let parent = lifetime.syntax().parent()?;
387 match parent.kind() {
388 SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => {
389 sema.resolve_label(lifetime).map(Definition::Label).map(NameRefClass::Definition)
391 SyntaxKind::LIFETIME_ARG
392 | SyntaxKind::SELF_PARAM
393 | SyntaxKind::TYPE_BOUND
394 | SyntaxKind::WHERE_PRED
395 | SyntaxKind::REF_TYPE => sema
396 .resolve_lifetime_param(lifetime)
397 .map(GenericParam::LifetimeParam)
398 .map(Definition::GenericParam)
399 .map(NameRefClass::Definition),
400 // lifetime bounds, as in the 'b in 'a: 'b aren't wrapped in TypeBound nodes so we gotta check
401 // if our lifetime is in a LifetimeParam without being the constrained lifetime
402 _ if ast::LifetimeParam::cast(parent).and_then(|param| param.lifetime()).as_ref()
405 sema.resolve_lifetime_param(lifetime)
406 .map(GenericParam::LifetimeParam)
407 .map(Definition::GenericParam)
408 .map(NameRefClass::Definition)
415 impl From<PathResolution> for Definition {
416 fn from(path_resolution: PathResolution) -> Self {
417 match path_resolution {
418 PathResolution::Def(def) => Definition::ModuleDef(def),
419 PathResolution::AssocItem(item) => {
420 let def = match item {
421 hir::AssocItem::Function(it) => it.into(),
422 hir::AssocItem::Const(it) => it.into(),
423 hir::AssocItem::TypeAlias(it) => it.into(),
425 Definition::ModuleDef(def)
427 PathResolution::Local(local) => Definition::Local(local),
428 PathResolution::TypeParam(par) => Definition::GenericParam(par.into()),
429 PathResolution::Macro(def) => Definition::Macro(def),
430 PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def),
431 PathResolution::ConstParam(par) => Definition::GenericParam(par.into()),