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, LifetimeParam, Local, MacroDef, Module,
10 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)),
45 pub fn visibility(&self, db: &RootDatabase) -> Option<Visibility> {
47 Definition::Macro(_) => None,
48 Definition::Field(sf) => Some(sf.visibility(db)),
49 Definition::ModuleDef(def) => def.definition_visibility(db),
50 Definition::SelfType(_) => None,
51 Definition::Local(_) => None,
52 Definition::TypeParam(_) => None,
53 Definition::LifetimeParam(_) => None,
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),
69 hir::ModuleDef::Variant(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,
76 Definition::SelfType(_) => return None,
77 Definition::Local(it) => it.name(db)?,
78 Definition::TypeParam(it) => it.name(db),
79 Definition::LifetimeParam(it) => it.name(db),
88 Definition(Definition),
89 /// `None` in `if let None = Some(82) {}`.
90 ConstReference(Definition),
91 /// `field` in `if let Foo { field } = foo`.
94 field_ref: Definition,
99 /// `Definition` defined by this name.
100 pub fn defined(self, db: &dyn HirDatabase) -> Option<Definition> {
101 let res = match self {
102 NameClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()),
103 NameClass::Definition(it) => it,
104 NameClass::ConstReference(_) => return None,
105 NameClass::PatFieldShorthand { local_def, field_ref: _ } => {
106 Definition::Local(local_def)
112 /// `Definition` referenced or defined by this name.
113 pub fn referenced_or_defined(self, db: &dyn HirDatabase) -> Definition {
115 NameClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()),
116 NameClass::Definition(it) | NameClass::ConstReference(it) => it,
117 NameClass::PatFieldShorthand { local_def: _, field_ref } => field_ref,
121 pub fn classify(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<NameClass> {
122 let _p = profile::span("classify_name");
124 let parent = name.syntax().parent()?;
126 if let Some(bind_pat) = ast::IdentPat::cast(parent.clone()) {
127 if let Some(def) = sema.resolve_bind_pat_to_const(&bind_pat) {
128 return Some(NameClass::ConstReference(Definition::ModuleDef(def)));
135 if let Some(use_tree) = it.syntax().parent().and_then(ast::UseTree::cast) {
136 let path = use_tree.path()?;
137 let path_segment = path.segment()?;
138 let name_ref_class = path_segment
140 // The rename might be from a `self` token, so fallback to the name higher
143 if path_segment.self_token().is_none() {
147 let use_tree = use_tree
151 // Skip over UseTreeList
152 .and_then(SyntaxNode::parent)
153 .and_then(ast::UseTree::cast)?;
154 let path = use_tree.path()?;
155 let path_segment = path.segment()?;
156 path_segment.name_ref()
158 .and_then(|name_ref| NameRefClass::classify(sema, &name_ref))?;
160 Some(NameClass::Definition(name_ref_class.referenced(sema.db)))
162 let extern_crate = it.syntax().parent().and_then(ast::ExternCrate::cast)?;
163 let resolved = sema.resolve_extern_crate(&extern_crate)?;
164 Some(NameClass::ExternCrate(resolved))
167 ast::IdentPat(it) => {
168 let local = sema.to_def(&it)?;
170 if let Some(record_pat_field) = it.syntax().parent().and_then(ast::RecordPatField::cast) {
171 if record_pat_field.name_ref().is_none() {
172 if let Some(field) = sema.resolve_record_pat_field(&record_pat_field) {
173 let field = Definition::Field(field);
174 return Some(NameClass::PatFieldShorthand { local_def: local, field_ref: field });
179 Some(NameClass::Definition(Definition::Local(local)))
181 ast::RecordField(it) => {
182 let field: hir::Field = sema.to_def(&it)?;
183 Some(NameClass::Definition(Definition::Field(field)))
186 let def = sema.to_def(&it)?;
187 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
190 let def: hir::Struct = sema.to_def(&it)?;
191 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
194 let def: hir::Union = sema.to_def(&it)?;
195 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
198 let def: hir::Enum = sema.to_def(&it)?;
199 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
202 let def: hir::Trait = sema.to_def(&it)?;
203 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
206 let def: hir::Static = sema.to_def(&it)?;
207 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
209 ast::Variant(it) => {
210 let def: hir::Variant = sema.to_def(&it)?;
211 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
214 let def: hir::Function = sema.to_def(&it)?;
215 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
218 let def: hir::Const = sema.to_def(&it)?;
219 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
221 ast::TypeAlias(it) => {
222 let def: hir::TypeAlias = sema.to_def(&it)?;
223 Some(NameClass::Definition(Definition::ModuleDef(def.into())))
225 ast::MacroRules(it) => {
226 let def = sema.to_def(&it)?;
227 Some(NameClass::Definition(Definition::Macro(def)))
229 ast::TypeParam(it) => {
230 let def = sema.to_def(&it)?;
231 Some(NameClass::Definition(Definition::TypeParam(def)))
238 pub fn classify_lifetime(
239 sema: &Semantics<RootDatabase>,
240 lifetime: &ast::Lifetime,
241 ) -> Option<NameClass> {
242 let _p = profile::span("classify_lifetime").detail(|| lifetime.to_string());
243 let parent = lifetime.syntax().parent()?;
247 ast::LifetimeParam(it) => {
248 let def = sema.to_def(&it)?;
249 Some(NameClass::Definition(Definition::LifetimeParam(def)))
251 ast::Label(_it) => None,
259 pub enum NameRefClass {
261 Definition(Definition),
262 FieldShorthand { local_ref: Local, field_ref: Definition },
266 /// `Definition`, which this name refers to.
267 pub fn referenced(self, db: &dyn HirDatabase) -> Definition {
269 NameRefClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()),
270 NameRefClass::Definition(def) => def,
271 NameRefClass::FieldShorthand { local_ref, field_ref: _ } => {
272 // FIXME: this is inherently ambiguous -- this name refers to
273 // two different defs....
274 Definition::Local(local_ref)
279 // Note: we don't have unit-tests for this rather important function.
280 // It is primarily exercised via goto definition tests in `ide`.
282 sema: &Semantics<RootDatabase>,
283 name_ref: &ast::NameRef,
284 ) -> Option<NameRefClass> {
285 let _p = profile::span("classify_name_ref").detail(|| name_ref.to_string());
287 let parent = name_ref.syntax().parent()?;
289 if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) {
290 if let Some(func) = sema.resolve_method_call(&method_call) {
291 return Some(NameRefClass::Definition(Definition::ModuleDef(func.into())));
295 if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
296 if let Some(field) = sema.resolve_field(&field_expr) {
297 return Some(NameRefClass::Definition(Definition::Field(field)));
301 if let Some(record_field) = ast::RecordExprField::for_field_name(name_ref) {
302 if let Some((field, local)) = sema.resolve_record_field(&record_field) {
303 let field = Definition::Field(field);
304 let res = match local {
305 None => NameRefClass::Definition(field),
307 NameRefClass::FieldShorthand { field_ref: field, local_ref: local }
314 if let Some(record_pat_field) = ast::RecordPatField::cast(parent.clone()) {
315 if let Some(field) = sema.resolve_record_pat_field(&record_pat_field) {
316 let field = Definition::Field(field);
317 return Some(NameRefClass::Definition(field));
321 if ast::AssocTypeArg::cast(parent.clone()).is_some() {
322 // `Trait<Assoc = Ty>`
324 let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?;
325 let resolved = sema.resolve_path(&path)?;
326 if let PathResolution::Def(ModuleDef::Trait(tr)) = resolved {
330 .filter_map(|assoc| match assoc {
331 hir::AssocItem::TypeAlias(it) => Some(*it),
334 .find(|alias| alias.name(sema.db).to_string() == **name_ref.text())
336 return Some(NameRefClass::Definition(Definition::ModuleDef(
337 ModuleDef::TypeAlias(ty),
343 if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) {
344 if let Some(path) = macro_call.path() {
345 if path.qualifier().is_none() {
346 // Only use this to resolve single-segment macro calls like `foo!()`. Multi-segment
347 // paths are handled below (allowing `log<|>::info!` to resolve to the log crate).
348 if let Some(macro_def) = sema.resolve_macro_call(¯o_call) {
349 return Some(NameRefClass::Definition(Definition::Macro(macro_def)));
355 if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) {
356 if let Some(resolved) = sema.resolve_path(&path) {
357 return Some(NameRefClass::Definition(resolved.into()));
361 let extern_crate = ast::ExternCrate::cast(parent)?;
362 let resolved = sema.resolve_extern_crate(&extern_crate)?;
363 Some(NameRefClass::ExternCrate(resolved))
366 pub fn classify_lifetime(
367 sema: &Semantics<RootDatabase>,
368 lifetime: &ast::Lifetime,
369 ) -> Option<NameRefClass> {
370 let _p = profile::span("classify_lifetime_ref").detail(|| lifetime.to_string());
371 let parent = lifetime.syntax().parent()?;
372 match parent.kind() {
373 SyntaxKind::LIFETIME_ARG
374 | SyntaxKind::SELF_PARAM
375 | SyntaxKind::TYPE_BOUND
376 | SyntaxKind::WHERE_PRED
377 | SyntaxKind::REF_TYPE => sema
378 .resolve_lifetime_param(lifetime)
379 .map(Definition::LifetimeParam)
380 .map(NameRefClass::Definition),
381 // lifetime bounds, as in the 'b in 'a: 'b aren't wrapped in TypeBound nodes so we gotta check
382 // if our lifetime is in a LifetimeParam without being the constrained lifetime
383 _ if ast::LifetimeParam::cast(parent).and_then(|param| param.lifetime()).as_ref()
386 sema.resolve_lifetime_param(lifetime)
387 .map(Definition::LifetimeParam)
388 .map(NameRefClass::Definition)
390 SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => None,
396 impl From<PathResolution> for Definition {
397 fn from(path_resolution: PathResolution) -> Self {
398 match path_resolution {
399 PathResolution::Def(def) => Definition::ModuleDef(def),
400 PathResolution::AssocItem(item) => {
401 let def = match item {
402 hir::AssocItem::Function(it) => it.into(),
403 hir::AssocItem::Const(it) => it.into(),
404 hir::AssocItem::TypeAlias(it) => it.into(),
406 Definition::ModuleDef(def)
408 PathResolution::Local(local) => Definition::Local(local),
409 PathResolution::TypeParam(par) => Definition::TypeParam(par),
410 PathResolution::Macro(def) => Definition::Macro(def),
411 PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def),