From 1222869b3eb66f2fc67b6780feb897eddadfdec5 Mon Sep 17 00:00:00 2001 From: alibektas Date: Sun, 14 May 2023 02:38:03 +0200 Subject: [PATCH] Fix #14557. Docs aliases can now be detected and used in searching for workspace symbols --- crates/hir/src/symbols.rs | 96 +++++---- crates/ide-db/src/symbol_index.rs | 27 +++ .../ide-db/src/test_data/test_doc_alias.txt | 202 ++++++++++++++++++ .../test_symbol_index_collection.txt | 22 ++ crates/ide/src/goto_definition.rs | 1 + crates/ide/src/lib.rs | 2 +- crates/ide/src/navigation_target.rs | 13 +- crates/ide/src/runnables.rs | 16 +- crates/rust-analyzer/src/handlers/request.rs | 5 +- 9 files changed, 337 insertions(+), 47 deletions(-) create mode 100644 crates/ide-db/src/test_data/test_doc_alias.txt diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs index af37206ead..207e8206c9 100644 --- a/crates/hir/src/symbols.rs +++ b/crates/hir/src/symbols.rs @@ -20,6 +20,7 @@ pub struct FileSymbol { pub def: ModuleDef, pub loc: DeclarationLocation, pub container_name: Option, + pub is_alias: bool, } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -249,46 +250,69 @@ impl<'a> SymbolCollector<'a> { ::Data: HasSource, <::Data as HasSource>::Value: HasName, { - self.push_file_symbol(|s| { - let loc = id.lookup(s.db.upcast()); - let source = loc.source(s.db.upcast()); - let name_node = source.value.name()?; - Some(FileSymbol { - name: name_node.text().into(), - def: ModuleDef::from(id.into()), - container_name: s.current_container_name.clone(), - loc: DeclarationLocation { - hir_file_id: source.file_id, - ptr: SyntaxNodePtr::new(source.value.syntax()), - name_ptr: SyntaxNodePtr::new(name_node.syntax()), - }, - }) - }) + let loc = id.lookup(self.db.upcast()); + let source = loc.source(self.db.upcast()); + let Some(name_node) = source.value.name() else { return }; + let def = ModuleDef::from(id.into()); + let dec_loc = DeclarationLocation { + hir_file_id: source.file_id, + ptr: SyntaxNodePtr::new(source.value.syntax()), + name_ptr: SyntaxNodePtr::new(name_node.syntax()), + }; + + if let Some(attrs) = def.attrs(self.db) { + for alias in attrs.doc_aliases() { + self.symbols.push(FileSymbol { + name: alias, + def, + loc: dec_loc.clone(), + container_name: self.current_container_name.clone(), + is_alias: true, + }); + } + } + + self.symbols.push(FileSymbol { + name: name_node.text().into(), + def, + container_name: self.current_container_name.clone(), + loc: dec_loc, + is_alias: false, + }); } fn push_module(&mut self, module_id: ModuleId) { - self.push_file_symbol(|s| { - let def_map = module_id.def_map(s.db.upcast()); - let module_data = &def_map[module_id.local_id]; - let declaration = module_data.origin.declaration()?; - let module = declaration.to_node(s.db.upcast()); - let name_node = module.name()?; - Some(FileSymbol { - name: name_node.text().into(), - def: ModuleDef::Module(module_id.into()), - container_name: s.current_container_name.clone(), - loc: DeclarationLocation { - hir_file_id: declaration.file_id, - ptr: SyntaxNodePtr::new(module.syntax()), - name_ptr: SyntaxNodePtr::new(name_node.syntax()), - }, - }) - }) - } + let def_map = module_id.def_map(self.db.upcast()); + let module_data = &def_map[module_id.local_id]; + let Some(declaration) = module_data.origin.declaration() else { return }; + let module = declaration.to_node(self.db.upcast()); + let Some(name_node) = module.name() else { return }; + let dec_loc = DeclarationLocation { + hir_file_id: declaration.file_id, + ptr: SyntaxNodePtr::new(module.syntax()), + name_ptr: SyntaxNodePtr::new(name_node.syntax()), + }; - fn push_file_symbol(&mut self, f: impl FnOnce(&Self) -> Option) { - if let Some(file_symbol) = f(self) { - self.symbols.push(file_symbol); + let def = ModuleDef::Module(module_id.into()); + + if let Some(attrs) = def.attrs(self.db) { + for alias in attrs.doc_aliases() { + self.symbols.push(FileSymbol { + name: alias, + def, + loc: dec_loc.clone(), + container_name: self.current_container_name.clone(), + is_alias: true, + }); + } } + + self.symbols.push(FileSymbol { + name: name_node.text().into(), + def: ModuleDef::Module(module_id.into()), + container_name: self.current_container_name.clone(), + loc: dec_loc, + is_alias: false, + }); } } diff --git a/crates/ide-db/src/symbol_index.rs b/crates/ide-db/src/symbol_index.rs index fa796ae13b..b54c43b296 100644 --- a/crates/ide-db/src/symbol_index.rs +++ b/crates/ide-db/src/symbol_index.rs @@ -434,4 +434,31 @@ struct StructInModB; expect_file!["./test_data/test_symbol_index_collection.txt"].assert_debug_eq(&symbols); } + + #[test] + fn test_doc_alias() { + let (db, _) = RootDatabase::with_single_file( + r#" +#[doc(alias="s1")] +#[doc(alias="s2")] +#[doc(alias("mul1","mul2"))] +struct Struct; + +#[doc(alias="s1")] +struct Duplicate; + "#, + ); + + let symbols: Vec<_> = Crate::from(db.test_crate()) + .modules(&db) + .into_iter() + .map(|module_id| { + let mut symbols = SymbolCollector::collect_module(&db, module_id); + symbols.sort_by_key(|it| it.name.clone()); + (module_id, symbols) + }) + .collect(); + + expect_file!["./test_data/test_doc_alias.txt"].assert_debug_eq(&symbols); + } } diff --git a/crates/ide-db/src/test_data/test_doc_alias.txt b/crates/ide-db/src/test_data/test_doc_alias.txt new file mode 100644 index 0000000000..77714efa35 --- /dev/null +++ b/crates/ide-db/src/test_data/test_doc_alias.txt @@ -0,0 +1,202 @@ +[ + ( + Module { + id: ModuleId { + krate: Idx::(0), + block: None, + local_id: Idx::(0), + }, + }, + [ + FileSymbol { + name: "Duplicate", + def: Adt( + Struct( + Struct { + id: StructId( + 1, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 83..119, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 109..118, + }, + }, + container_name: None, + is_alias: false, + }, + FileSymbol { + name: "Struct", + def: Adt( + Struct( + Struct { + id: StructId( + 0, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 0..81, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + }, + container_name: None, + is_alias: false, + }, + FileSymbol { + name: "mul1", + def: Adt( + Struct( + Struct { + id: StructId( + 0, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 0..81, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + }, + container_name: None, + is_alias: true, + }, + FileSymbol { + name: "mul2", + def: Adt( + Struct( + Struct { + id: StructId( + 0, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 0..81, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + }, + container_name: None, + is_alias: true, + }, + FileSymbol { + name: "s1", + def: Adt( + Struct( + Struct { + id: StructId( + 0, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 0..81, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + }, + container_name: None, + is_alias: true, + }, + FileSymbol { + name: "s1", + def: Adt( + Struct( + Struct { + id: StructId( + 1, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 83..119, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 109..118, + }, + }, + container_name: None, + is_alias: true, + }, + FileSymbol { + name: "s2", + def: Adt( + Struct( + Struct { + id: StructId( + 0, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: HirFileId( + 0, + ), + ptr: SyntaxNodePtr { + kind: STRUCT, + range: 0..81, + }, + name_ptr: SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + }, + container_name: None, + is_alias: true, + }, + ], + ), +] diff --git a/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/crates/ide-db/src/test_data/test_symbol_index_collection.txt index 1e34dd633c..b5adfc13d9 100644 --- a/crates/ide-db/src/test_data/test_symbol_index_collection.txt +++ b/crates/ide-db/src/test_data/test_symbol_index_collection.txt @@ -31,6 +31,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "CONST", @@ -55,6 +56,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "CONST_WITH_INNER", @@ -79,6 +81,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "Enum", @@ -105,6 +108,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "Macro", @@ -131,6 +135,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "STATIC", @@ -155,6 +160,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "Struct", @@ -181,6 +187,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "StructFromMacro", @@ -207,6 +214,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "StructInFn", @@ -235,6 +243,7 @@ container_name: Some( "main", ), + is_alias: false, }, FileSymbol { name: "StructInNamedConst", @@ -263,6 +272,7 @@ container_name: Some( "CONST_WITH_INNER", ), + is_alias: false, }, FileSymbol { name: "StructInUnnamedConst", @@ -289,6 +299,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "Trait", @@ -313,6 +324,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "Union", @@ -339,6 +351,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "a_mod", @@ -365,6 +378,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "b_mod", @@ -391,6 +405,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "define_struct", @@ -417,6 +432,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "impl_fn", @@ -441,6 +457,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "macro_rules_macro", @@ -467,6 +484,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "main", @@ -491,6 +509,7 @@ }, }, container_name: None, + is_alias: false, }, FileSymbol { name: "trait_fn", @@ -517,6 +536,7 @@ container_name: Some( "Trait", ), + is_alias: false, }, ], ), @@ -554,6 +574,7 @@ }, }, container_name: None, + is_alias: false, }, ], ), @@ -591,6 +612,7 @@ }, }, container_name: None, + is_alias: false, }, ], ), diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index ef3f14d79d..4e641357e3 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -113,6 +113,7 @@ fn try_lookup_include_path( file_id, full_range: TextRange::new(0.into(), size), name: path.into(), + alias: None, focus_range: None, kind: None, container_name: None, diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 72d20af663..4e5f01e716 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -405,7 +405,7 @@ impl Analysis { self.with_db(|db| { symbol_index::world_symbols(db, query) .into_iter() // xx: should we make this a par iter? - .filter_map(|s| s.def.try_to_nav(db)) + .filter_map(|s| s.try_to_nav(db)) .collect::>() }) } diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs index d8ce79de37..65c37ca68c 100644 --- a/crates/ide/src/navigation_target.rs +++ b/crates/ide/src/navigation_target.rs @@ -45,6 +45,9 @@ pub struct NavigationTarget { pub container_name: Option, pub description: Option, pub docs: Option, + /// In addition to a `name` field, a `NavigationTarget` may also be aliased + /// In such cases we want a `NavigationTarget` to be accessible by its alias + pub alias: Option, } impl fmt::Debug for NavigationTarget { @@ -154,6 +157,7 @@ impl NavigationTarget { container_name: None, description: None, docs: None, + alias: None, } } } @@ -165,7 +169,8 @@ impl TryToNav for FileSymbol { Some(NavigationTarget { file_id: full_range.file_id, - name: self.name.clone(), + name: self.def.name(db)?.to_smol_str(), + alias: if self.is_alias { Some(self.name.clone()) } else { None }, kind: Some(hir::ModuleDefId::from(self.def).into()), full_range: full_range.range, focus_range: Some(name_range.range), @@ -466,6 +471,7 @@ impl ToNav for LocalSource { NavigationTarget { file_id, name, + alias: None, kind: Some(kind), full_range, focus_range, @@ -494,6 +500,7 @@ impl ToNav for hir::Label { NavigationTarget { file_id, name, + alias: None, kind: Some(SymbolKind::Label), full_range, focus_range, @@ -534,6 +541,7 @@ impl TryToNav for hir::TypeParam { Some(NavigationTarget { file_id, name, + alias: None, kind: Some(SymbolKind::TypeParam), full_range, focus_range, @@ -560,6 +568,7 @@ impl TryToNav for hir::LifetimeParam { Some(NavigationTarget { file_id, name, + alias: None, kind: Some(SymbolKind::LifetimeParam), full_range, focus_range: Some(full_range), @@ -589,6 +598,7 @@ impl TryToNav for hir::ConstParam { Some(NavigationTarget { file_id, name, + alias: None, kind: Some(SymbolKind::ConstParam), full_range, focus_range, @@ -643,6 +653,7 @@ fn foo() { enum FooInner { } } focus_range: 34..42, name: "FooInner", kind: Enum, + container_name: "foo", description: "enum FooInner", }, ] diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 64150cc2f7..ec57c02145 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs @@ -2232,14 +2232,14 @@ mod tests { file_id: FileId( 0, ), - full_range: 52..115, - focus_range: 67..75, - name: "foo_test", + full_range: 121..185, + focus_range: 136..145, + name: "foo2_test", kind: Function, }, kind: Test { test_id: Path( - "tests::foo_test", + "tests::foo2_test", ), attr: TestAttr { ignore: false, @@ -2253,14 +2253,14 @@ mod tests { file_id: FileId( 0, ), - full_range: 121..185, - focus_range: 136..145, - name: "foo2_test", + full_range: 52..115, + focus_range: 67..75, + name: "foo_test", kind: Function, }, kind: Test { test_id: Path( - "tests::foo2_test", + "tests::foo_test", ), attr: TestAttr { ignore: false, diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index 9c198eefc7..c8eda567db 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -520,7 +520,10 @@ pub(crate) fn handle_workspace_symbol( #[allow(deprecated)] let info = SymbolInformation { - name: nav.name.to_string(), + name: match nav.alias { + Some(ref alias) => format!("{} (alias {})", alias, nav.name), + None => format!("{}", nav.name), + }, kind: nav .kind .map(to_proto::symbol_kind)