rust-analyzer/crates/ide_db/src/defs.rs

415 lines
17 KiB
Rust
Raw Normal View History

2020-02-06 15:23:28 +00:00
//! `NameDefinition` keeps information about the element we want to search references for.
//! The element is represented by `NameKind`. It's located inside some `container` and
//! has a `visibility`, which defines a search scope.
//! Note that the reference search is possible for not all of the classified items.
2020-02-06 15:24:14 +00:00
// FIXME: this badly needs rename/rewrite (matklad, 2020-02-06).
2020-02-06 15:23:28 +00:00
use hir::{
db::HirDatabase, Crate, Field, HasVisibility, Impl, LifetimeParam, Local, MacroDef, Module,
2020-12-16 20:35:15 +00:00
ModuleDef, Name, PathResolution, Semantics, TypeParam, Visibility,
2020-02-06 15:23:28 +00:00
};
2020-08-12 16:26:51 +00:00
use syntax::{
ast::{self, AstNode},
2020-12-16 20:35:15 +00:00
match_ast, SyntaxKind, SyntaxNode,
2020-02-06 15:23:28 +00:00
};
use crate::RootDatabase;
2020-03-03 17:36:39 +00:00
// FIXME: a more precise name would probably be `Symbol`?
2020-06-03 11:15:54 +00:00
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
2020-03-03 17:36:39 +00:00
pub enum Definition {
2020-02-06 15:23:28 +00:00
Macro(MacroDef),
2020-04-25 12:23:34 +00:00
Field(Field),
2020-02-07 13:26:59 +00:00
ModuleDef(ModuleDef),
SelfType(Impl),
2020-02-06 15:23:28 +00:00
Local(Local),
TypeParam(TypeParam),
2020-12-16 20:35:15 +00:00
LifetimeParam(LifetimeParam),
// FIXME: Label
2020-02-06 15:23:28 +00:00
}
2020-03-03 17:36:39 +00:00
impl Definition {
pub fn module(&self, db: &RootDatabase) -> Option<Module> {
2020-02-19 13:56:22 +00:00
match self {
2020-03-03 17:36:39 +00:00
Definition::Macro(it) => it.module(db),
2020-04-25 12:23:34 +00:00
Definition::Field(it) => Some(it.parent_def(db).module(db)),
2020-03-03 17:36:39 +00:00
Definition::ModuleDef(it) => it.module(db),
Definition::SelfType(it) => Some(it.module(db)),
Definition::Local(it) => Some(it.module(db)),
Definition::TypeParam(it) => Some(it.module(db)),
2020-12-16 20:35:15 +00:00
Definition::LifetimeParam(it) => Some(it.module(db)),
}
}
2020-02-19 13:45:49 +00:00
2020-03-24 20:45:42 +00:00
pub fn visibility(&self, db: &RootDatabase) -> Option<Visibility> {
2020-02-19 13:56:22 +00:00
match self {
2020-03-03 17:36:39 +00:00
Definition::Macro(_) => None,
2020-04-25 12:23:34 +00:00
Definition::Field(sf) => Some(sf.visibility(db)),
2020-05-11 11:28:14 +00:00
Definition::ModuleDef(def) => def.definition_visibility(db),
2020-03-03 17:36:39 +00:00
Definition::SelfType(_) => None,
Definition::Local(_) => None,
Definition::TypeParam(_) => None,
2020-12-16 20:35:15 +00:00
Definition::LifetimeParam(_) => None,
2020-02-19 13:45:49 +00:00
}
}
2020-03-03 17:22:52 +00:00
pub fn name(&self, db: &RootDatabase) -> Option<Name> {
let name = match self {
2020-03-03 17:36:39 +00:00
Definition::Macro(it) => it.name(db)?,
2020-04-25 12:23:34 +00:00
Definition::Field(it) => it.name(db),
2020-03-03 17:36:39 +00:00
Definition::ModuleDef(def) => match def {
2020-03-03 17:22:52 +00:00
hir::ModuleDef::Module(it) => it.name(db)?,
hir::ModuleDef::Function(it) => it.name(db),
hir::ModuleDef::Adt(def) => match def {
hir::Adt::Struct(it) => it.name(db),
hir::Adt::Union(it) => it.name(db),
hir::Adt::Enum(it) => it.name(db),
},
hir::ModuleDef::EnumVariant(it) => it.name(db),
hir::ModuleDef::Const(it) => it.name(db)?,
hir::ModuleDef::Static(it) => it.name(db)?,
hir::ModuleDef::Trait(it) => it.name(db),
hir::ModuleDef::TypeAlias(it) => it.name(db),
hir::ModuleDef::BuiltinType(_) => return None,
},
2020-03-03 17:36:39 +00:00
Definition::SelfType(_) => return None,
Definition::Local(it) => it.name(db)?,
Definition::TypeParam(it) => it.name(db),
2020-12-16 20:35:15 +00:00
Definition::LifetimeParam(it) => it.name(db),
2020-03-03 17:22:52 +00:00
};
Some(name)
}
}
#[derive(Debug)]
pub enum NameClass {
ExternCrate(Crate),
2020-03-03 17:36:39 +00:00
Definition(Definition),
2020-10-15 15:18:07 +00:00
/// `None` in `if let None = Some(82) {}`.
2020-03-03 17:36:39 +00:00
ConstReference(Definition),
2020-10-15 15:18:07 +00:00
/// `field` in `if let Foo { field } = foo`.
PatFieldShorthand {
2020-10-15 15:18:07 +00:00
local_def: Local,
field_ref: Definition,
},
}
impl NameClass {
2020-10-15 15:33:32 +00:00
/// `Definition` defined by this name.
pub fn defined(self, db: &dyn HirDatabase) -> Option<Definition> {
let res = match self {
NameClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()),
NameClass::Definition(it) => it,
NameClass::ConstReference(_) => return None,
2020-10-15 15:18:07 +00:00
NameClass::PatFieldShorthand { local_def, field_ref: _ } => {
Definition::Local(local_def)
}
};
Some(res)
}
2020-10-15 15:33:32 +00:00
/// `Definition` referenced or defined by this name.
pub fn referenced_or_defined(self, db: &dyn HirDatabase) -> Definition {
match self {
NameClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()),
2020-03-03 17:36:39 +00:00
NameClass::Definition(it) | NameClass::ConstReference(it) => it,
2020-10-15 15:18:07 +00:00
NameClass::PatFieldShorthand { local_def: _, field_ref } => field_ref,
}
}
2020-10-15 15:27:50 +00:00
pub fn classify(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option<NameClass> {
let _p = profile::span("classify_name");
2020-10-15 15:27:50 +00:00
let parent = name.syntax().parent()?;
2020-10-15 15:27:50 +00:00
if let Some(bind_pat) = ast::IdentPat::cast(parent.clone()) {
if let Some(def) = sema.resolve_bind_pat_to_const(&bind_pat) {
return Some(NameClass::ConstReference(Definition::ModuleDef(def)));
}
2020-02-28 15:36:14 +00:00
}
2020-10-15 15:27:50 +00:00
match_ast! {
match parent {
ast::Rename(it) => {
if let Some(use_tree) = it.syntax().parent().and_then(ast::UseTree::cast) {
let path = use_tree.path()?;
let path_segment = path.segment()?;
let name_ref_class = path_segment
.name_ref()
// The rename might be from a `self` token, so fallback to the name higher
// in the use tree.
.or_else(||{
if path_segment.self_token().is_none() {
return None;
}
2020-10-15 15:27:50 +00:00
let use_tree = use_tree
.syntax()
.parent()
.as_ref()
// Skip over UseTreeList
.and_then(SyntaxNode::parent)
.and_then(ast::UseTree::cast)?;
let path = use_tree.path()?;
let path_segment = path.segment()?;
path_segment.name_ref()
})
.and_then(|name_ref| NameRefClass::classify(sema, &name_ref))?;
2020-10-15 15:33:32 +00:00
Some(NameClass::Definition(name_ref_class.referenced(sema.db)))
2020-10-15 15:27:50 +00:00
} else {
let extern_crate = it.syntax().parent().and_then(ast::ExternCrate::cast)?;
let resolved = sema.resolve_extern_crate(&extern_crate)?;
Some(NameClass::ExternCrate(resolved))
}
},
ast::IdentPat(it) => {
let local = sema.to_def(&it)?;
2020-10-15 15:27:50 +00:00
if let Some(record_pat_field) = it.syntax().parent().and_then(ast::RecordPatField::cast) {
if record_pat_field.name_ref().is_none() {
if let Some(field) = sema.resolve_record_pat_field(&record_pat_field) {
let field = Definition::Field(field);
return Some(NameClass::PatFieldShorthand { local_def: local, field_ref: field });
}
}
}
2020-06-06 19:16:59 +00:00
2020-10-15 15:27:50 +00:00
Some(NameClass::Definition(Definition::Local(local)))
},
ast::RecordField(it) => {
let field: hir::Field = sema.to_def(&it)?;
Some(NameClass::Definition(Definition::Field(field)))
},
ast::Module(it) => {
let def = sema.to_def(&it)?;
Some(NameClass::Definition(Definition::ModuleDef(def.into())))
},
ast::Struct(it) => {
let def: hir::Struct = sema.to_def(&it)?;
Some(NameClass::Definition(Definition::ModuleDef(def.into())))
},
ast::Union(it) => {
let def: hir::Union = sema.to_def(&it)?;
Some(NameClass::Definition(Definition::ModuleDef(def.into())))
},
ast::Enum(it) => {
let def: hir::Enum = sema.to_def(&it)?;
Some(NameClass::Definition(Definition::ModuleDef(def.into())))
},
ast::Trait(it) => {
let def: hir::Trait = sema.to_def(&it)?;
Some(NameClass::Definition(Definition::ModuleDef(def.into())))
},
ast::Static(it) => {
let def: hir::Static = sema.to_def(&it)?;
Some(NameClass::Definition(Definition::ModuleDef(def.into())))
},
ast::Variant(it) => {
let def: hir::EnumVariant = sema.to_def(&it)?;
Some(NameClass::Definition(Definition::ModuleDef(def.into())))
},
ast::Fn(it) => {
let def: hir::Function = sema.to_def(&it)?;
Some(NameClass::Definition(Definition::ModuleDef(def.into())))
},
ast::Const(it) => {
let def: hir::Const = sema.to_def(&it)?;
Some(NameClass::Definition(Definition::ModuleDef(def.into())))
},
ast::TypeAlias(it) => {
let def: hir::TypeAlias = sema.to_def(&it)?;
Some(NameClass::Definition(Definition::ModuleDef(def.into())))
},
2020-12-15 14:37:37 +00:00
ast::MacroRules(it) => {
2020-10-15 15:27:50 +00:00
let def = sema.to_def(&it)?;
Some(NameClass::Definition(Definition::Macro(def)))
},
ast::TypeParam(it) => {
let def = sema.to_def(&it)?;
Some(NameClass::Definition(Definition::TypeParam(def)))
},
_ => None,
}
2020-02-06 15:23:28 +00:00
}
}
2020-12-16 20:35:15 +00:00
pub fn classify_lifetime(
sema: &Semantics<RootDatabase>,
lifetime: &ast::Lifetime,
) -> Option<NameClass> {
let _p = profile::span("classify_lifetime").detail(|| lifetime.to_string());
let parent = lifetime.syntax().parent()?;
match_ast! {
match parent {
ast::LifetimeParam(it) => {
let def = sema.to_def(&it)?;
Some(NameClass::Definition(Definition::LifetimeParam(def)))
},
ast::Label(_it) => None,
_ => None,
}
}
}
2020-02-06 15:23:28 +00:00
}
#[derive(Debug)]
pub enum NameRefClass {
ExternCrate(Crate),
Definition(Definition),
2020-10-15 15:33:32 +00:00
FieldShorthand { local_ref: Local, field_ref: Definition },
}
impl NameRefClass {
2020-10-15 15:33:32 +00:00
/// `Definition`, which this name refers to.
pub fn referenced(self, db: &dyn HirDatabase) -> Definition {
match self {
NameRefClass::ExternCrate(krate) => Definition::ModuleDef(krate.root_module(db).into()),
NameRefClass::Definition(def) => def,
2020-10-15 15:33:32 +00:00
NameRefClass::FieldShorthand { local_ref, field_ref: _ } => {
// FIXME: this is inherently ambiguous -- this name refers to
// two different defs....
Definition::Local(local_ref)
}
}
}
2020-10-15 15:27:50 +00:00
// Note: we don't have unit-tests for this rather important function.
// It is primarily exercised via goto definition tests in `ide`.
pub fn classify(
sema: &Semantics<RootDatabase>,
name_ref: &ast::NameRef,
) -> Option<NameRefClass> {
2020-12-04 19:26:28 +00:00
let _p = profile::span("classify_name_ref").detail(|| name_ref.to_string());
2020-10-15 15:27:50 +00:00
let parent = name_ref.syntax().parent()?;
2020-10-15 15:27:50 +00:00
if let Some(method_call) = ast::MethodCallExpr::cast(parent.clone()) {
if let Some(func) = sema.resolve_method_call(&method_call) {
return Some(NameRefClass::Definition(Definition::ModuleDef(func.into())));
}
}
2020-10-15 15:27:50 +00:00
if let Some(field_expr) = ast::FieldExpr::cast(parent.clone()) {
if let Some(field) = sema.resolve_field(&field_expr) {
return Some(NameRefClass::Definition(Definition::Field(field)));
}
}
2020-10-15 15:27:50 +00:00
if let Some(record_field) = ast::RecordExprField::for_field_name(name_ref) {
if let Some((field, local)) = sema.resolve_record_field(&record_field) {
let field = Definition::Field(field);
let res = match local {
None => NameRefClass::Definition(field),
2020-10-15 15:33:32 +00:00
Some(local) => {
NameRefClass::FieldShorthand { field_ref: field, local_ref: local }
}
2020-10-15 15:27:50 +00:00
};
return Some(res);
}
}
2020-10-15 15:27:50 +00:00
if let Some(record_pat_field) = ast::RecordPatField::cast(parent.clone()) {
if let Some(field) = sema.resolve_record_pat_field(&record_pat_field) {
let field = Definition::Field(field);
return Some(NameRefClass::Definition(field));
}
}
2020-10-15 15:27:50 +00:00
if ast::AssocTypeArg::cast(parent.clone()).is_some() {
// `Trait<Assoc = Ty>`
// ^^^^^
let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?;
let resolved = sema.resolve_path(&path)?;
if let PathResolution::Def(ModuleDef::Trait(tr)) = resolved {
if let Some(ty) = tr
.items(sema.db)
.iter()
.filter_map(|assoc| match assoc {
hir::AssocItem::TypeAlias(it) => Some(*it),
_ => None,
})
.find(|alias| alias.name(sema.db).to_string() == **name_ref.text())
{
return Some(NameRefClass::Definition(Definition::ModuleDef(
ModuleDef::TypeAlias(ty),
)));
}
}
}
2020-10-15 15:27:50 +00:00
if let Some(macro_call) = parent.ancestors().find_map(ast::MacroCall::cast) {
if let Some(path) = macro_call.path() {
if path.qualifier().is_none() {
// Only use this to resolve single-segment macro calls like `foo!()`. Multi-segment
// paths are handled below (allowing `log<|>::info!` to resolve to the log crate).
if let Some(macro_def) = sema.resolve_macro_call(&macro_call) {
return Some(NameRefClass::Definition(Definition::Macro(macro_def)));
}
}
}
}
2020-10-15 15:27:50 +00:00
if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) {
if let Some(resolved) = sema.resolve_path(&path) {
return Some(NameRefClass::Definition(resolved.into()));
}
}
2020-10-15 15:27:50 +00:00
let extern_crate = ast::ExternCrate::cast(parent)?;
let resolved = sema.resolve_extern_crate(&extern_crate)?;
Some(NameRefClass::ExternCrate(resolved))
}
2020-12-16 20:35:15 +00:00
pub fn classify_lifetime(
sema: &Semantics<RootDatabase>,
lifetime: &ast::Lifetime,
) -> Option<NameRefClass> {
let _p = profile::span("classify_lifetime_ref").detail(|| lifetime.to_string());
let parent = lifetime.syntax().parent()?;
match parent.kind() {
SyntaxKind::LIFETIME_ARG
| SyntaxKind::SELF_PARAM
| SyntaxKind::TYPE_BOUND
| SyntaxKind::WHERE_PRED
| SyntaxKind::REF_TYPE => sema
.resolve_lifetime_param(lifetime)
.map(Definition::LifetimeParam)
.map(NameRefClass::Definition),
// lifetime bounds, as in the 'b in 'a: 'b aren't wrapped in TypeBound nodes so we gotta check
// if our lifetime is in a LifetimeParam without being the constrained lifetime
_ if ast::LifetimeParam::cast(parent).and_then(|param| param.lifetime()).as_ref()
!= Some(lifetime) =>
{
sema.resolve_lifetime_param(lifetime)
.map(Definition::LifetimeParam)
.map(NameRefClass::Definition)
}
SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => None,
_ => None,
}
}
}
impl From<PathResolution> for Definition {
fn from(path_resolution: PathResolution) -> Self {
match path_resolution {
PathResolution::Def(def) => Definition::ModuleDef(def),
PathResolution::AssocItem(item) => {
let def = match item {
hir::AssocItem::Function(it) => it.into(),
hir::AssocItem::Const(it) => it.into(),
hir::AssocItem::TypeAlias(it) => it.into(),
};
Definition::ModuleDef(def)
}
PathResolution::Local(local) => Definition::Local(local),
PathResolution::TypeParam(par) => Definition::TypeParam(par),
PathResolution::Macro(def) => Definition::Macro(def),
PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def),
}
}
}