From 11577288c23b1f2de1aaba0137c9c2d6344b9a92 Mon Sep 17 00:00:00 2001 From: Ekaterina Babshukova Date: Sat, 14 Sep 2019 14:38:10 +0300 Subject: [PATCH] initial classify_name --- crates/ra_hir/src/from_source.rs | 60 +++++- crates/ra_ide_api/src/goto_definition.rs | 2 +- crates/ra_ide_api/src/hover.rs | 2 +- crates/ra_ide_api/src/name_ref_kind.rs | 6 +- crates/ra_ide_api/src/references.rs | 154 ++++++++++++--- crates/ra_ide_api/src/search_scope.rs | 186 +++++++++++++++++++ crates/ra_ide_api/src/syntax_highlighting.rs | 2 +- 7 files changed, 376 insertions(+), 36 deletions(-) create mode 100644 crates/ra_ide_api/src/search_scope.rs diff --git a/crates/ra_hir/src/from_source.rs b/crates/ra_hir/src/from_source.rs index f80d8eb5f1e..e09414ca3d6 100644 --- a/crates/ra_hir/src/from_source.rs +++ b/crates/ra_hir/src/from_source.rs @@ -11,8 +11,9 @@ use crate::{ db::{AstDatabase, DefDatabase, HirDatabase}, ids::{AstItemDef, LocationCtx}, name::AsName, - Const, Crate, Enum, EnumVariant, FieldSource, Function, HasSource, ImplBlock, Module, - ModuleSource, Source, Static, Struct, StructField, Trait, TypeAlias, Union, VariantDef, + AssocItem, Const, Crate, Enum, EnumVariant, FieldSource, Function, HasSource, ImplBlock, + Module, ModuleDef, ModuleSource, Source, Static, Struct, StructField, Trait, TypeAlias, Union, + VariantDef, }; pub trait FromSource: Sized { @@ -129,6 +130,61 @@ impl FromSource for StructField { } } +impl FromSource for AssocItem { + type Ast = ast::ImplItem; + fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source) -> Option { + macro_rules! def { + ($kind:ident, $ast:ident) => { + $kind::from_source(db, Source { file_id: src.file_id, ast: $ast }) + .and_then(|it| Some(AssocItem::from(it))) + }; + } + + match src.ast { + ast::ImplItem::FnDef(f) => def!(Function, f), + ast::ImplItem::ConstDef(c) => def!(Const, c), + ast::ImplItem::TypeAliasDef(a) => def!(TypeAlias, a), + } + } +} + +// not fully matched +impl FromSource for ModuleDef { + type Ast = ast::ModuleItem; + fn from_source(db: &(impl DefDatabase + AstDatabase), src: Source) -> Option { + macro_rules! def { + ($kind:ident, $ast:ident) => { + $kind::from_source(db, Source { file_id: src.file_id, ast: $ast }) + .and_then(|it| Some(ModuleDef::from(it))) + }; + } + + match src.ast { + ast::ModuleItem::FnDef(f) => def!(Function, f), + ast::ModuleItem::ConstDef(c) => def!(Const, c), + ast::ModuleItem::TypeAliasDef(a) => def!(TypeAlias, a), + ast::ModuleItem::TraitDef(t) => def!(Trait, t), + ast::ModuleItem::StaticDef(s) => def!(Static, s), + ast::ModuleItem::StructDef(s) => { + let src = Source { file_id: src.file_id, ast: s }; + let s = Struct::from_source(db, src)?; + Some(ModuleDef::Adt(s.into())) + } + ast::ModuleItem::EnumDef(e) => { + let src = Source { file_id: src.file_id, ast: e }; + let e = Enum::from_source(db, src)?; + Some(ModuleDef::Adt(e.into())) + } + ast::ModuleItem::Module(ref m) if !m.has_semi() => { + let src = Source { file_id: src.file_id, ast: ModuleSource::Module(m.clone()) }; + let module = Module::from_definition(db, src)?; + Some(ModuleDef::Module(module)) + } + _ => None, + } + } +} + // FIXME: simplify it impl ModuleSource { pub fn from_position( diff --git a/crates/ra_ide_api/src/goto_definition.rs b/crates/ra_ide_api/src/goto_definition.rs index 41a88314fb2..13e42bb35c1 100644 --- a/crates/ra_ide_api/src/goto_definition.rs +++ b/crates/ra_ide_api/src/goto_definition.rs @@ -10,7 +10,7 @@ use ra_syntax::{ use crate::{ db::RootDatabase, display::ShortLabel, - name_ref_kind::{classify_name_ref, NameRefKind::*}, + name_ref_kind::{classify_name_ref, NameKind::*}, FilePosition, NavigationTarget, RangeInfo, }; diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs index 24b161c5c11..5c2549dc38c 100644 --- a/crates/ra_ide_api/src/hover.rs +++ b/crates/ra_ide_api/src/hover.rs @@ -14,7 +14,7 @@ use crate::{ description_from_symbol, docs_from_symbol, macro_label, rust_code_markup, rust_code_markup_with_doc, ShortLabel, }, - name_ref_kind::{classify_name_ref, NameRefKind::*}, + name_ref_kind::{classify_name_ref, NameKind::*}, FilePosition, FileRange, RangeInfo, }; diff --git a/crates/ra_ide_api/src/name_ref_kind.rs b/crates/ra_ide_api/src/name_ref_kind.rs index 149585971e9..eb8caabfe42 100644 --- a/crates/ra_ide_api/src/name_ref_kind.rs +++ b/crates/ra_ide_api/src/name_ref_kind.rs @@ -6,7 +6,7 @@ use test_utils::tested_by; use crate::db::RootDatabase; -pub enum NameRefKind { +pub enum NameKind { Method(hir::Function), Macro(hir::MacroDef), FieldAccess(hir::StructField), @@ -22,8 +22,8 @@ pub(crate) fn classify_name_ref( db: &RootDatabase, analyzer: &hir::SourceAnalyzer, name_ref: &ast::NameRef, -) -> Option { - use NameRefKind::*; +) -> Option { + use NameKind::*; // Check if it is a method if let Some(method_call) = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast) { diff --git a/crates/ra_ide_api/src/references.rs b/crates/ra_ide_api/src/references.rs index 4247c6d9085..9335bc8cab2 100644 --- a/crates/ra_ide_api/src/references.rs +++ b/crates/ra_ide_api/src/references.rs @@ -1,13 +1,18 @@ //! FIXME: write short doc here -use hir::{Either, ModuleSource}; +use hir::{FromSource, ModuleSource}; use ra_db::{SourceDatabase, SourceDatabaseExt}; -use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SourceFile, SyntaxNode}; +use ra_syntax::{algo::find_node_at_offset, ast, AstNode, AstPtr, SyntaxNode}; use relative_path::{RelativePath, RelativePathBuf}; use crate::{ - db::RootDatabase, FileId, FilePosition, FileRange, FileSystemEdit, NavigationTarget, RangeInfo, - SourceChange, SourceFileEdit, TextRange, + db::RootDatabase, + name_ref_kind::{ + classify_name_ref, + NameKind::{self, *}, + }, + FileId, FilePosition, FileRange, FileSystemEdit, NavigationTarget, RangeInfo, SourceChange, + SourceFileEdit, TextRange, }; #[derive(Debug, Clone)] @@ -52,41 +57,92 @@ pub(crate) fn find_all_refs( position: FilePosition, ) -> Option> { let parse = db.parse(position.file_id); - let RangeInfo { range, info: (binding, analyzer) } = find_binding(db, &parse.tree(), position)?; - let declaration = NavigationTarget::from_bind_pat(position.file_id, &binding); + let syntax = parse.tree().syntax().clone(); + let RangeInfo { range, info: (analyzer, name_kind) } = find_name(db, &syntax, position)?; - let references = analyzer - .find_all_refs(&binding) - .into_iter() - .map(move |ref_desc| FileRange { file_id: position.file_id, range: ref_desc.range }) - .collect::>(); + let declaration = match name_kind { + Macro(mac) => NavigationTarget::from_macro_def(db, mac), + FieldAccess(field) => NavigationTarget::from_field(db, field), + AssocItem(assoc) => NavigationTarget::from_assoc_item(db, assoc), + Method(func) => NavigationTarget::from_def_source(db, func), + Def(def) => NavigationTarget::from_def(db, def)?, + SelfType(ref ty) => match ty.as_adt() { + Some((def_id, _)) => NavigationTarget::from_adt_def(db, def_id), + None => return None, + }, + Pat(pat) => NavigationTarget::from_pat(db, position.file_id, pat), + SelfParam(par) => NavigationTarget::from_self_param(position.file_id, par), + 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![], + }; return Some(RangeInfo::new(range, ReferenceSearchResult { declaration, references })); - fn find_binding<'a>( + fn find_name<'a>( db: &RootDatabase, - source_file: &SourceFile, + syntax: &SyntaxNode, position: FilePosition, - ) -> Option> { - let syntax = source_file.syntax(); - if let Some(binding) = find_node_at_offset::(syntax, position.offset) { - let range = binding.syntax().text_range(); - let analyzer = hir::SourceAnalyzer::new(db, position.file_id, binding.syntax(), None); - return Some(RangeInfo::new(range, (binding, analyzer))); - }; - let name_ref = find_node_at_offset::(syntax, position.offset)?; + ) -> 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)?; + let range = name.syntax().text_range(); + return Some(RangeInfo::new(range, (analyzer, name_kind))); + } + 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 resolved = analyzer.resolve_local_name(&name_ref)?; - if let Either::A(ptr) = resolved.ptr() { - if let ast::Pat::BindPat(binding) = ptr.to_node(source_file.syntax()) { - return Some(RangeInfo::new(range, (binding, analyzer))); - } - } - None + let name_kind = classify_name_ref(db, &analyzer, &name_ref)?; + Some(RangeInfo::new(range, (analyzer, name_kind))) } } +fn classify_name(db: &RootDatabase, file_id: FileId, name: &ast::Name) -> Option { + let parent = name.syntax().parent()?; + let file_id = file_id.into(); + + if let Some(pat) = ast::BindPat::cast(parent.clone()) { + return Some(Pat(AstPtr::new(&pat))); + } + if let Some(var) = ast::EnumVariant::cast(parent.clone()) { + let src = hir::Source { file_id, ast: var }; + let var = hir::EnumVariant::from_source(db, src)?; + return Some(Def(var.into())); + } + if let Some(field) = ast::RecordFieldDef::cast(parent.clone()) { + let src = hir::Source { file_id, ast: hir::FieldSource::Named(field) }; + let field = hir::StructField::from_source(db, src)?; + return Some(FieldAccess(field)); + } + if let Some(field) = ast::TupleFieldDef::cast(parent.clone()) { + let src = hir::Source { file_id, ast: hir::FieldSource::Pos(field) }; + let field = hir::StructField::from_source(db, src)?; + return Some(FieldAccess(field)); + } + if let Some(_) = parent.parent().and_then(ast::ItemList::cast) { + let ast = ast::ImplItem::cast(parent.clone())?; + let src = hir::Source { file_id, ast }; + let item = hir::AssocItem::from_source(db, src)?; + return Some(AssocItem(item)); + } + if let Some(item) = ast::ModuleItem::cast(parent.clone()) { + let src = hir::Source { file_id, ast: item }; + let def = hir::ModuleDef::from_source(db, src)?; + return Some(Def(def)); + } + // FIXME: TYPE_PARAM, ALIAS, MACRO_CALL; Union + + None +} + pub(crate) fn rename( db: &RootDatabase, position: FilePosition, @@ -249,6 +305,48 @@ mod tests { assert_eq!(refs.len(), 2); } + #[test] + fn test_find_all_refs_field_name() { + let code = r#" + //- /lib.rs + struct Foo { + spam<|>: u32, + } + "#; + + let refs = get_all_refs(code); + assert_eq!(refs.len(), 1); + } + + #[test] + fn test_find_all_refs_impl_item_name() { + let code = r#" + //- /lib.rs + struct Foo; + impl Foo { + fn f<|>(&self) { } + } + "#; + + let refs = get_all_refs(code); + assert_eq!(refs.len(), 1); + } + + #[test] + fn test_find_all_refs_enum_var_name() { + let code = r#" + //- /lib.rs + enum Foo { + A, + B<|>, + C, + } + "#; + + let refs = get_all_refs(code); + assert_eq!(refs.len(), 1); + } + fn get_all_refs(text: &str) -> ReferenceSearchResult { let (analysis, position) = single_file_with_position(text); analysis.find_all_refs(position).unwrap().unwrap() diff --git a/crates/ra_ide_api/src/search_scope.rs b/crates/ra_ide_api/src/search_scope.rs new file mode 100644 index 00000000000..ca1ac2b0309 --- /dev/null +++ b/crates/ra_ide_api/src/search_scope.rs @@ -0,0 +1,186 @@ +pub enum SearchScope { + Function(hir::Function), + Module(hir::Module), + Crate(hir::Crate), + Crates(Vec), +} + +pub struct SearchScope{ + pub scope: Vec +} + +pub fn find_all_refs(db: &RootDatabase, decl: NameKind) -> Vec { + let (module, visibility) = match decl { + FieldAccess(field) => { + let parent = field.parent_def(db); + let module = parent.module(db); + let visibility = match parent { + VariantDef::Struct(s) => s.source(db).ast.visibility(), + VariantDef::EnumVariant(v) => v.parent_enum(db).source(db).ast.visibility(), + }; + (module, visibility) + } + AssocItem(item) => { + let parent = item.parent_trait(db)?; + let module = parent.module(db); + let visibility = parent.source(db).ast.visibility(); + (module, visibility) + } + Def(def) => { + let (module, visibility) = match def { + ModuleDef::Module(m) => (m, ), + ModuleDef::Function(f) => (f.module(db), f.source(db).ast.visibility()), + ModuleDef::Adt::Struct(s) => (s.module(db), s.source(db).ast.visibility()), + ModuleDef::Adt::Union(u) => (u.module(db), u.source(db).ast.visibility()), + ModuleDef::Adt::Enum(e) => (e.module(db), e.source(db).ast.visibility()), + ModuleDef::EnumVariant(v) => (v.module(db), v.source(db).ast.visibility()), + ModuleDef::Const(c) => (c.module(db), c.source(db).ast.visibility()), + ModuleDef::Static(s) => (s.module(db), s.source(db).ast.visibility()), + ModuleDef::Trait(t) => (t.module(db), t.source(db).ast.visibility()), + ModuleDef::TypeAlias(a) => (a.module(db), a.source(db).ast.visibility()), + ModuleDef::BuiltinType(_) => return vec![]; + }; + (module, visibility) + } + // FIXME: add missing kinds + _ => return vec![]; + }; + let scope = scope(db, module, visibility); +} + +fn scope(db: &RootDatabase, module: hir::Module, item_vis: Option) -> SearchScope { + if let Some(v) = item_vis { + let krate = module.krate(db)?; + + if v.syntax().text() == "pub" { + SearchScope::Crate(krate) + } + if v.syntax().text() == "pub(crate)" { + let crate_graph = db.crate_graph(); + let crates = crate_graph.iter().filter(|id| { + crate_graph.dependencies(id).any(|d| d.crate_id() == krate.crate_id()) + }).map(|id| Crate { id }).collect::>(); + crates.insert(0, krate); + SearchScope::Crates(crates) + } + // FIXME: "pub(super)", "pub(in path)" + SearchScope::Module(module) + } + SearchScope::Module(module) +} + +fn process_one(db, scope: SearchScope, pat) { + match scope { + SearchScope::Crate(krate) => { + let text = db.file_text(position.file_id).as_str(); + let parse = SourceFile::parse(text); + for (offset, name) in text.match_indices(pat) { + if let Some() = find_node_at_offset(parse, offset) { + + } + } + } + } +} + +#[cfg(test)] +mod tests { + use crate::{ + mock_analysis::analysis_and_position, mock_analysis::single_file_with_position, FileId, + ReferenceSearchResult, + }; + use insta::assert_debug_snapshot; + use test_utils::assert_eq_text; + + #[test] + fn test_find_all_refs_for_local() { + let code = r#" + fn main() { + let mut i = 1; + let j = 1; + i = i<|> + j; + + { + i = 0; + } + + i = 5; + } + "#; + + let refs = get_all_refs(code); + assert_eq!(refs.len(), 5); + } + + #[test] + fn test_find_all_refs_for_param_inside() { + let code = r#" + fn foo(i : u32) -> u32 { + i<|> + }"#; + + let refs = get_all_refs(code); + assert_eq!(refs.len(), 2); + } + + #[test] + fn test_find_all_refs_for_fn_param() { + let code = r#" + fn foo(i<|> : u32) -> u32 { + i + }"#; + + let refs = get_all_refs(code); + assert_eq!(refs.len(), 2); + } + + #[test] + fn test_find_all_refs_field_name() { + let code = r#" + //- /lib.rs + struct Foo { + spam<|>: u32, + } + "#; + + let refs = get_all_refs(code); + assert_eq!(refs.len(), 1); + } + + #[test] + fn test_find_all_refs_methods() { + let code = r#" + //- /lib.rs + struct Foo; + impl Foo { + pub fn a() { + self.b() + } + fn b(&self) {} + } + "#; + + let refs = get_all_refs(code); + assert_eq!(refs.len(), 1); + } + + #[test] + fn test_find_all_refs_pub_enum() { + let code = r#" + //- /lib.rs + pub enum Foo { + A, + B<|>, + C, + } + "#; + + let refs = get_all_refs(code); + assert_eq!(refs.len(), 1); + } + + fn get_all_refs(text: &str) -> ReferenceSearchResult { + let (analysis, position) = single_file_with_position(text); + analysis.find_all_refs(position).unwrap().unwrap() + } +} \ No newline at end of file diff --git a/crates/ra_ide_api/src/syntax_highlighting.rs b/crates/ra_ide_api/src/syntax_highlighting.rs index 1d290387c41..03104e3486c 100644 --- a/crates/ra_ide_api/src/syntax_highlighting.rs +++ b/crates/ra_ide_api/src/syntax_highlighting.rs @@ -14,7 +14,7 @@ use ra_syntax::{ use crate::{ db::RootDatabase, - name_ref_kind::{classify_name_ref, NameRefKind::*}, + name_ref_kind::{classify_name_ref, NameKind::*}, FileId, };