From 121aa35f12d282066651d906ea9a8b2da8209605 Mon Sep 17 00:00:00 2001 From: Ekaterina Babshukova Date: Fri, 4 Oct 2019 03:20:14 +0300 Subject: return Declaration from classify_name_ref --- crates/ra_ide_api/src/name_kind.rs | 432 +++++++++++++++++++++++-------------- 1 file changed, 266 insertions(+), 166 deletions(-) (limited to 'crates/ra_ide_api/src/name_kind.rs') diff --git a/crates/ra_ide_api/src/name_kind.rs b/crates/ra_ide_api/src/name_kind.rs index 5dc6b1a13..31f6f277d 100644 --- a/crates/ra_ide_api/src/name_kind.rs +++ b/crates/ra_ide_api/src/name_kind.rs @@ -1,101 +1,127 @@ //! FIXME: write short doc here -use hir::{Either, FromSource, HasSource}; +use hir::{ + db::AstDatabase, Adt, AssocItem, DefWithBody, Either, EnumVariant, FromSource, HasSource, + HirFileId, MacroDef, ModuleDef, ModuleSource, Path, PathResolution, SourceAnalyzer, + StructField, Ty, VariantDef, +}; use ra_db::FileId; use ra_syntax::{ast, ast::VisibilityOwner, AstNode, AstPtr}; -use test_utils::tested_by; use crate::db::RootDatabase; +pub enum NameKind { + Macro(MacroDef), + FieldAccess(StructField), + AssocItem(AssocItem), + Def(ModuleDef), + SelfType(Ty), + Pat((DefWithBody, AstPtr)), + SelfParam(AstPtr), + GenericParam(u32), +} + pub(crate) struct Declaration { visibility: Option, - container: hir::ModuleSource, + container: ModuleSource, pub item: NameKind, } -pub(crate) enum NameKind { - Macro(hir::MacroDef), - FieldAccess(hir::StructField), - AssocItem(hir::AssocItem), - Def(hir::ModuleDef), - SelfType(hir::Ty), - Pat(AstPtr), - SelfParam(AstPtr), - GenericParam(u32), +trait HasDeclaration { + type Def; + type Ref; + + fn declaration(self, db: &RootDatabase) -> Declaration; + fn from_def(db: &RootDatabase, file_id: HirFileId, def: Self::Def) -> Option; + fn from_ref( + db: &RootDatabase, + analyzer: &SourceAnalyzer, + refer: Self::Ref, + ) -> Option; +} + +macro_rules! match_ast { + (match $node:ident { + $( ast::$ast:ident($it:ident) => $res:block, )* + _ => $catch_all:expr, + }) => {{ + $( if let Some($it) = ast::$ast::cast($node.clone()) $res else )* + { $catch_all } + }}; } pub(crate) fn classify_name_ref( db: &RootDatabase, - analyzer: &hir::SourceAnalyzer, + file_id: FileId, + analyzer: &SourceAnalyzer, name_ref: &ast::NameRef, -) -> Option { - use NameKind::*; - - // Check if it is a method - if let Some(method_call) = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast) { - tested_by!(goto_definition_works_for_methods); - if let Some(func) = analyzer.resolve_method_call(&method_call) { - return Some(AssocItem(func.into())); +) -> Option { + let parent = name_ref.syntax().parent()?; + match_ast! { + match parent { + ast::MethodCallExpr(it) => { + return AssocItem::from_ref(db, analyzer, it); + }, + ast::FieldExpr(it) => { + if let Some(field) = analyzer.resolve_field(&it) { + return Some(field.declaration(db)); + } + }, + ast::RecordField(it) => { + if let Some(record_lit) = it.syntax().ancestors().find_map(ast::RecordLit::cast) { + let variant_def = analyzer.resolve_record_literal(&record_lit)?; + let hir_path = Path::from_name_ref(name_ref); + let hir_name = hir_path.as_ident()?; + let field = variant_def.field(db, hir_name)?; + return Some(field.declaration(db)); + } + }, + _ => (), } } - // It could be a macro call - if let Some(macro_call) = name_ref - .syntax() - .parent() - .and_then(|node| node.parent()) - .and_then(|node| node.parent()) - .and_then(ast::MacroCall::cast) - { - tested_by!(goto_definition_works_for_macros); - if let Some(mac) = analyzer.resolve_macro_call(db, ¯o_call) { - return Some(Macro(mac)); + let file_id = file_id.into(); + let container = parent.ancestors().find_map(|node| { + if let Some(it) = ast::Module::cast(node.clone()) { + Some(ModuleSource::Module(it)) + } else if let Some(it) = ast::SourceFile::cast(node.clone()) { + Some(ModuleSource::SourceFile(it)) + } else { + None } - } - - // It could also be a field access - if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::FieldExpr::cast) { - tested_by!(goto_definition_works_for_fields); - if let Some(field) = analyzer.resolve_field(&field_expr) { - return Some(FieldAccess(field)); - }; - } - - // It could also be a named field - if let Some(field_expr) = name_ref.syntax().parent().and_then(ast::RecordField::cast) { - tested_by!(goto_definition_works_for_record_fields); + })?; - if let Some(record_lit) = field_expr.syntax().ancestors().find_map(ast::RecordLit::cast) { - let variant_def = analyzer.resolve_record_literal(&record_lit)?; - let hir_path = hir::Path::from_name_ref(name_ref); - let hir_name = hir_path.as_ident()?; - let field = variant_def.field(db, hir_name)?; - return Some(FieldAccess(field)); + if let Some(macro_call) = + parent.parent().and_then(|node| node.parent()).and_then(ast::MacroCall::cast) + { + if let Some(mac) = analyzer.resolve_macro_call(db, ¯o_call) { + return Some(Declaration { item: NameKind::Macro(mac), container, visibility: None }); } } // General case, a path or a local: - if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) { - if let Some(resolved) = analyzer.resolve_path(db, &path) { - return match resolved { - hir::PathResolution::Def(def) => Some(Def(def)), - hir::PathResolution::LocalBinding(Either::A(pat)) => Some(Pat(pat)), - hir::PathResolution::LocalBinding(Either::B(par)) => Some(SelfParam(par)), - hir::PathResolution::GenericParam(par) => { - // FIXME: get generic param def - Some(GenericParam(par)) - } - hir::PathResolution::Macro(def) => Some(Macro(def)), - hir::PathResolution::SelfType(impl_block) => { - let ty = impl_block.target_ty(db); - Some(SelfType(ty)) - } - hir::PathResolution::AssocItem(assoc) => Some(AssocItem(assoc)), - }; + let path = name_ref.syntax().ancestors().find_map(ast::Path::cast)?; + let resolved = analyzer.resolve_path(db, &path)?; + match resolved { + PathResolution::Def(def) => Some(def.declaration(db)), + PathResolution::LocalBinding(Either::A(pat)) => decl_from_pat(db, file_id, pat), + PathResolution::LocalBinding(Either::B(par)) => { + Some(Declaration { item: NameKind::SelfParam(par), container, visibility: None }) + } + PathResolution::GenericParam(par) => { + // FIXME: get generic param def + Some(Declaration { item: NameKind::GenericParam(par), container, visibility: None }) + } + PathResolution::Macro(def) => { + Some(Declaration { item: NameKind::Macro(def), container, visibility: None }) } + PathResolution::SelfType(impl_block) => { + let ty = impl_block.target_ty(db); + let container = impl_block.module().definition_source(db).ast; + Some(Declaration { item: NameKind::SelfType(ty), container, visibility: None }) + } + PathResolution::AssocItem(assoc) => Some(assoc.declaration(db)), } - - None } pub(crate) fn classify_name( @@ -103,114 +129,188 @@ pub(crate) fn classify_name( file_id: FileId, name: &ast::Name, ) -> Option { - use NameKind::*; - let parent = name.syntax().parent()?; let file_id = file_id.into(); - macro_rules! match_ast { - (match $node:ident { - $( ast::$ast:ident($it:ident) => $res:block, )* - _ => $catch_all:block, - }) => {{ - $( if let Some($it) = ast::$ast::cast($node.clone()) $res else )* - $catch_all - }}; - } - - let container = parent.ancestors().find_map(|n| { - match_ast! { - match n { - ast::Module(it) => { Some(hir::ModuleSource::Module(it)) }, - ast::SourceFile(it) => { Some(hir::ModuleSource::SourceFile(it)) }, - _ => { None }, - } - } - })?; - - // FIXME: add ast::MacroCall(it) - let (item, visibility) = match_ast! { + match_ast! { match parent { ast::BindPat(it) => { - let pat = AstPtr::new(&it); - (Pat(pat), None) + decl_from_pat(db, file_id, AstPtr::new(&it)) }, ast::RecordFieldDef(it) => { - let src = hir::Source { file_id, ast: hir::FieldSource::Named(it) }; - let field = hir::StructField::from_source(db, src)?; - let visibility = match field.parent_def(db) { - hir::VariantDef::Struct(s) => s.source(db).ast.visibility(), - hir::VariantDef::EnumVariant(e) => e.source(db).ast.parent_enum().visibility(), - }; - (FieldAccess(field), visibility) - }, - ast::FnDef(it) => { - if parent.parent().and_then(ast::ItemList::cast).is_some() { - let src = hir::Source { file_id, ast: ast::ImplItem::from(it.clone()) }; - let item = hir::AssocItem::from_source(db, src)?; - (AssocItem(item), it.visibility()) - } else { - let src = hir::Source { file_id, ast: it.clone() }; - let def = hir::Function::from_source(db, src)?; - (Def(def.into()), it.visibility()) - } - }, - ast::ConstDef(it) => { - if parent.parent().and_then(ast::ItemList::cast).is_some() { - let src = hir::Source { file_id, ast: ast::ImplItem::from(it.clone()) }; - let item = hir::AssocItem::from_source(db, src)?; - (AssocItem(item), it.visibility()) - } else { - let src = hir::Source { file_id, ast: it.clone() }; - let def = hir::Const::from_source(db, src)?; - (Def(def.into()), it.visibility()) - } - }, - ast::TypeAliasDef(it) => { - if parent.parent().and_then(ast::ItemList::cast).is_some() { - let src = hir::Source { file_id, ast: ast::ImplItem::from(it.clone()) }; - let item = hir::AssocItem::from_source(db, src)?; - (AssocItem(item), it.visibility()) - } else { - let src = hir::Source { file_id, ast: it.clone() }; - let def = hir::TypeAlias::from_source(db, src)?; - (Def(def.into()), it.visibility()) - } - }, - ast::Module(it) => { - let src = hir::Source { file_id, ast: hir::ModuleSource::Module(it.clone()) }; - let def = hir::Module::from_definition(db, src)?; - (Def(def.into()), it.visibility()) - }, - ast::StructDef(it) => { - let src = hir::Source { file_id, ast: it.clone() }; - let def = hir::Struct::from_source(db, src)?; - (Def(def.into()), it.visibility()) + StructField::from_def(db, file_id, it) }, - ast::EnumDef(it) => { - let src = hir::Source { file_id, ast: it.clone() }; - let def = hir::Enum::from_source(db, src)?; - (Def(def.into()), it.visibility()) - }, - ast::TraitDef(it) => { - let src = hir::Source { file_id, ast: it.clone() }; - let def = hir::Trait::from_source(db, src)?; - (Def(def.into()), it.visibility()) - }, - ast::StaticDef(it) => { - let src = hir::Source { file_id, ast: it.clone() }; - let def = hir::Static::from_source(db, src)?; - (Def(def.into()), it.visibility()) + ast::ImplItem(it) => { + AssocItem::from_def(db, file_id, it.clone()).or_else(|| { + match it { + ast::ImplItem::FnDef(f) => ModuleDef::from_def(db, file_id, f.into()), + ast::ImplItem::ConstDef(c) => ModuleDef::from_def(db, file_id, c.into()), + ast::ImplItem::TypeAliasDef(a) => ModuleDef::from_def(db, file_id, a.into()), + } + }) }, ast::EnumVariant(it) => { let src = hir::Source { file_id, ast: it.clone() }; - let def = hir::EnumVariant::from_source(db, src)?; - (Def(def.into()), it.parent_enum().visibility()) + let def: ModuleDef = EnumVariant::from_source(db, src)?.into(); + Some(def.declaration(db)) }, - _ => { - return None; + ast::ModuleItem(it) => { + ModuleDef::from_def(db, file_id, it) }, + _ => None, } - }; - Some(Declaration { item, container, visibility }) + } +} + +fn decl_from_pat( + db: &RootDatabase, + file_id: HirFileId, + pat: AstPtr, +) -> Option { + let root = db.parse_or_expand(file_id)?; + // FIXME: use match_ast! + let def = pat.to_node(&root).syntax().ancestors().find_map(|node| { + if let Some(it) = ast::FnDef::cast(node.clone()) { + let src = hir::Source { file_id, ast: it }; + Some(hir::Function::from_source(db, src)?.into()) + } else if let Some(it) = ast::ConstDef::cast(node.clone()) { + let src = hir::Source { file_id, ast: it }; + Some(hir::Const::from_source(db, src)?.into()) + } else if let Some(it) = ast::StaticDef::cast(node.clone()) { + let src = hir::Source { file_id, ast: it }; + Some(hir::Static::from_source(db, src)?.into()) + } else { + None + } + })?; + let item = NameKind::Pat((def, pat)); + let container = def.module(db).definition_source(db).ast; + Some(Declaration { item, container, visibility: None }) +} + +impl HasDeclaration for StructField { + type Def = ast::RecordFieldDef; + type Ref = ast::FieldExpr; + + fn declaration(self, db: &RootDatabase) -> Declaration { + let item = NameKind::FieldAccess(self); + let parent = self.parent_def(db); + let container = parent.module(db).definition_source(db).ast; + let visibility = match parent { + VariantDef::Struct(s) => s.source(db).ast.visibility(), + VariantDef::EnumVariant(e) => e.source(db).ast.parent_enum().visibility(), + }; + Declaration { item, container, visibility } + } + + fn from_def(db: &RootDatabase, file_id: HirFileId, def: Self::Def) -> Option { + let src = hir::Source { file_id, ast: hir::FieldSource::Named(def) }; + let field = StructField::from_source(db, src)?; + Some(field.declaration(db)) + } + + fn from_ref( + db: &RootDatabase, + analyzer: &SourceAnalyzer, + refer: Self::Ref, + ) -> Option { + let field = analyzer.resolve_field(&refer)?; + Some(field.declaration(db)) + } +} + +impl HasDeclaration for AssocItem { + type Def = ast::ImplItem; + type Ref = ast::MethodCallExpr; + + fn declaration(self, db: &RootDatabase) -> Declaration { + let item = NameKind::AssocItem(self); + let container = self.module(db).definition_source(db).ast; + let visibility = match self { + AssocItem::Function(f) => f.source(db).ast.visibility(), + AssocItem::Const(c) => c.source(db).ast.visibility(), + AssocItem::TypeAlias(a) => a.source(db).ast.visibility(), + }; + Declaration { item, container, visibility } + } + + fn from_def(db: &RootDatabase, file_id: HirFileId, def: Self::Def) -> Option { + let src = hir::Source { file_id, ast: def }; + let item = AssocItem::from_source(db, src)?; + Some(item.declaration(db)) + } + + fn from_ref( + db: &RootDatabase, + analyzer: &SourceAnalyzer, + refer: Self::Ref, + ) -> Option { + let func: AssocItem = analyzer.resolve_method_call(&refer)?.into(); + Some(func.declaration(db)) + } } + +impl HasDeclaration for ModuleDef { + type Def = ast::ModuleItem; + type Ref = ast::Path; + + fn declaration(self, db: &RootDatabase) -> Declaration { + // FIXME: use macro + let (container, visibility) = match self { + ModuleDef::Module(it) => { + let container = + it.parent(db).or_else(|| Some(it)).unwrap().definition_source(db).ast; + let visibility = it.declaration_source(db).and_then(|s| s.ast.visibility()); + (container, visibility) + } + ModuleDef::EnumVariant(it) => { + let container = it.module(db).definition_source(db).ast; + let visibility = it.source(db).ast.parent_enum().visibility(); + (container, visibility) + } + ModuleDef::Function(it) => { + (it.module(db).definition_source(db).ast, it.source(db).ast.visibility()) + } + ModuleDef::Const(it) => { + (it.module(db).definition_source(db).ast, it.source(db).ast.visibility()) + } + ModuleDef::Static(it) => { + (it.module(db).definition_source(db).ast, it.source(db).ast.visibility()) + } + ModuleDef::Trait(it) => { + (it.module(db).definition_source(db).ast, it.source(db).ast.visibility()) + } + ModuleDef::TypeAlias(it) => { + (it.module(db).definition_source(db).ast, it.source(db).ast.visibility()) + } + ModuleDef::Adt(Adt::Struct(it)) => { + (it.module(db).definition_source(db).ast, it.source(db).ast.visibility()) + } + ModuleDef::Adt(Adt::Union(it)) => { + (it.module(db).definition_source(db).ast, it.source(db).ast.visibility()) + } + ModuleDef::Adt(Adt::Enum(it)) => { + (it.module(db).definition_source(db).ast, it.source(db).ast.visibility()) + } + ModuleDef::BuiltinType(..) => unreachable!(), + }; + let item = NameKind::Def(self); + Declaration { item, container, visibility } + } + + fn from_def(db: &RootDatabase, file_id: HirFileId, def: Self::Def) -> Option { + let src = hir::Source { file_id, ast: def }; + let def = ModuleDef::from_source(db, src)?; + Some(def.declaration(db)) + } + + fn from_ref( + db: &RootDatabase, + analyzer: &SourceAnalyzer, + refer: Self::Ref, + ) -> Option { + None + } +} + +// FIXME: impl HasDeclaration for hir::MacroDef -- cgit v1.2.3