From 5b03773fbeea55d86f64e5fb69a0d0f1d6a4f7e8 Mon Sep 17 00:00:00 2001 From: Ekaterina Babshukova Date: Thu, 10 Oct 2019 18:51:51 +0300 Subject: implement search of references --- crates/ra_ide_api/src/name_kind.rs | 83 ++++++++++++++++++----------------- crates/ra_ide_api/src/references.rs | 50 +++++++++++---------- crates/ra_ide_api/src/search_scope.rs | 77 ++++++++++++++++++++++++++------ 3 files changed, 134 insertions(+), 76 deletions(-) (limited to 'crates/ra_ide_api') diff --git a/crates/ra_ide_api/src/name_kind.rs b/crates/ra_ide_api/src/name_kind.rs index 6d1eb153f..8eef540f6 100644 --- a/crates/ra_ide_api/src/name_kind.rs +++ b/crates/ra_ide_api/src/name_kind.rs @@ -10,6 +10,7 @@ use ra_syntax::{ast, ast::VisibilityOwner, AstNode, AstPtr}; use crate::db::RootDatabase; +#[derive(PartialEq, Eq)] pub enum NameKind { Macro(MacroDef), FieldAccess(StructField), @@ -21,23 +22,24 @@ pub enum NameKind { GenericParam(u32), } -pub(crate) struct Declaration { +#[derive(PartialEq, Eq)] +pub(crate) struct Definition { pub visibility: Option, pub container: Module, pub item: NameKind, } -trait HasDeclaration { +trait HasDefinition { type Def; type Ref; - fn declaration(self, db: &RootDatabase) -> Declaration; - fn from_def(db: &RootDatabase, file_id: HirFileId, def: Self::Def) -> Option; + fn definition(self, db: &RootDatabase) -> Definition; + fn from_def(db: &RootDatabase, file_id: HirFileId, def: Self::Def) -> Option; fn from_ref( db: &RootDatabase, analyzer: &SourceAnalyzer, refer: Self::Ref, - ) -> Option; + ) -> Option; } macro_rules! match_ast { @@ -55,7 +57,7 @@ pub(crate) fn classify_name_ref( file_id: FileId, analyzer: &SourceAnalyzer, name_ref: &ast::NameRef, -) -> Option { +) -> Option { let parent = name_ref.syntax().parent()?; match_ast! { match parent { @@ -64,7 +66,7 @@ pub(crate) fn classify_name_ref( }, ast::FieldExpr(it) => { if let Some(field) = analyzer.resolve_field(&it) { - return Some(field.declaration(db)); + return Some(field.definition(db)); } }, ast::RecordField(it) => { @@ -73,7 +75,7 @@ pub(crate) fn classify_name_ref( 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)); + return Some(field.definition(db)); } }, _ => (), @@ -83,12 +85,13 @@ pub(crate) fn classify_name_ref( let ast = ModuleSource::from_child_node(db, file_id, &parent); let file_id = file_id.into(); let container = Module::from_definition(db, Source { file_id, ast })?; + let visibility = None; 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 }); + return Some(Definition { item: NameKind::Macro(mac), container, visibility }); } } @@ -96,24 +99,24 @@ pub(crate) fn classify_name_ref( 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::Def(def) => Some(def.definition(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 }) + Some(Definition { item: NameKind::SelfParam(par), container, visibility }) } PathResolution::GenericParam(par) => { // FIXME: get generic param def - Some(Declaration { item: NameKind::GenericParam(par), container, visibility: None }) + Some(Definition { item: NameKind::GenericParam(par), container, visibility }) } PathResolution::Macro(def) => { - Some(Declaration { item: NameKind::Macro(def), container, visibility: None }) + Some(Definition { item: NameKind::Macro(def), container, visibility }) } PathResolution::SelfType(impl_block) => { let ty = impl_block.target_ty(db); let container = impl_block.module(); - Some(Declaration { item: NameKind::SelfType(ty), container, visibility: None }) + Some(Definition { item: NameKind::SelfType(ty), container, visibility }) } - PathResolution::AssocItem(assoc) => Some(assoc.declaration(db)), + PathResolution::AssocItem(assoc) => Some(assoc.definition(db)), } } @@ -121,7 +124,7 @@ pub(crate) fn classify_name( db: &RootDatabase, file_id: FileId, name: &ast::Name, -) -> Option { +) -> Option { let parent = name.syntax().parent()?; let file_id = file_id.into(); @@ -145,7 +148,7 @@ pub(crate) fn classify_name( ast::EnumVariant(it) => { let src = hir::Source { file_id, ast: it.clone() }; let def: ModuleDef = EnumVariant::from_source(db, src)?.into(); - Some(def.declaration(db)) + Some(def.definition(db)) }, ast::ModuleItem(it) => { ModuleDef::from_def(db, file_id, it) @@ -159,7 +162,7 @@ fn decl_from_pat( db: &RootDatabase, file_id: HirFileId, pat: AstPtr, -) -> Option { +) -> Option { let root = db.parse_or_expand(file_id)?; // FIXME: use match_ast! let def = pat.to_node(&root).syntax().ancestors().find_map(|node| { @@ -178,14 +181,14 @@ fn decl_from_pat( })?; let item = NameKind::Pat((def, pat)); let container = def.module(db); - Some(Declaration { item, container, visibility: None }) + Some(Definition { item, container, visibility: None }) } -impl HasDeclaration for StructField { +impl HasDefinition for StructField { type Def = ast::RecordFieldDef; type Ref = ast::FieldExpr; - fn declaration(self, db: &RootDatabase) -> Declaration { + fn definition(self, db: &RootDatabase) -> Definition { let item = NameKind::FieldAccess(self); let parent = self.parent_def(db); let container = parent.module(db); @@ -193,30 +196,30 @@ impl HasDeclaration for StructField { VariantDef::Struct(s) => s.source(db).ast.visibility(), VariantDef::EnumVariant(e) => e.source(db).ast.parent_enum().visibility(), }; - Declaration { item, container, visibility } + Definition { item, container, visibility } } - fn from_def(db: &RootDatabase, file_id: HirFileId, def: Self::Def) -> Option { + 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)) + Some(field.definition(db)) } fn from_ref( db: &RootDatabase, analyzer: &SourceAnalyzer, refer: Self::Ref, - ) -> Option { + ) -> Option { let field = analyzer.resolve_field(&refer)?; - Some(field.declaration(db)) + Some(field.definition(db)) } } -impl HasDeclaration for AssocItem { +impl HasDefinition for AssocItem { type Def = ast::ImplItem; type Ref = ast::MethodCallExpr; - fn declaration(self, db: &RootDatabase) -> Declaration { + fn definition(self, db: &RootDatabase) -> Definition { let item = NameKind::AssocItem(self); let container = self.module(db); let visibility = match self { @@ -224,30 +227,30 @@ impl HasDeclaration for AssocItem { AssocItem::Const(c) => c.source(db).ast.visibility(), AssocItem::TypeAlias(a) => a.source(db).ast.visibility(), }; - Declaration { item, container, visibility } + Definition { item, container, visibility } } - fn from_def(db: &RootDatabase, file_id: HirFileId, def: Self::Def) -> Option { + 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)) + Some(item.definition(db)) } fn from_ref( db: &RootDatabase, analyzer: &SourceAnalyzer, refer: Self::Ref, - ) -> Option { + ) -> Option { let func: AssocItem = analyzer.resolve_method_call(&refer)?.into(); - Some(func.declaration(db)) + Some(func.definition(db)) } } -impl HasDeclaration for ModuleDef { +impl HasDefinition for ModuleDef { type Def = ast::ModuleItem; type Ref = ast::Path; - fn declaration(self, db: &RootDatabase) -> Declaration { + fn definition(self, db: &RootDatabase) -> Definition { let (container, visibility) = match self { ModuleDef::Module(it) => { let container = it.parent(db).or_else(|| Some(it)).unwrap(); @@ -270,22 +273,22 @@ impl HasDeclaration for ModuleDef { ModuleDef::BuiltinType(..) => unreachable!(), }; let item = NameKind::Def(self); - Declaration { item, container, visibility } + Definition { item, container, visibility } } - fn from_def(db: &RootDatabase, file_id: HirFileId, def: Self::Def) -> Option { + 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)) + Some(def.definition(db)) } fn from_ref( db: &RootDatabase, analyzer: &SourceAnalyzer, refer: Self::Ref, - ) -> Option { + ) -> Option { None } } -// FIXME: impl HasDeclaration for hir::MacroDef +// FIXME: impl HasDefinition for hir::MacroDef diff --git a/crates/ra_ide_api/src/references.rs b/crates/ra_ide_api/src/references.rs index 24453452b..24cd7e827 100644 --- a/crates/ra_ide_api/src/references.rs +++ b/crates/ra_ide_api/src/references.rs @@ -7,10 +7,8 @@ use relative_path::{RelativePath, RelativePathBuf}; use crate::{ db::RootDatabase, - name_kind::{ - classify_name, classify_name_ref, - NameKind::{self, *}, - }, + name_kind::{classify_name, classify_name_ref, Definition, NameKind::*}, + search_scope::find_refs, FileId, FilePosition, FileRange, FileSystemEdit, NavigationTarget, RangeInfo, SourceChange, SourceFileEdit, TextRange, }; @@ -58,9 +56,9 @@ pub(crate) fn find_all_refs( ) -> Option> { let parse = db.parse(position.file_id); let syntax = parse.tree().syntax().clone(); - let RangeInfo { range, info: (analyzer, name_kind) } = find_name(db, &syntax, position)?; + let RangeInfo { range, info: (name, def) } = find_name(db, &syntax, position)?; - let declaration = match name_kind { + let declaration = match def.item { Macro(mac) => NavigationTarget::from_macro_def(db, mac), FieldAccess(field) => NavigationTarget::from_field(db, field), AssocItem(assoc) => NavigationTarget::from_assoc_item(db, assoc), @@ -74,14 +72,19 @@ pub(crate) fn find_all_refs( GenericParam(_) => return None, }; - let references = match name_kind { - Pat((_, pat)) => analyzer - .find_all_refs(&pat.to_node(&syntax)) - .into_iter() - .map(move |ref_desc| FileRange { file_id: position.file_id, range: ref_desc.range }) - .collect::>(), - _ => vec![], - }; + // let references = match name_kind { + // Pat((_, pat)) => analyzer + // .find_all_refs(&pat.to_node(&syntax)) + // .into_iter() + // .map(move |ref_desc| FileRange { file_id: position.file_id, range: ref_desc.range }) + // .collect::>(), + // _ => vec![], + // }; + let references = find_refs(db, def, name); + let references = references + .into_iter() + .map(move |ref_desc| FileRange { file_id: position.file_id, range: ref_desc.range }) + .collect::>(); return Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references })); @@ -89,18 +92,17 @@ pub(crate) fn find_all_refs( db: &RootDatabase, syntax: &SyntaxNode, position: FilePosition, - ) -> Option> { + ) -> Option> { if let Some(name) = find_node_at_offset::(&syntax, position.offset) { - let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name.syntax(), None); - let name_kind = classify_name(db, position.file_id, &name)?.item; + let def = classify_name(db, position.file_id, &name)?; let range = name.syntax().text_range(); - return Some(RangeInfo::new(range, (analyzer, name_kind))); + return Some(RangeInfo::new(range, (name.text().to_string(), def))); } let name_ref = find_node_at_offset::(&syntax, position.offset)?; let range = name_ref.syntax().text_range(); let analyzer = hir::SourceAnalyzer::new(db, position.file_id, name_ref.syntax(), None); - let name_kind = classify_name_ref(db, position.file_id, &analyzer, &name_ref)?.item; - Some(RangeInfo::new(range, (analyzer, name_kind))) + let def = classify_name_ref(db, position.file_id, &analyzer, &name_ref)?; + Some(RangeInfo::new(range, (name_ref.text().to_string(), def))) } } @@ -271,12 +273,16 @@ mod tests { let code = r#" //- /lib.rs struct Foo { - spam<|>: u32, + pub spam<|>: u32, + } + + fn main(s: Foo) { + let f = s.spam; } "#; let refs = get_all_refs(code); - assert_eq!(refs.len(), 1); + assert_eq!(refs.len(), 2); } #[test] diff --git a/crates/ra_ide_api/src/search_scope.rs b/crates/ra_ide_api/src/search_scope.rs index 1cf1aed37..9fcbdcc3a 100644 --- a/crates/ra_ide_api/src/search_scope.rs +++ b/crates/ra_ide_api/src/search_scope.rs @@ -1,18 +1,69 @@ -use hir::{DefWithBody, HasSource, ModuleSource}; +use hir::{ + source_binder::ReferenceDescriptor, DefWithBody, HasSource, ModuleSource, SourceAnalyzer, +}; use ra_db::{FileId, SourceDatabase}; -use ra_syntax::{AstNode, TextRange}; +use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SourceFile, TextRange, TextUnit}; use crate::{ db::RootDatabase, - name_kind::{Declaration, NameKind}, + name_kind::{classify_name_ref, Definition, NameKind}, }; -pub struct SearchScope { +pub(crate) struct SearchScope { pub scope: Vec<(FileId, Option)>, } -impl Declaration { - pub fn scope(self, db: &RootDatabase) -> Option { +pub(crate) fn find_refs( + db: &RootDatabase, + def: Definition, + name: String, +) -> Vec { + let pat = name.as_str(); + let scope = def.scope(db).scope; + let mut refs = vec![]; + + let is_match = |file_id: FileId, name_ref: &ast::NameRef| -> bool { + let analyzer = SourceAnalyzer::new(db, file_id, name_ref.syntax(), None); + let classified = classify_name_ref(db, file_id, &analyzer, &name_ref); + if let Some(d) = classified { + d == def + } else { + false + } + }; + + for (file_id, text_range) in scope { + let text = db.file_text(file_id); + let parse = SourceFile::parse(&text); + let syntax = parse.tree().syntax().clone(); + + for (idx, _) in text.match_indices(pat) { + let offset = TextUnit::from_usize(idx); + if let Some(name_ref) = find_node_at_offset::(&syntax, offset) { + let name_range = name_ref.syntax().text_range(); + + if let Some(range) = text_range { + if name_range.is_subrange(&range) && is_match(file_id, &name_ref) { + refs.push(ReferenceDescriptor { + name: name_ref.text().to_string(), + range: name_ref.syntax().text_range(), + }); + } + } else if is_match(file_id, &name_ref) { + refs.push(ReferenceDescriptor { + name: name_ref.text().to_string(), + range: name_ref.syntax().text_range(), + }); + } + } + } + } + + return refs; +} + +impl Definition { + pub fn scope(&self, db: &RootDatabase) -> SearchScope { let module_src = self.container.definition_source(db); let file_id = module_src.file_id.original_file(db); @@ -22,16 +73,16 @@ impl Declaration { DefWithBody::Const(c) => c.source(db).ast.syntax().text_range(), DefWithBody::Static(s) => s.source(db).ast.syntax().text_range(), }; - return Some(SearchScope { scope: vec![(file_id, Some(range))] }); + return SearchScope { scope: vec![(file_id, Some(range))] }; } - if let Some(vis) = self.visibility { + if let Some(ref vis) = self.visibility { let source_root_id = db.file_source_root(file_id); let source_root = db.source_root(source_root_id); let mut files = source_root.walk().map(|id| (id.into(), None)).collect::>(); if vis.syntax().text() == "pub(crate)" { - return Some(SearchScope { scope: files }); + return SearchScope { scope: files }; } if vis.syntax().text() == "pub" { let krate = self.container.krate(db).unwrap(); @@ -48,17 +99,15 @@ impl Declaration { } } - return Some(SearchScope { scope: files }); + return SearchScope { scope: files }; } - // FIXME: extend to "pub(super)" and "pub(in path)" cases, - // then remove `Option` - return None; + // FIXME: "pub(super)", "pub(in path)" } let range = match module_src.ast { ModuleSource::Module(m) => Some(m.syntax().text_range()), ModuleSource::SourceFile(_) => None, }; - Some(SearchScope { scope: vec![(file_id, range)] }) + SearchScope { scope: vec![(file_id, range)] } } } -- cgit v1.2.3