From d26d0ada50fd0063c03e28bc2673f9f63fd23d95 Mon Sep 17 00:00:00 2001 From: Ekaterina Babshukova Date: Sat, 12 Oct 2019 18:47:17 +0300 Subject: restructure a bit --- crates/ra_ide_api/src/references/classify.rs | 143 +++++++ crates/ra_ide_api/src/references/definition.rs | 177 +++++++++ crates/ra_ide_api/src/references/rename.rs | 467 +++++++++++++++++++++++ crates/ra_ide_api/src/references/search_scope.rs | 61 +++ 4 files changed, 848 insertions(+) create mode 100644 crates/ra_ide_api/src/references/classify.rs create mode 100644 crates/ra_ide_api/src/references/definition.rs create mode 100644 crates/ra_ide_api/src/references/rename.rs create mode 100644 crates/ra_ide_api/src/references/search_scope.rs (limited to 'crates/ra_ide_api/src/references') diff --git a/crates/ra_ide_api/src/references/classify.rs b/crates/ra_ide_api/src/references/classify.rs new file mode 100644 index 000000000..0b604a5cf --- /dev/null +++ b/crates/ra_ide_api/src/references/classify.rs @@ -0,0 +1,143 @@ +use hir::{ + AssocItem, Either, EnumVariant, FromSource, Module, ModuleDef, ModuleSource, Path, + PathResolution, Source, SourceAnalyzer, StructField, +}; +use ra_db::FileId; +use ra_syntax::{ast, match_ast, AstNode, AstPtr}; + +use super::{definition::HasDefinition, Definition, NameKind}; +use crate::db::RootDatabase; + +use hir::{db::AstDatabase, HirFileId}; + +pub(crate) fn classify_name( + db: &RootDatabase, + file_id: FileId, + name: &ast::Name, +) -> Option { + let parent = name.syntax().parent()?; + let file_id = file_id.into(); + + match_ast! { + match parent { + ast::BindPat(it) => { + decl_from_pat(db, file_id, AstPtr::new(&it)) + }, + ast::RecordFieldDef(it) => { + StructField::from_def(db, file_id, it) + }, + 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: ModuleDef = EnumVariant::from_source(db, src)?.into(); + Some(def.definition(db)) + }, + ast::ModuleItem(it) => { + ModuleDef::from_def(db, file_id, it) + }, + _ => None, + } + } +} + +pub(crate) fn classify_name_ref( + db: &RootDatabase, + file_id: FileId, + name_ref: &ast::NameRef, +) -> Option { + let analyzer = SourceAnalyzer::new(db, file_id, name_ref.syntax(), None); + 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.definition(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.definition(db)); + } + }, + _ => (), + } + } + + 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(Definition { item: NameKind::Macro(mac), container, visibility }); + } + } + + // General case, a path or a local: + 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.definition(db)), + PathResolution::LocalBinding(Either::A(pat)) => decl_from_pat(db, file_id, pat), + PathResolution::LocalBinding(Either::B(par)) => { + Some(Definition { item: NameKind::SelfParam(par), container, visibility }) + } + PathResolution::GenericParam(par) => { + // FIXME: get generic param def + Some(Definition { item: NameKind::GenericParam(par), container, visibility }) + } + PathResolution::Macro(def) => { + 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(Definition { item: NameKind::SelfType(ty), container, visibility }) + } + PathResolution::AssocItem(assoc) => Some(assoc.definition(db)), + } +} + +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); + Some(Definition { item, container, visibility: None }) +} diff --git a/crates/ra_ide_api/src/references/definition.rs b/crates/ra_ide_api/src/references/definition.rs new file mode 100644 index 000000000..65b1f8dd7 --- /dev/null +++ b/crates/ra_ide_api/src/references/definition.rs @@ -0,0 +1,177 @@ +use hir::{ + db::AstDatabase, Adt, AssocItem, DefWithBody, FromSource, HasSource, HirFileId, MacroDef, + Module, ModuleDef, SourceAnalyzer, StructField, Ty, VariantDef, +}; +use ra_syntax::{ast, ast::VisibilityOwner, AstNode, AstPtr}; + +use crate::db::RootDatabase; + +#[derive(Debug, PartialEq, Eq)] +pub enum NameKind { + Macro(MacroDef), + FieldAccess(StructField), + AssocItem(AssocItem), + Def(ModuleDef), + SelfType(Ty), + Pat((DefWithBody, AstPtr)), + SelfParam(AstPtr), + GenericParam(u32), +} + +#[derive(PartialEq, Eq)] +pub(crate) struct Definition { + pub visibility: Option, + pub container: Module, + pub item: NameKind, +} + +pub(super) trait HasDefinition { + type Def; + type Ref; + + 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; +} + +// 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); +// Some(Definition { item, container, visibility: None }) +// } + +impl HasDefinition for StructField { + type Def = ast::RecordFieldDef; + type Ref = ast::FieldExpr; + + fn definition(self, db: &RootDatabase) -> Definition { + let item = NameKind::FieldAccess(self); + let parent = self.parent_def(db); + let container = parent.module(db); + let visibility = match parent { + VariantDef::Struct(s) => s.source(db).ast.visibility(), + VariantDef::EnumVariant(e) => e.source(db).ast.parent_enum().visibility(), + }; + Definition { 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.definition(db)) + } + + fn from_ref( + db: &RootDatabase, + analyzer: &SourceAnalyzer, + refer: Self::Ref, + ) -> Option { + let field = analyzer.resolve_field(&refer)?; + Some(field.definition(db)) + } +} + +impl HasDefinition for AssocItem { + type Def = ast::ImplItem; + type Ref = ast::MethodCallExpr; + + fn definition(self, db: &RootDatabase) -> Definition { + let item = NameKind::AssocItem(self); + let container = self.module(db); + 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(), + }; + Definition { item, container, visibility } + } + + fn from_def(db: &RootDatabase, file_id: HirFileId, def: Self::Def) -> Option { + if def.syntax().parent().and_then(ast::ItemList::cast).is_none() { + return None; + } + let src = hir::Source { file_id, ast: def }; + let item = AssocItem::from_source(db, src)?; + Some(item.definition(db)) + } + + fn from_ref( + db: &RootDatabase, + analyzer: &SourceAnalyzer, + refer: Self::Ref, + ) -> Option { + let func: AssocItem = analyzer.resolve_method_call(&refer)?.into(); + Some(func.definition(db)) + } +} + +impl HasDefinition for ModuleDef { + type Def = ast::ModuleItem; + type Ref = ast::Path; + + fn definition(self, db: &RootDatabase) -> Definition { + let (container, visibility) = match self { + ModuleDef::Module(it) => { + let container = it.parent(db).or_else(|| Some(it)).unwrap(); + let visibility = it.declaration_source(db).and_then(|s| s.ast.visibility()); + (container, visibility) + } + ModuleDef::EnumVariant(it) => { + let container = it.module(db); + let visibility = it.source(db).ast.parent_enum().visibility(); + (container, visibility) + } + ModuleDef::Function(it) => (it.module(db), it.source(db).ast.visibility()), + ModuleDef::Const(it) => (it.module(db), it.source(db).ast.visibility()), + ModuleDef::Static(it) => (it.module(db), it.source(db).ast.visibility()), + ModuleDef::Trait(it) => (it.module(db), it.source(db).ast.visibility()), + ModuleDef::TypeAlias(it) => (it.module(db), it.source(db).ast.visibility()), + ModuleDef::Adt(Adt::Struct(it)) => (it.module(db), it.source(db).ast.visibility()), + ModuleDef::Adt(Adt::Union(it)) => (it.module(db), it.source(db).ast.visibility()), + ModuleDef::Adt(Adt::Enum(it)) => (it.module(db), it.source(db).ast.visibility()), + ModuleDef::BuiltinType(..) => unreachable!(), + }; + let item = NameKind::Def(self); + Definition { 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.definition(db)) + } + + fn from_ref( + db: &RootDatabase, + analyzer: &SourceAnalyzer, + refer: Self::Ref, + ) -> Option { + None + } +} + +// FIXME: impl HasDefinition for hir::MacroDef diff --git a/crates/ra_ide_api/src/references/rename.rs b/crates/ra_ide_api/src/references/rename.rs new file mode 100644 index 000000000..7e564a40e --- /dev/null +++ b/crates/ra_ide_api/src/references/rename.rs @@ -0,0 +1,467 @@ +use hir::ModuleSource; +use ra_db::SourceDatabase; +use ra_syntax::{algo::find_node_at_offset, ast, AstNode, SyntaxNode}; +use relative_path::{RelativePath, RelativePathBuf}; + +use crate::{ + db::RootDatabase, FileId, FilePosition, FileSystemEdit, RangeInfo, SourceChange, + SourceFileEdit, TextRange, +}; + +use super::find_all_refs; + +pub(crate) fn rename( + db: &RootDatabase, + position: FilePosition, + new_name: &str, +) -> Option> { + let parse = db.parse(position.file_id); + if let Some((ast_name, ast_module)) = + find_name_and_module_at_offset(parse.tree().syntax(), position) + { + let range = ast_name.syntax().text_range(); + rename_mod(db, &ast_name, &ast_module, position, new_name) + .map(|info| RangeInfo::new(range, info)) + } else { + rename_reference(db, position, new_name) + } +} + +fn find_name_and_module_at_offset( + syntax: &SyntaxNode, + position: FilePosition, +) -> Option<(ast::Name, ast::Module)> { + let ast_name = find_node_at_offset::(syntax, position.offset)?; + let ast_module = ast::Module::cast(ast_name.syntax().parent()?)?; + Some((ast_name, ast_module)) +} + +fn source_edit_from_file_id_range( + file_id: FileId, + range: TextRange, + new_name: &str, +) -> SourceFileEdit { + SourceFileEdit { + file_id, + edit: { + let mut builder = ra_text_edit::TextEditBuilder::default(); + builder.replace(range, new_name.into()); + builder.finish() + }, + } +} + +fn rename_mod( + db: &RootDatabase, + ast_name: &ast::Name, + ast_module: &ast::Module, + position: FilePosition, + new_name: &str, +) -> Option { + let mut source_file_edits = Vec::new(); + let mut file_system_edits = Vec::new(); + let module_src = hir::Source { file_id: position.file_id.into(), ast: ast_module.clone() }; + if let Some(module) = hir::Module::from_declaration(db, module_src) { + let src = module.definition_source(db); + let file_id = src.file_id.original_file(db); + match src.ast { + ModuleSource::SourceFile(..) => { + let mod_path: RelativePathBuf = db.file_relative_path(file_id); + // mod is defined in path/to/dir/mod.rs + let dst_path = if mod_path.file_stem() == Some("mod") { + mod_path + .parent() + .and_then(|p| p.parent()) + .or_else(|| Some(RelativePath::new(""))) + .map(|p| p.join(new_name).join("mod.rs")) + } else { + Some(mod_path.with_file_name(new_name).with_extension("rs")) + }; + if let Some(path) = dst_path { + let move_file = FileSystemEdit::MoveFile { + src: file_id, + dst_source_root: db.file_source_root(position.file_id), + dst_path: path, + }; + file_system_edits.push(move_file); + } + } + ModuleSource::Module(..) => {} + } + } + + let edit = SourceFileEdit { + file_id: position.file_id, + edit: { + let mut builder = ra_text_edit::TextEditBuilder::default(); + builder.replace(ast_name.syntax().text_range(), new_name.into()); + builder.finish() + }, + }; + source_file_edits.push(edit); + + Some(SourceChange::from_edits("rename", source_file_edits, file_system_edits)) +} + +fn rename_reference( + db: &RootDatabase, + position: FilePosition, + new_name: &str, +) -> Option> { + let RangeInfo { range, info: refs } = find_all_refs(db, position)?; + + let edit = refs + .into_iter() + .map(|range| source_edit_from_file_id_range(range.file_id, range.range, new_name)) + .collect::>(); + + if edit.is_empty() { + return None; + } + + Some(RangeInfo::new(range, SourceChange::source_file_edits("rename", edit))) +} + +#[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 { + pub spam<|>: u32, + } + + fn main(s: Foo) { + let f = s.spam; + } + "#; + + let refs = get_all_refs(code); + assert_eq!(refs.len(), 2); + } + + #[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); + } + + #[test] + fn test_find_all_refs_modules() { + let code = r#" + //- /lib.rs + pub mod foo; + pub mod bar; + + fn f() { + let i = foo::Foo { n: 5 }; + } + + //- /foo.rs + use crate::bar; + + pub struct Foo { + pub n: u32, + } + + fn f() { + let i = bar::Bar { n: 5 }; + } + + //- /bar.rs + use crate::foo; + + pub struct Bar { + pub n: u32, + } + + fn f() { + let i = foo::Foo<|> { n: 5 }; + } + "#; + + let (analysis, pos) = analysis_and_position(code); + let refs = analysis.find_all_refs(pos).unwrap().unwrap(); + assert_eq!(refs.len(), 3); + } + + fn get_all_refs(text: &str) -> ReferenceSearchResult { + let (analysis, position) = single_file_with_position(text); + analysis.find_all_refs(position).unwrap().unwrap() + } + + #[test] + fn test_rename_for_local() { + test_rename( + r#" + fn main() { + let mut i = 1; + let j = 1; + i = i<|> + j; + + { + i = 0; + } + + i = 5; + }"#, + "k", + r#" + fn main() { + let mut k = 1; + let j = 1; + k = k + j; + + { + k = 0; + } + + k = 5; + }"#, + ); + } + + #[test] + fn test_rename_for_param_inside() { + test_rename( + r#" + fn foo(i : u32) -> u32 { + i<|> + }"#, + "j", + r#" + fn foo(j : u32) -> u32 { + j + }"#, + ); + } + + #[test] + fn test_rename_refs_for_fn_param() { + test_rename( + r#" + fn foo(i<|> : u32) -> u32 { + i + }"#, + "new_name", + r#" + fn foo(new_name : u32) -> u32 { + new_name + }"#, + ); + } + + #[test] + fn test_rename_for_mut_param() { + test_rename( + r#" + fn foo(mut i<|> : u32) -> u32 { + i + }"#, + "new_name", + r#" + fn foo(mut new_name : u32) -> u32 { + new_name + }"#, + ); + } + + #[test] + fn test_rename_mod() { + let (analysis, position) = analysis_and_position( + " + //- /lib.rs + mod bar; + + //- /bar.rs + mod foo<|>; + + //- /bar/foo.rs + // emtpy + ", + ); + let new_name = "foo2"; + let source_change = analysis.rename(position, new_name).unwrap(); + assert_debug_snapshot!(&source_change, +@r###" + Some( + RangeInfo { + range: [4; 7), + info: SourceChange { + label: "rename", + source_file_edits: [ + SourceFileEdit { + file_id: FileId( + 2, + ), + edit: TextEdit { + atoms: [ + AtomTextEdit { + delete: [4; 7), + insert: "foo2", + }, + ], + }, + }, + ], + file_system_edits: [ + MoveFile { + src: FileId( + 3, + ), + dst_source_root: SourceRootId( + 0, + ), + dst_path: "bar/foo2.rs", + }, + ], + cursor_position: None, + }, + }, + ) + "###); + } + + #[test] + fn test_rename_mod_in_dir() { + let (analysis, position) = analysis_and_position( + " + //- /lib.rs + mod fo<|>o; + //- /foo/mod.rs + // emtpy + ", + ); + let new_name = "foo2"; + let source_change = analysis.rename(position, new_name).unwrap(); + assert_debug_snapshot!(&source_change, + @r###" + Some( + RangeInfo { + range: [4; 7), + info: SourceChange { + label: "rename", + source_file_edits: [ + SourceFileEdit { + file_id: FileId( + 1, + ), + edit: TextEdit { + atoms: [ + AtomTextEdit { + delete: [4; 7), + insert: "foo2", + }, + ], + }, + }, + ], + file_system_edits: [ + MoveFile { + src: FileId( + 2, + ), + dst_source_root: SourceRootId( + 0, + ), + dst_path: "foo2/mod.rs", + }, + ], + cursor_position: None, + }, + }, + ) + "### + ); + } + + fn test_rename(text: &str, new_name: &str, expected: &str) { + let (analysis, position) = single_file_with_position(text); + let source_change = analysis.rename(position, new_name).unwrap(); + let mut text_edit_builder = ra_text_edit::TextEditBuilder::default(); + let mut file_id: Option = None; + if let Some(change) = source_change { + for edit in change.info.source_file_edits { + file_id = Some(edit.file_id); + for atom in edit.edit.as_atoms() { + text_edit_builder.replace(atom.delete, atom.insert.clone()); + } + } + } + let result = + text_edit_builder.finish().apply(&*analysis.file_text(file_id.unwrap()).unwrap()); + assert_eq_text!(expected, &*result); + } +} diff --git a/crates/ra_ide_api/src/references/search_scope.rs b/crates/ra_ide_api/src/references/search_scope.rs new file mode 100644 index 000000000..557ee7b57 --- /dev/null +++ b/crates/ra_ide_api/src/references/search_scope.rs @@ -0,0 +1,61 @@ +use hir::{DefWithBody, HasSource, ModuleSource}; +use ra_db::{FileId, SourceDatabase}; +use ra_syntax::{AstNode, TextRange}; + +use crate::db::RootDatabase; + +use super::{Definition, NameKind}; + +pub(crate) struct SearchScope { + pub scope: Vec<(FileId, Option)>, +} + +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); + + if let NameKind::Pat((def, _)) = self.item { + let range = match def { + DefWithBody::Function(f) => f.source(db).ast.syntax().text_range(), + DefWithBody::Const(c) => c.source(db).ast.syntax().text_range(), + DefWithBody::Static(s) => s.source(db).ast.syntax().text_range(), + }; + return SearchScope { scope: vec![(file_id, Some(range))] }; + } + + 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().to_string().as_str() == "pub(crate)" { + return SearchScope { scope: files }; + } + if vis.syntax().to_string().as_str() == "pub" { + let krate = self.container.krate(db).unwrap(); + let crate_graph = db.crate_graph(); + + for crate_id in crate_graph.iter() { + let mut crate_deps = crate_graph.dependencies(crate_id); + + if crate_deps.any(|dep| dep.crate_id() == krate.crate_id()) { + let root_file = crate_graph.crate_root(crate_id); + let source_root_id = db.file_source_root(root_file); + let source_root = db.source_root(source_root_id); + files.extend(source_root.walk().map(|id| (id.into(), None))); + } + } + + return SearchScope { scope: files }; + } + // FIXME: "pub(super)", "pub(in path)" + } + + let range = match module_src.ast { + ModuleSource::Module(m) => Some(m.syntax().text_range()), + ModuleSource::SourceFile(_) => None, + }; + SearchScope { scope: vec![(file_id, range)] } + } +} -- cgit v1.2.3