Unnamed repository; edit this file 'description' to name the repository.
Allow navigation targets to be duplicated when the focus range lies in the macro definition site
38 files changed, 851 insertions, 488 deletions
diff --git a/Cargo.lock b/Cargo.lock index 5ba5c15a1d..876ba2546a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -608,6 +608,7 @@ dependencies = [ name = "ide" version = "0.0.0" dependencies = [ + "arrayvec", "cfg", "cov-mark", "crossbeam-channel", diff --git a/crates/base-db/src/fixture.rs b/crates/base-db/src/fixture.rs index cfba01a032..bfdd21555f 100644 --- a/crates/base-db/src/fixture.rs +++ b/crates/base-db/src/fixture.rs @@ -135,7 +135,7 @@ impl ChangeFixture { let mut file_set = FileSet::default(); let mut current_source_root_kind = SourceRootKind::Local; - let mut file_id = FileId(0); + let mut file_id = FileId::from_raw(0); let mut roots = Vec::new(); let mut file_position = None; @@ -210,7 +210,7 @@ impl ChangeFixture { let path = VfsPath::new_virtual_path(meta.path); file_set.insert(file_id, path); files.push(file_id); - file_id.0 += 1; + file_id = FileId::from_raw(file_id.index() + 1); } if crates.is_empty() { @@ -255,7 +255,7 @@ impl ChangeFixture { if let Some(mini_core) = mini_core { let core_file = file_id; - file_id.0 += 1; + file_id = FileId::from_raw(file_id.index() + 1); let mut fs = FileSet::default(); fs.insert(core_file, VfsPath::new_virtual_path("/sysroot/core/lib.rs".to_string())); @@ -296,7 +296,6 @@ impl ChangeFixture { let mut proc_macros = ProcMacros::default(); if !proc_macro_names.is_empty() { let proc_lib_file = file_id; - file_id.0 += 1; proc_macro_defs.extend(default_test_proc_macros()); let (proc_macro, source) = filter_test_proc_macros(&proc_macro_names, proc_macro_defs); diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs index 12b449932d..c2472363aa 100644 --- a/crates/base-db/src/input.rs +++ b/crates/base-db/src/input.rs @@ -880,7 +880,7 @@ mod tests { fn detect_cyclic_dependency_indirect() { let mut graph = CrateGraph::default(); let crate1 = graph.add_crate_root( - FileId(1u32), + FileId::from_raw(1u32), Edition2018, None, None, @@ -893,7 +893,7 @@ mod tests { None, ); let crate2 = graph.add_crate_root( - FileId(2u32), + FileId::from_raw(2u32), Edition2018, None, None, @@ -906,7 +906,7 @@ mod tests { None, ); let crate3 = graph.add_crate_root( - FileId(3u32), + FileId::from_raw(3u32), Edition2018, None, None, @@ -942,7 +942,7 @@ mod tests { fn detect_cyclic_dependency_direct() { let mut graph = CrateGraph::default(); let crate1 = graph.add_crate_root( - FileId(1u32), + FileId::from_raw(1u32), Edition2018, None, None, @@ -955,7 +955,7 @@ mod tests { None, ); let crate2 = graph.add_crate_root( - FileId(2u32), + FileId::from_raw(2u32), Edition2018, None, None, @@ -985,7 +985,7 @@ mod tests { fn it_works() { let mut graph = CrateGraph::default(); let crate1 = graph.add_crate_root( - FileId(1u32), + FileId::from_raw(1u32), Edition2018, None, None, @@ -998,7 +998,7 @@ mod tests { None, ); let crate2 = graph.add_crate_root( - FileId(2u32), + FileId::from_raw(2u32), Edition2018, None, None, @@ -1011,7 +1011,7 @@ mod tests { None, ); let crate3 = graph.add_crate_root( - FileId(3u32), + FileId::from_raw(3u32), Edition2018, None, None, @@ -1041,7 +1041,7 @@ mod tests { fn dashes_are_normalized() { let mut graph = CrateGraph::default(); let crate1 = graph.add_crate_root( - FileId(1u32), + FileId::from_raw(1u32), Edition2018, None, None, @@ -1054,7 +1054,7 @@ mod tests { None, ); let crate2 = graph.add_crate_root( - FileId(2u32), + FileId::from_raw(2u32), Edition2018, None, None, diff --git a/crates/base-db/src/span.rs b/crates/base-db/src/span.rs index 6723bf97fe..3464f4cb6d 100644 --- a/crates/base-db/src/span.rs +++ b/crates/base-db/src/span.rs @@ -70,7 +70,7 @@ impl fmt::Debug for SpanAnchor { } impl tt::SpanAnchor for SpanAnchor { - const DUMMY: Self = SpanAnchor { file_id: FileId(0), ast_id: ROOT_ERASED_FILE_AST_ID }; + const DUMMY: Self = SpanAnchor { file_id: FileId::BOGUS, ast_id: ROOT_ERASED_FILE_AST_ID }; } /// Input to the analyzer is a set of files, where each file is identified by @@ -99,12 +99,6 @@ impl From<HirFileId> for u32 { } } -impl From<u32> for HirFileId { - fn from(value: u32) -> Self { - HirFileId(value) - } -} - impl From<MacroCallId> for HirFileId { fn from(value: MacroCallId) -> Self { value.as_file() @@ -147,7 +141,7 @@ pub enum HirFileIdRepr { impl fmt::Debug for HirFileIdRepr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::FileId(arg0) => f.debug_tuple("FileId").field(&arg0.0).finish(), + Self::FileId(arg0) => f.debug_tuple("FileId").field(&arg0.index()).finish(), Self::MacroFile(arg0) => { f.debug_tuple("MacroFile").field(&arg0.macro_call_id.0).finish() } @@ -156,9 +150,9 @@ impl fmt::Debug for HirFileIdRepr { } impl From<FileId> for HirFileId { - fn from(FileId(id): FileId) -> Self { - assert!(id < Self::MAX_FILE_ID); - HirFileId(id) + fn from(id: FileId) -> Self { + assert!(id.index() < Self::MAX_FILE_ID); + HirFileId(id.index()) } } @@ -192,7 +186,7 @@ impl HirFileId { #[inline] pub fn file_id(self) -> Option<FileId> { match self.0 & Self::MACRO_FILE_TAG_MASK { - 0 => Some(FileId(self.0)), + 0 => Some(FileId::from_raw(self.0)), _ => None, } } @@ -200,7 +194,7 @@ impl HirFileId { #[inline] pub fn repr(self) -> HirFileIdRepr { match self.0 & Self::MACRO_FILE_TAG_MASK { - 0 => HirFileIdRepr::FileId(FileId(self.0)), + 0 => HirFileIdRepr::FileId(FileId::from_raw(self.0)), _ => HirFileIdRepr::MacroFile(MacroFileId { macro_call_id: MacroCallId(InternId::from(self.0 ^ Self::MACRO_FILE_TAG_MASK)), }), diff --git a/crates/hir-def/src/attr/tests.rs b/crates/hir-def/src/attr/tests.rs index 796f165c7c..0f98a4ec93 100644 --- a/crates/hir-def/src/attr/tests.rs +++ b/crates/hir-def/src/attr/tests.rs @@ -13,7 +13,7 @@ fn assert_parse_result(input: &str, expected: DocExpr) { let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); let tt = syntax_node_to_token_tree( tt.syntax(), - SpanMapRef::RealSpanMap(&RealSpanMap::absolute(FileId(0))), + SpanMapRef::RealSpanMap(&RealSpanMap::absolute(FileId::from_raw(0))), ); let cfg = DocExpr::parse(&tt); assert_eq!(cfg, expected); diff --git a/crates/hir-def/src/generics.rs b/crates/hir-def/src/generics.rs index 1c1c481a8e..0d95d916ff 100644 --- a/crates/hir-def/src/generics.rs +++ b/crates/hir-def/src/generics.rs @@ -524,7 +524,7 @@ fn file_id_and_params_of( (src.file_id, src.value.generic_param_list()) } // We won't be using this ID anyway - GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => (FileId(!0).into(), None), + GenericDefId::EnumVariantId(_) | GenericDefId::ConstId(_) => (FileId::BOGUS.into(), None), } } diff --git a/crates/hir-expand/src/files.rs b/crates/hir-expand/src/files.rs index ed8639d7a1..174e590569 100644 --- a/crates/hir-expand/src/files.rs +++ b/crates/hir-expand/src/files.rs @@ -307,6 +307,40 @@ impl InFile<TextRange> { }; range } + + pub fn original_node_file_range( + self, + db: &dyn db::ExpandDatabase, + ) -> (FileRange, SyntaxContextId) { + match self.file_id.repr() { + HirFileIdRepr::FileId(file_id) => { + (FileRange { file_id, range: self.value }, SyntaxContextId::ROOT) + } + HirFileIdRepr::MacroFile(mac_file) => { + match ExpansionInfo::new(db, mac_file).map_node_range_up(db, self.value) { + Some(it) => it, + None => { + let loc = db.lookup_intern_macro_call(mac_file.macro_call_id); + (loc.kind.original_call_range(db), SyntaxContextId::ROOT) + } + } + } + } + } + + pub fn original_node_file_range_opt( + self, + db: &dyn db::ExpandDatabase, + ) -> Option<(FileRange, SyntaxContextId)> { + match self.file_id.repr() { + HirFileIdRepr::FileId(file_id) => { + Some((FileRange { file_id, range: self.value }, SyntaxContextId::ROOT)) + } + HirFileIdRepr::MacroFile(mac_file) => { + ExpansionInfo::new(db, mac_file).map_node_range_up(db, self.value) + } + } + } } impl<N: AstNode> InFile<N> { diff --git a/crates/hir-expand/src/fixup.rs b/crates/hir-expand/src/fixup.rs index 77ddf7a48c..11775c531d 100644 --- a/crates/hir-expand/src/fixup.rs +++ b/crates/hir-expand/src/fixup.rs @@ -54,8 +54,10 @@ pub(crate) fn fixup_syntax(span_map: SpanMapRef<'_>, node: &SyntaxNode) -> Synta let dummy_range = TextRange::empty(TextSize::new(0)); // we use a file id of `FileId(!0)` to signal a fake node, and the text range's start offset as // the index into the replacement vec but only if the end points to !0 - let dummy_anchor = - SpanAnchor { file_id: FileId(!0), ast_id: ErasedFileAstId::from_raw(RawIdx::from(!0)) }; + let dummy_anchor = SpanAnchor { + file_id: FileId::from_raw(!0), + ast_id: ErasedFileAstId::from_raw(RawIdx::from(!0)), + }; let fake_span = |range| SpanData { range: dummy_range, anchor: dummy_anchor, @@ -308,7 +310,7 @@ fn reverse_fixups_(tt: &mut Subtree, undo_info: &[Subtree]) { .filter(|tt| match tt { tt::TokenTree::Leaf(leaf) => { let span = leaf.span(); - span.anchor.file_id != FileId(!0) || span.range.end() == TextSize::new(!0) + span.anchor.file_id != FileId::from_raw(!0) || span.range.end() == TextSize::new(!0) } tt::TokenTree::Subtree(_) => true, }) @@ -318,7 +320,7 @@ fn reverse_fixups_(tt: &mut Subtree, undo_info: &[Subtree]) { SmallVec::from_const([tt.into()]) } tt::TokenTree::Leaf(leaf) => { - if leaf.span().anchor.file_id == FileId(!0) { + if leaf.span().anchor.file_id == FileId::from_raw(!0) { let original = undo_info[u32::from(leaf.span().range.start()) as usize].clone(); if original.delimiter.kind == tt::DelimiterKind::Invisible { original.token_trees.into() @@ -373,7 +375,7 @@ mod tests { #[track_caller] fn check(ra_fixture: &str, mut expect: Expect) { let parsed = syntax::SourceFile::parse(ra_fixture); - let span_map = SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(FileId(0)))); + let span_map = SpanMap::RealSpanMap(Arc::new(RealSpanMap::absolute(FileId::from_raw(0)))); let fixups = super::fixup_syntax(span_map.as_ref(), &parsed.syntax_node()); let mut tt = mbe::syntax_node_to_token_tree_modified( &parsed.syntax_node(), diff --git a/crates/hir-expand/src/hygiene.rs b/crates/hir-expand/src/hygiene.rs index a809e92d62..7b03709ace 100644 --- a/crates/hir-expand/src/hygiene.rs +++ b/crates/hir-expand/src/hygiene.rs @@ -197,6 +197,7 @@ pub trait SyntaxContextExt { fn normalize_to_macro_rules(self, db: &dyn ExpandDatabase) -> Self; fn normalize_to_macros_2_0(self, db: &dyn ExpandDatabase) -> Self; fn parent_ctxt(self, db: &dyn ExpandDatabase) -> Self; + fn remove_mark(&mut self, db: &dyn ExpandDatabase) -> (Option<MacroCallId>, Transparency); fn outer_mark(self, db: &dyn ExpandDatabase) -> (Option<MacroCallId>, Transparency); fn marks(self, db: &dyn ExpandDatabase) -> Vec<(Option<MacroCallId>, Transparency)>; } @@ -223,6 +224,11 @@ impl SyntaxContextExt for SyntaxContextId { let data = db.lookup_intern_syntax_context(self); (data.outer_expn, data.outer_transparency) } + fn remove_mark(&mut self, db: &dyn ExpandDatabase) -> (Option<MacroCallId>, Transparency) { + let data = db.lookup_intern_syntax_context(*self); + *self = data.parent; + (data.outer_expn, data.outer_transparency) + } fn marks(self, db: &dyn ExpandDatabase) -> Vec<(Option<MacroCallId>, Transparency)> { let mut marks = marks_rev(self, db).collect::<Vec<_>>(); marks.reverse(); diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs index 71c98b2770..167ba0eb5d 100644 --- a/crates/hir-expand/src/lib.rs +++ b/crates/hir-expand/src/lib.rs @@ -380,6 +380,21 @@ impl MacroDefId { db.intern_macro_call(MacroCallLoc { def: self, krate, eager: None, kind, call_site }) } + pub fn definition_range(&self, db: &dyn db::ExpandDatabase) -> InFile<TextRange> { + match self.kind { + MacroDefKind::Declarative(id) + | MacroDefKind::BuiltIn(_, id) + | MacroDefKind::BuiltInAttr(_, id) + | MacroDefKind::BuiltInDerive(_, id) + | MacroDefKind::BuiltInEager(_, id) => { + id.with_value(db.ast_id_map(id.file_id).get(id.value).text_range()) + } + MacroDefKind::ProcMacro(_, _, id) => { + id.with_value(db.ast_id_map(id.file_id).get(id.value).text_range()) + } + } + } + pub fn ast_id(&self) -> Either<AstId<ast::Macro>, AstId<ast::Fn>> { match self.kind { MacroDefKind::ProcMacro(.., id) => return Either::Right(id), diff --git a/crates/hir-expand/src/quote.rs b/crates/hir-expand/src/quote.rs index 0950f5d287..acbde26c8d 100644 --- a/crates/hir-expand/src/quote.rs +++ b/crates/hir-expand/src/quote.rs @@ -261,8 +261,8 @@ mod tests { assert_eq!(quoted.to_string(), "hello"); let t = format!("{quoted:?}"); expect![[r#" - SUBTREE $$ SpanData { range: 0..0, anchor: SpanAnchor(FileId(4294967295), 0), ctx: SyntaxContextId(0) } SpanData { range: 0..0, anchor: SpanAnchor(FileId(4294967295), 0), ctx: SyntaxContextId(0) } - IDENT hello SpanData { range: 0..0, anchor: SpanAnchor(FileId(4294967295), 0), ctx: SyntaxContextId(0) }"#]].assert_eq(&t); + SUBTREE $$ SpanData { range: 0..0, anchor: SpanAnchor(FileId(937550), 0), ctx: SyntaxContextId(0) } SpanData { range: 0..0, anchor: SpanAnchor(FileId(937550), 0), ctx: SyntaxContextId(0) } + IDENT hello SpanData { range: 0..0, anchor: SpanAnchor(FileId(937550), 0), ctx: SyntaxContextId(0) }"#]].assert_eq(&t); } #[test] diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs index 0ac1db9311..1858533531 100644 --- a/crates/hir/src/attrs.rs +++ b/crates/hir/src/attrs.rs @@ -241,7 +241,7 @@ fn modpath_from_str(db: &dyn HirDatabase, link: &str) -> Option<ModPath> { ModPath::from_src( db.upcast(), ast_path, - SpanMapRef::RealSpanMap(&RealSpanMap::absolute(FileId(0))), + SpanMapRef::RealSpanMap(&RealSpanMap::absolute(FileId::BOGUS)), ) }; diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 53e60c5862..7e210740ea 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -125,6 +125,7 @@ pub use { }, hir_expand::{ attrs::{Attr, AttrId}, + hygiene::{marks_rev, SyntaxContextExt}, name::{known, Name}, tt, ExpandResult, HirFileId, HirFileIdExt, InFile, InMacroFile, InRealFile, MacroFileId, }, diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs index a392070fd8..2b03d575cb 100644 --- a/crates/hir/src/symbols.rs +++ b/crates/hir/src/symbols.rs @@ -9,7 +9,7 @@ use hir_def::{ }; use hir_expand::{HirFileId, InFile}; use hir_ty::db::HirDatabase; -use syntax::{ast::HasName, AstNode, SmolStr, SyntaxNode, SyntaxNodePtr}; +use syntax::{ast::HasName, AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr}; use crate::{Module, ModuleDef, Semantics}; @@ -32,7 +32,7 @@ pub struct DeclarationLocation { /// This points to the whole syntax node of the declaration. pub ptr: SyntaxNodePtr, /// This points to the [`syntax::ast::Name`] identifier of the declaration. - pub name_ptr: SyntaxNodePtr, + pub name_ptr: AstPtr<syntax::ast::Name>, } impl DeclarationLocation { @@ -185,7 +185,7 @@ impl<'a> SymbolCollector<'a> { let dec_loc = DeclarationLocation { hir_file_id: source.file_id, ptr: SyntaxNodePtr::new(use_tree_src.syntax()), - name_ptr: SyntaxNodePtr::new(name.syntax()), + name_ptr: AstPtr::new(&name), }; self.symbols.push(FileSymbol { @@ -289,7 +289,7 @@ impl<'a> SymbolCollector<'a> { let dec_loc = DeclarationLocation { hir_file_id: source.file_id, ptr: SyntaxNodePtr::new(source.value.syntax()), - name_ptr: SyntaxNodePtr::new(name_node.syntax()), + name_ptr: AstPtr::new(&name_node), }; if let Some(attrs) = def.attrs(self.db) { @@ -322,7 +322,7 @@ impl<'a> SymbolCollector<'a> { let dec_loc = DeclarationLocation { hir_file_id: declaration.file_id, ptr: SyntaxNodePtr::new(module.syntax()), - name_ptr: SyntaxNodePtr::new(name_node.syntax()), + name_ptr: AstPtr::new(&name_node), }; let def = ModuleDef::Module(module_id.into()); diff --git a/crates/ide-db/src/test_data/test_doc_alias.txt b/crates/ide-db/src/test_data/test_doc_alias.txt index 72a6eb5eab..4a72881fe5 100644 --- a/crates/ide-db/src/test_data/test_doc_alias.txt +++ b/crates/ide-db/src/test_data/test_doc_alias.txt @@ -27,10 +27,12 @@ kind: STRUCT, range: 83..119, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 109..118, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 109..118, + }, + ), }, container_name: None, is_alias: false, @@ -54,10 +56,12 @@ kind: STRUCT, range: 0..81, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 74..80, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + ), }, container_name: None, is_alias: false, @@ -81,10 +85,12 @@ kind: STRUCT, range: 0..81, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 74..80, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + ), }, container_name: None, is_alias: true, @@ -108,10 +114,12 @@ kind: STRUCT, range: 0..81, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 74..80, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + ), }, container_name: None, is_alias: true, @@ -135,10 +143,12 @@ kind: STRUCT, range: 0..81, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 74..80, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + ), }, container_name: None, is_alias: true, @@ -162,10 +172,12 @@ kind: STRUCT, range: 83..119, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 109..118, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 109..118, + }, + ), }, container_name: None, is_alias: true, @@ -189,10 +201,12 @@ kind: STRUCT, range: 0..81, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 74..80, - }, + name_ptr: AstPtr( + 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 375ac55981..da1f3167d7 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 @@ -25,10 +25,12 @@ kind: TYPE_ALIAS, range: 397..417, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 402..407, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 402..407, + }, + ), }, container_name: None, is_alias: false, @@ -50,10 +52,12 @@ kind: CONST, range: 340..361, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 346..351, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 346..351, + }, + ), }, container_name: None, is_alias: false, @@ -75,10 +79,12 @@ kind: CONST, range: 520..592, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 526..542, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 526..542, + }, + ), }, container_name: None, is_alias: false, @@ -102,10 +108,12 @@ kind: ENUM, range: 185..207, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 190..194, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 190..194, + }, + ), }, container_name: None, is_alias: false, @@ -129,10 +137,12 @@ kind: USE_TREE, range: 654..676, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 663..676, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 663..676, + }, + ), }, container_name: None, is_alias: false, @@ -156,10 +166,12 @@ kind: MACRO_DEF, range: 153..168, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 159..164, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 159..164, + }, + ), }, container_name: None, is_alias: false, @@ -181,10 +193,12 @@ kind: STATIC, range: 362..396, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 369..375, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 369..375, + }, + ), }, container_name: None, is_alias: false, @@ -208,10 +222,12 @@ kind: STRUCT, range: 170..184, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 177..183, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 177..183, + }, + ), }, container_name: None, is_alias: false, @@ -235,10 +251,12 @@ kind: STRUCT, range: 0..22, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 6..21, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 6..21, + }, + ), }, container_name: None, is_alias: false, @@ -262,10 +280,12 @@ kind: STRUCT, range: 318..336, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 325..335, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 325..335, + }, + ), }, container_name: Some( "main", @@ -291,10 +311,12 @@ kind: STRUCT, range: 555..581, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 562..580, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 562..580, + }, + ), }, container_name: Some( "CONST_WITH_INNER", @@ -320,10 +342,12 @@ kind: STRUCT, range: 479..507, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 486..506, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 486..506, + }, + ), }, container_name: None, is_alias: false, @@ -345,10 +369,12 @@ kind: TRAIT, range: 261..300, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 267..272, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 267..272, + }, + ), }, container_name: None, is_alias: false, @@ -372,10 +398,12 @@ kind: USE_TREE, range: 682..696, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 691..696, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 691..696, + }, + ), }, container_name: None, is_alias: false, @@ -399,10 +427,12 @@ kind: UNION, range: 208..222, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 214..219, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 214..219, + }, + ), }, container_name: None, is_alias: false, @@ -426,10 +456,12 @@ kind: MODULE, range: 419..457, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 423..428, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 423..428, + }, + ), }, container_name: None, is_alias: false, @@ -453,10 +485,12 @@ kind: MODULE, range: 594..604, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 598..603, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 598..603, + }, + ), }, container_name: None, is_alias: false, @@ -480,10 +514,12 @@ kind: MACRO_RULES, range: 51..131, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 64..77, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 64..77, + }, + ), }, container_name: None, is_alias: false, @@ -505,10 +541,12 @@ kind: FN, range: 242..257, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 245..252, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 245..252, + }, + ), }, container_name: None, is_alias: false, @@ -532,10 +570,12 @@ kind: MACRO_RULES, range: 1..48, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 14..31, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 14..31, + }, + ), }, container_name: None, is_alias: false, @@ -557,10 +597,12 @@ kind: FN, range: 302..338, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 305..309, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 305..309, + }, + ), }, container_name: None, is_alias: false, @@ -584,10 +626,12 @@ kind: USE_TREE, range: 611..648, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 628..648, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 628..648, + }, + ), }, container_name: None, is_alias: false, @@ -609,10 +653,12 @@ kind: FN, range: 279..298, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 282..290, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 282..290, + }, + ), }, container_name: Some( "Trait", @@ -649,10 +695,12 @@ kind: STRUCT, range: 435..455, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 442..454, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 442..454, + }, + ), }, container_name: None, is_alias: false, @@ -687,10 +735,12 @@ kind: USE_TREE, range: 111..143, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 127..143, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 127..143, + }, + ), }, container_name: None, is_alias: false, @@ -714,10 +764,12 @@ kind: STRUCT, range: 0..20, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 7..19, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 7..19, + }, + ), }, container_name: None, is_alias: false, @@ -741,10 +793,12 @@ kind: USE_TREE, range: 25..59, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 41..59, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 41..59, + }, + ), }, container_name: None, is_alias: false, @@ -768,10 +822,12 @@ kind: USE_TREE, range: 65..105, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 95..105, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 95..105, + }, + ), }, container_name: None, is_alias: false, @@ -795,10 +851,12 @@ kind: USE_TREE, range: 65..105, }, - name_ptr: SyntaxNodePtr { - kind: NAME, - range: 95..105, - }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 95..105, + }, + ), }, container_name: None, is_alias: false, diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml index d5c3439f95..0943574ec1 100644 --- a/crates/ide/Cargo.toml +++ b/crates/ide/Cargo.toml @@ -14,6 +14,7 @@ doctest = false [dependencies] cov-mark = "2.0.0-pre.1" crossbeam-channel = "0.5.5" +arrayvec = "0.7.4" either.workspace = true itertools.workspace = true tracing.workspace = true diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs index 5cc64e60ed..458b852e2a 100644 --- a/crates/ide/src/call_hierarchy.rs +++ b/crates/ide/src/call_hierarchy.rs @@ -1,5 +1,7 @@ //! Entry point for call-hierarchy +use std::iter; + use hir::{DescendPreference, Semantics}; use ide_db::{ defs::{Definition, NameClass, NameRefClass}, @@ -66,7 +68,10 @@ pub(crate) fn incoming_calls( def.try_to_nav(sema.db) }); if let Some(nav) = nav { - calls.add(nav, sema.original_range(name.syntax()).range); + calls.add(nav.call_site, sema.original_range(name.syntax()).range); + if let Some(other) = nav.def_site { + calls.add(other, sema.original_range(name.syntax()).range); + } } } } @@ -117,8 +122,9 @@ pub(crate) fn outgoing_calls( function.try_to_nav(db).zip(Some(range)) } }?; - Some((nav_target, range)) + Some(nav_target.into_iter().zip(iter::repeat(range))) }) + .flatten() .for_each(|(nav, range)| calls.add(nav, range)); Some(calls.into_items()) diff --git a/crates/ide/src/doc_links/tests.rs b/crates/ide/src/doc_links/tests.rs index 9ae70ae66f..f388aea4c3 100644 --- a/crates/ide/src/doc_links/tests.rs +++ b/crates/ide/src/doc_links/tests.rs @@ -1,4 +1,4 @@ -use std::ffi::OsStr; +use std::{ffi::OsStr, iter}; use expect_test::{expect, Expect}; use hir::Semantics; @@ -63,10 +63,12 @@ fn check_doc_links(ra_fixture: &str) { let defs = extract_definitions_from_docs(&docs); let actual: Vec<_> = defs .into_iter() - .map(|(_, link, ns)| { + .flat_map(|(_, link, ns)| { let def = resolve_doc_path_for_def(sema.db, cursor_def, &link, ns) .unwrap_or_else(|| panic!("Failed to resolve {link}")); - let nav_target = def.try_to_nav(sema.db).unwrap(); + def.try_to_nav(sema.db).unwrap().into_iter().zip(iter::repeat(link)) + }) + .map(|(nav_target, link)| { let range = FileRange { file_id: nav_target.file_id, range: nav_target.focus_or_full_range() }; (range, link) diff --git a/crates/ide/src/goto_declaration.rs b/crates/ide/src/goto_declaration.rs index ad7ec19645..fae1007435 100644 --- a/crates/ide/src/goto_declaration.rs +++ b/crates/ide/src/goto_declaration.rs @@ -66,6 +66,7 @@ pub(crate) fn goto_declaration( let item = trait_.items(db).into_iter().find(|it| it.name(db) == name)?; item.try_to_nav(db) }) + .flatten() .collect(); if info.is_empty() { diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index 5ca82a362f..7491879a67 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -52,7 +52,7 @@ pub(crate) fn goto_definition( if let Some(doc_comment) = token_as_doc_comment(&original_token) { return doc_comment.get_definition_with_descend_at(sema, offset, |def, _, link_range| { let nav = def.try_to_nav(db)?; - Some(RangeInfo::new(link_range, vec![nav])) + Some(RangeInfo::new(link_range, nav.collect())) }); } @@ -88,6 +88,7 @@ pub(crate) fn goto_definition( .resolved_crate(db) .map(|it| it.root_module().to_nav(sema.db)) .into_iter() + .flatten() .collect(); } try_filter_trait_item_definition(sema, &def) @@ -138,6 +139,7 @@ fn try_lookup_include_path( docs: None, }) } + /// finds the trait definition of an impl'd item, except function /// e.g. /// ```rust @@ -166,13 +168,13 @@ fn try_filter_trait_item_definition( .iter() .filter(|itm| discriminant(*itm) == discri_value) .find_map(|itm| (itm.name(db)? == name).then(|| itm.try_to_nav(db)).flatten()) - .map(|it| vec![it]) + .map(|it| it.collect()) } } } fn def_to_nav(db: &RootDatabase, def: Definition) -> Vec<NavigationTarget> { - def.try_to_nav(db).map(|it| vec![it]).unwrap_or_default() + def.try_to_nav(db).map(|it| it.collect()).unwrap_or_default() } #[cfg(test)] @@ -405,8 +407,6 @@ fn bar() { ); } - // FIXME: We should emit two targets here, one for the identifier in the declaration, one for - // the macro call #[test] fn goto_def_for_macro_defined_fn_no_arg() { check( @@ -414,7 +414,7 @@ fn bar() { //- /lib.rs macro_rules! define_fn { () => (fn foo() {}) - + //^^^ } define_fn!(); @@ -1748,9 +1748,9 @@ macro_rules! foo { fn $ident(Foo { $ident }: Foo) {} } } -foo!(foo$0); - //^^^ - //^^^ + foo!(foo$0); + //^^^ + //^^^ "#, ); check( diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs index bb474282dd..6384db39d7 100644 --- a/crates/ide/src/goto_implementation.rs +++ b/crates/ide/src/goto_implementation.rs @@ -82,7 +82,11 @@ pub(crate) fn goto_implementation( } fn impls_for_ty(sema: &Semantics<'_, RootDatabase>, ty: hir::Type) -> Vec<NavigationTarget> { - Impl::all_for_type(sema.db, ty).into_iter().filter_map(|imp| imp.try_to_nav(sema.db)).collect() + Impl::all_for_type(sema.db, ty) + .into_iter() + .filter_map(|imp| imp.try_to_nav(sema.db)) + .flatten() + .collect() } fn impls_for_trait( @@ -92,6 +96,7 @@ fn impls_for_trait( Impl::all_for_trait(sema.db, trait_) .into_iter() .filter_map(|imp| imp.try_to_nav(sema.db)) + .flatten() .collect() } @@ -109,6 +114,7 @@ fn impls_for_trait_item( })?; item.try_to_nav(sema.db) }) + .flatten() .collect() } diff --git a/crates/ide/src/goto_type_definition.rs b/crates/ide/src/goto_type_definition.rs index 83f134aaaf..ad393d9800 100644 --- a/crates/ide/src/goto_type_definition.rs +++ b/crates/ide/src/goto_type_definition.rs @@ -31,9 +31,11 @@ pub(crate) fn goto_type_definition( let mut res = Vec::new(); let mut push = |def: Definition| { - if let Some(nav) = def.try_to_nav(db) { - if !res.contains(&nav) { - res.push(nav); + if let Some(navs) = def.try_to_nav(db) { + for nav in navs { + if !res.contains(&nav) { + res.push(nav); + } } } }; diff --git a/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs index 8daff8c2eb..3aed007f3e 100644 --- a/crates/ide/src/highlight_related.rs +++ b/crates/ide/src/highlight_related.rs @@ -117,7 +117,7 @@ fn highlight_closure_captures( local .sources(sema.db) .into_iter() - .map(|x| x.to_nav(sema.db)) + .flat_map(|x| x.to_nav(sema.db)) .filter(|decl| decl.file_id == file_id) .filter_map(|decl| decl.focus_range) .map(move |range| HighlightedRange { range, category }) @@ -216,7 +216,7 @@ fn highlight_references( local .sources(sema.db) .into_iter() - .map(|x| x.to_nav(sema.db)) + .flat_map(|x| x.to_nav(sema.db)) .filter(|decl| decl.file_id == file_id) .filter_map(|decl| decl.focus_range) .map(|range| HighlightedRange { range, category }) @@ -225,21 +225,27 @@ fn highlight_references( }); } def => { - let hl_range = match def { + let navs = match def { Definition::Module(module) => { - Some(NavigationTarget::from_module_to_decl(sema.db, module)) + NavigationTarget::from_module_to_decl(sema.db, module) + } + def => match def.try_to_nav(sema.db) { + Some(it) => it, + None => continue, + }, + }; + for nav in navs { + if nav.file_id != file_id { + continue; + } + let hl_range = nav.focus_range.map(|range| { + let category = references::decl_mutability(&def, node, range) + .then_some(ReferenceCategory::Write); + HighlightedRange { range, category } + }); + if let Some(hl_range) = hl_range { + res.insert(hl_range); } - def => def.try_to_nav(sema.db), - } - .filter(|decl| decl.file_id == file_id) - .and_then(|decl| decl.focus_range) - .map(|range| { - let category = references::decl_mutability(&def, node, range) - .then_some(ReferenceCategory::Write); - HighlightedRange { range, category } - }); - if let Some(hl_range) = hl_range { - res.insert(hl_range); } } } diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 88a5b62342..5ad119ace8 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -21,6 +21,7 @@ use crate::{ doc_links::token_as_doc_comment, markdown_remove::remove_markdown, markup::Markup, + navigation_target::UpmappingResult, runnables::{runnable_fn, runnable_mod}, FileId, FilePosition, NavigationTarget, RangeInfo, Runnable, TryToNav, }; @@ -73,7 +74,7 @@ impl HoverAction { it.module(db)?, it.name(db).map(|name| name.display(db).to_string()), ), - nav: it.try_to_nav(db)?, + nav: it.try_to_nav(db)?.call_site(), }) }) .collect(); @@ -342,22 +343,26 @@ fn show_implementations_action(db: &RootDatabase, def: Definition) -> Option<Hov } let adt = match def { - Definition::Trait(it) => return it.try_to_nav(db).map(to_action), + Definition::Trait(it) => { + return it.try_to_nav(db).map(UpmappingResult::call_site).map(to_action) + } Definition::Adt(it) => Some(it), Definition::SelfType(it) => it.self_ty(db).as_adt(), _ => None, }?; - adt.try_to_nav(db).map(to_action) + adt.try_to_nav(db).map(UpmappingResult::call_site).map(to_action) } fn show_fn_references_action(db: &RootDatabase, def: Definition) -> Option<HoverAction> { match def { - Definition::Function(it) => it.try_to_nav(db).map(|nav_target| { - HoverAction::Reference(FilePosition { - file_id: nav_target.file_id, - offset: nav_target.focus_or_full_range().start(), + Definition::Function(it) => { + it.try_to_nav(db).map(UpmappingResult::call_site).map(|nav_target| { + HoverAction::Reference(FilePosition { + file_id: nav_target.file_id, + offset: nav_target.focus_or_full_range().start(), + }) }) - }), + } _ => None, } } diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 7ea9d4f103..ca334e9157 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -315,6 +315,7 @@ impl HirWrite for InlayHintLabelBuilder<'_> { } self.make_new_part(); let Some(location) = ModuleDef::from(def).try_to_nav(self.db) else { return }; + let location = location.call_site(); let location = FileRange { file_id: location.file_id, range: location.focus_or_full_range() }; self.location = Some(location); diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 3390331e0e..d8f6e4e1b1 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -100,7 +100,7 @@ pub use crate::{ markup::Markup, moniker::{MonikerDescriptorKind, MonikerKind, MonikerResult, PackageInformation}, move_item::Direction, - navigation_target::NavigationTarget, + navigation_target::{NavigationTarget, UpmappingResult}, prime_caches::ParallelPrimeCachesProgress, references::ReferenceSearchResult, rename::RenameError, @@ -230,7 +230,7 @@ impl Analysis { // `AnalysisHost` for creating a fully-featured analysis. pub fn from_single_file(text: String) -> (Analysis, FileId) { let mut host = AnalysisHost::default(); - let file_id = FileId(0); + let file_id = FileId::from_raw(0); let mut file_set = FileSet::default(); file_set.insert(file_id, VfsPath::new_virtual_path("/main.rs".to_string())); let source_root = SourceRoot::new_local(file_set); @@ -413,6 +413,7 @@ impl Analysis { symbol_index::world_symbols(db, query) .into_iter() // xx: should we make this a par iter? .filter_map(|s| s.try_to_nav(db)) + .map(UpmappingResult::call_site) .collect::<Vec<_>>() }) } diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs index df0c4a6ade..31f4aad41e 100644 --- a/crates/ide/src/navigation_target.rs +++ b/crates/ide/src/navigation_target.rs @@ -2,6 +2,7 @@ use std::fmt; +use arrayvec::ArrayVec; use either::Either; use hir::{ db::ExpandDatabase, symbols::FileSymbol, AssocItem, FieldSource, HasContainer, HasSource, @@ -72,15 +73,15 @@ impl fmt::Debug for NavigationTarget { } pub(crate) trait ToNav { - fn to_nav(&self, db: &RootDatabase) -> NavigationTarget; + fn to_nav(&self, db: &RootDatabase) -> UpmappingResult<NavigationTarget>; } pub(crate) trait TryToNav { - fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget>; + fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>>; } impl<T: TryToNav, U: TryToNav> TryToNav for Either<T, U> { - fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { + fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> { match self { Either::Left(it) => it.try_to_nav(db), Either::Right(it) => it.try_to_nav(db), @@ -93,23 +94,30 @@ impl NavigationTarget { self.focus_range.unwrap_or(self.full_range) } - pub(crate) fn from_module_to_decl(db: &RootDatabase, module: hir::Module) -> NavigationTarget { + pub(crate) fn from_module_to_decl( + db: &RootDatabase, + module: hir::Module, + ) -> UpmappingResult<NavigationTarget> { let name = module.name(db).map(|it| it.to_smol_str()).unwrap_or_default(); - if let Some(InFile { value, file_id }) = &module.declaration_source(db) { - let (file_id, full_range, focus_range) = - orig_range_with_focus(db, *file_id, value.syntax(), value.name()); - let mut res = NavigationTarget::from_syntax( - file_id, - name, - focus_range, - full_range, - SymbolKind::Module, - ); - res.docs = module.docs(db); - res.description = Some(module.display(db).to_string()); - return res; + match module.declaration_source(db) { + Some(InFile { value, file_id }) => { + orig_range_with_focus(db, file_id, value.syntax(), value.name()).map( + |(FileRange { file_id, range: full_range }, focus_range)| { + let mut res = NavigationTarget::from_syntax( + file_id, + name.clone(), + focus_range, + full_range, + SymbolKind::Module, + ); + res.docs = module.docs(db); + res.description = Some(module.display(db).to_string()); + res + }, + ) + } + _ => module.to_nav(db), } - module.to_nav(db) } #[cfg(test)] @@ -135,13 +143,14 @@ impl NavigationTarget { db: &RootDatabase, InFile { file_id, value }: InFile<&dyn ast::HasName>, kind: SymbolKind, - ) -> NavigationTarget { - let name = value.name().map(|it| it.text().into()).unwrap_or_else(|| "_".into()); - - let (file_id, full_range, focus_range) = - orig_range_with_focus(db, file_id, value.syntax(), value.name()); + ) -> UpmappingResult<NavigationTarget> { + let name: SmolStr = value.name().map(|it| it.text().into()).unwrap_or_else(|| "_".into()); - NavigationTarget::from_syntax(file_id, name, focus_range, full_range, kind) + orig_range_with_focus(db, file_id, value.syntax(), value.name()).map( + |(FileRange { file_id, range: full_range }, focus_range)| { + NavigationTarget::from_syntax(file_id, name.clone(), focus_range, full_range, kind) + }, + ) } fn from_syntax( @@ -166,49 +175,51 @@ impl NavigationTarget { } impl TryToNav for FileSymbol { - fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { - let full_range = self.loc.original_range(db); - let focus_range = self.loc.original_name_range(db); - let focus_range = if focus_range.file_id == full_range.file_id - && full_range.range.contains_range(focus_range.range) - { - Some(focus_range.range) - } else { - None - }; - - Some(NavigationTarget { - file_id: full_range.file_id, - name: self - .is_alias - .then(|| self.def.name(db)) - .flatten() - .map_or_else(|| self.name.clone(), |it| it.to_smol_str()), - alias: self.is_alias.then(|| self.name.clone()), - kind: Some(hir::ModuleDefId::from(self.def).into()), - full_range: full_range.range, - focus_range, - container_name: self.container_name.clone(), - description: match self.def { - hir::ModuleDef::Module(it) => Some(it.display(db).to_string()), - hir::ModuleDef::Function(it) => Some(it.display(db).to_string()), - hir::ModuleDef::Adt(it) => Some(it.display(db).to_string()), - hir::ModuleDef::Variant(it) => Some(it.display(db).to_string()), - hir::ModuleDef::Const(it) => Some(it.display(db).to_string()), - hir::ModuleDef::Static(it) => Some(it.display(db).to_string()), - hir::ModuleDef::Trait(it) => Some(it.display(db).to_string()), - hir::ModuleDef::TraitAlias(it) => Some(it.display(db).to_string()), - hir::ModuleDef::TypeAlias(it) => Some(it.display(db).to_string()), - hir::ModuleDef::Macro(it) => Some(it.display(db).to_string()), - hir::ModuleDef::BuiltinType(_) => None, - }, - docs: None, - }) + fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> { + let root = db.parse_or_expand(self.loc.hir_file_id); + self.loc.ptr.to_node(&root); + Some( + orig_range_with_focus( + db, + self.loc.hir_file_id, + &self.loc.ptr.to_node(&root), + Some(self.loc.name_ptr.to_node(&root)), + ) + .map(|(FileRange { file_id, range: full_range }, focus_range)| { + NavigationTarget { + file_id, + name: self + .is_alias + .then(|| self.def.name(db)) + .flatten() + .map_or_else(|| self.name.clone(), |it| it.to_smol_str()), + alias: self.is_alias.then(|| self.name.clone()), + kind: Some(hir::ModuleDefId::from(self.def).into()), + full_range, + focus_range, + container_name: self.container_name.clone(), + description: match self.def { + hir::ModuleDef::Module(it) => Some(it.display(db).to_string()), + hir::ModuleDef::Function(it) => Some(it.display(db).to_string()), + hir::ModuleDef::Adt(it) => Some(it.display(db).to_string()), + hir::ModuleDef::Variant(it) => Some(it.display(db).to_string()), + hir::ModuleDef::Const(it) => Some(it.display(db).to_string()), + hir::ModuleDef::Static(it) => Some(it.display(db).to_string()), + hir::ModuleDef::Trait(it) => Some(it.display(db).to_string()), + hir::ModuleDef::TraitAlias(it) => Some(it.display(db).to_string()), + hir::ModuleDef::TypeAlias(it) => Some(it.display(db).to_string()), + hir::ModuleDef::Macro(it) => Some(it.display(db).to_string()), + hir::ModuleDef::BuiltinType(_) => None, + }, + docs: None, + } + }), + ) } } impl TryToNav for Definition { - fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { + fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> { match self { Definition::Local(it) => Some(it.to_nav(db)), Definition::Label(it) => Some(it.to_nav(db)), @@ -236,7 +247,7 @@ impl TryToNav for Definition { } impl TryToNav for hir::ModuleDef { - fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { + fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> { match self { hir::ModuleDef::Module(it) => Some(it.to_nav(db)), hir::ModuleDef::Function(it) => it.try_to_nav(db), @@ -334,22 +345,26 @@ where D: HasSource + ToNavFromAst + Copy + HasDocs + HirDisplay, D::Ast: ast::HasName, { - fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { + fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> { let src = self.source(db)?; - let mut res = NavigationTarget::from_named( - db, - src.as_ref().map(|it| it as &dyn ast::HasName), - D::KIND, - ); - res.docs = self.docs(db); - res.description = Some(self.display(db).to_string()); - res.container_name = self.container_name(db); - Some(res) + Some( + NavigationTarget::from_named( + db, + src.as_ref().map(|it| it as &dyn ast::HasName), + D::KIND, + ) + .map(|mut res| { + res.docs = self.docs(db); + res.description = Some(self.display(db).to_string()); + res.container_name = self.container_name(db); + res + }), + ) } } impl ToNav for hir::Module { - fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { + fn to_nav(&self, db: &RootDatabase) -> UpmappingResult<NavigationTarget> { let InFile { file_id, value } = self.definition_source(db); let name = self.name(db).map(|it| it.to_smol_str()).unwrap_or_default(); @@ -358,13 +373,23 @@ impl ToNav for hir::Module { ModuleSource::Module(node) => (node.syntax(), node.name()), ModuleSource::BlockExpr(node) => (node.syntax(), None), }; - let (file_id, full_range, focus_range) = orig_range_with_focus(db, file_id, syntax, focus); - NavigationTarget::from_syntax(file_id, name, focus_range, full_range, SymbolKind::Module) + + orig_range_with_focus(db, file_id, syntax, focus).map( + |(FileRange { file_id, range: full_range }, focus_range)| { + NavigationTarget::from_syntax( + file_id, + name.clone(), + focus_range, + full_range, + SymbolKind::Module, + ) + }, + ) } } impl TryToNav for hir::Impl { - fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { + fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> { let InFile { file_id, value } = self.source(db)?; let derive_path = self.as_builtin_derive_path(db); @@ -373,82 +398,100 @@ impl TryToNav for hir::Impl { None => (file_id, value.self_ty(), value.syntax()), }; - let (file_id, full_range, focus_range) = orig_range_with_focus(db, file_id, syntax, focus); - Some(NavigationTarget::from_syntax( - file_id, - "impl".into(), - focus_range, - full_range, - SymbolKind::Impl, + Some(orig_range_with_focus(db, file_id, syntax, focus).map( + |(FileRange { file_id, range: full_range }, focus_range)| { + NavigationTarget::from_syntax( + file_id, + "impl".into(), + focus_range, + full_range, + SymbolKind::Impl, + ) + }, )) } } impl TryToNav for hir::ExternCrateDecl { - fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { + fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> { let src = self.source(db)?; let InFile { file_id, value } = src; let focus = value .rename() .map_or_else(|| value.name_ref().map(Either::Left), |it| it.name().map(Either::Right)); - let (file_id, full_range, focus_range) = - orig_range_with_focus(db, file_id, value.syntax(), focus); - let mut res = NavigationTarget::from_syntax( - file_id, - self.alias_or_name(db).unwrap_or_else(|| self.name(db)).to_smol_str(), - focus_range, - full_range, - SymbolKind::Module, - ); - res.docs = self.docs(db); - res.description = Some(self.display(db).to_string()); - res.container_name = container_name(db, *self); - Some(res) + Some(orig_range_with_focus(db, file_id, value.syntax(), focus).map( + |(FileRange { file_id, range: full_range }, focus_range)| { + let mut res = NavigationTarget::from_syntax( + file_id, + self.alias_or_name(db).unwrap_or_else(|| self.name(db)).to_smol_str(), + focus_range, + full_range, + SymbolKind::Module, + ); + + res.docs = self.docs(db); + res.description = Some(self.display(db).to_string()); + res.container_name = container_name(db, *self); + res + }, + )) } } impl TryToNav for hir::Field { - fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { + fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> { let src = self.source(db)?; let field_source = match &src.value { FieldSource::Named(it) => { - let mut res = - NavigationTarget::from_named(db, src.with_value(it), SymbolKind::Field); - res.docs = self.docs(db); - res.description = Some(self.display(db).to_string()); - res - } - FieldSource::Pos(it) => { - let FileRange { file_id, range } = - src.with_value(it.syntax()).original_file_range(db); - NavigationTarget::from_syntax(file_id, "".into(), None, range, SymbolKind::Field) + NavigationTarget::from_named(db, src.with_value(it), SymbolKind::Field).map( + |mut res| { + res.docs = self.docs(db); + res.description = Some(self.display(db).to_string()); + res + }, + ) } + FieldSource::Pos(it) => orig_range(db, src.file_id, it.syntax()).map( + |(FileRange { file_id, range: full_range }, focus_range)| { + NavigationTarget::from_syntax( + file_id, + format!("{}", self.index()).into(), + focus_range, + full_range, + SymbolKind::Field, + ) + }, + ), }; Some(field_source) } } impl TryToNav for hir::Macro { - fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { + fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> { let src = self.source(db)?; let name_owner: &dyn ast::HasName = match &src.value { Either::Left(it) => it, Either::Right(it) => it, }; - let mut res = NavigationTarget::from_named( - db, - src.as_ref().with_value(name_owner), - self.kind(db).into(), - ); - res.docs = self.docs(db); - Some(res) + Some( + NavigationTarget::from_named( + db, + src.as_ref().with_value(name_owner), + self.kind(db).into(), + ) + .map(|mut res| { + res.docs = self.docs(db); + res + }), + ) } } impl TryToNav for hir::Adt { - fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { + fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> { match self { hir::Adt::Struct(it) => it.try_to_nav(db), hir::Adt::Union(it) => it.try_to_nav(db), @@ -458,7 +501,7 @@ impl TryToNav for hir::Adt { } impl TryToNav for hir::AssocItem { - fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { + fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> { match self { AssocItem::Function(it) => it.try_to_nav(db), AssocItem::Const(it) => it.try_to_nav(db), @@ -468,7 +511,7 @@ impl TryToNav for hir::AssocItem { } impl TryToNav for hir::GenericParam { - fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { + fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> { match self { hir::GenericParam::TypeParam(it) => it.try_to_nav(db), hir::GenericParam::ConstParam(it) => it.try_to_nav(db), @@ -478,7 +521,7 @@ impl TryToNav for hir::GenericParam { } impl ToNav for LocalSource { - fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { + fn to_nav(&self, db: &RootDatabase) -> UpmappingResult<NavigationTarget> { let InFile { file_id, value } = &self.source; let file_id = *file_id; let local = self.local; @@ -487,60 +530,61 @@ impl ToNav for LocalSource { Either::Right(it) => (it.syntax(), it.name()), }; - let (file_id, full_range, focus_range) = orig_range_with_focus(db, file_id, node, name); - - let name = local.name(db).to_smol_str(); - let kind = if local.is_self(db) { - SymbolKind::SelfParam - } else if local.is_param(db) { - SymbolKind::ValueParam - } else { - SymbolKind::Local - }; - NavigationTarget { - file_id, - name, - alias: None, - kind: Some(kind), - full_range, - focus_range, - container_name: None, - description: None, - docs: None, - } + orig_range_with_focus(db, file_id, node, name).map( + |(FileRange { file_id, range: full_range }, focus_range)| { + let name = local.name(db).to_smol_str(); + let kind = if local.is_self(db) { + SymbolKind::SelfParam + } else if local.is_param(db) { + SymbolKind::ValueParam + } else { + SymbolKind::Local + }; + NavigationTarget { + file_id, + name, + alias: None, + kind: Some(kind), + full_range, + focus_range, + container_name: None, + description: None, + docs: None, + } + }, + ) } } impl ToNav for hir::Local { - fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { + fn to_nav(&self, db: &RootDatabase) -> UpmappingResult<NavigationTarget> { self.primary_source(db).to_nav(db) } } impl ToNav for hir::Label { - fn to_nav(&self, db: &RootDatabase) -> NavigationTarget { + fn to_nav(&self, db: &RootDatabase) -> UpmappingResult<NavigationTarget> { let InFile { file_id, value } = self.source(db); let name = self.name(db).to_smol_str(); - let (file_id, full_range, focus_range) = - orig_range_with_focus(db, file_id, value.syntax(), value.lifetime()); - - NavigationTarget { - file_id, - name, - alias: None, - kind: Some(SymbolKind::Label), - full_range, - focus_range, - container_name: None, - description: None, - docs: None, - } + orig_range_with_focus(db, file_id, value.syntax(), value.lifetime()).map( + |(FileRange { file_id, range: full_range }, focus_range)| NavigationTarget { + file_id, + name: name.clone(), + alias: None, + kind: Some(SymbolKind::Label), + full_range, + focus_range, + container_name: None, + description: None, + docs: None, + }, + ) } } impl TryToNav for hir::TypeParam { - fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { + fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> { let InFile { file_id, value } = self.merge().source(db)?; let name = self.name(db).to_smol_str(); @@ -559,51 +603,51 @@ impl TryToNav for hir::TypeParam { }; let focus = value.as_ref().either(|it| it.name(), |it| it.name()); - let (file_id, full_range, focus_range) = orig_range_with_focus(db, file_id, syntax, focus); - - Some(NavigationTarget { - file_id, - name, - alias: None, - kind: Some(SymbolKind::TypeParam), - full_range, - focus_range, - container_name: None, - description: None, - docs: None, - }) + Some(orig_range_with_focus(db, file_id, syntax, focus).map( + |(FileRange { file_id, range: full_range }, focus_range)| NavigationTarget { + file_id, + name: name.clone(), + alias: None, + kind: Some(SymbolKind::TypeParam), + full_range, + focus_range, + container_name: None, + description: None, + docs: None, + }, + )) } } impl TryToNav for hir::TypeOrConstParam { - fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { + fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> { self.split(db).try_to_nav(db) } } impl TryToNav for hir::LifetimeParam { - fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { + fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> { let InFile { file_id, value } = self.source(db)?; let name = self.name(db).to_smol_str(); - let FileRange { file_id, range } = - InFile::new(file_id, value.syntax()).original_file_range(db); - Some(NavigationTarget { - file_id, - name, - alias: None, - kind: Some(SymbolKind::LifetimeParam), - full_range: range, - focus_range: Some(range), - container_name: None, - description: None, - docs: None, - }) + Some(orig_range(db, file_id, value.syntax()).map( + |(FileRange { file_id, range: full_range }, focus_range)| NavigationTarget { + file_id, + name: name.clone(), + alias: None, + kind: Some(SymbolKind::LifetimeParam), + full_range, + focus_range, + container_name: None, + description: None, + docs: None, + }, + )) } } impl TryToNav for hir::ConstParam { - fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { + fn try_to_nav(&self, db: &RootDatabase) -> Option<UpmappingResult<NavigationTarget>> { let InFile { file_id, value } = self.merge().source(db)?; let name = self.name(db).to_smol_str(); @@ -615,46 +659,180 @@ impl TryToNav for hir::ConstParam { } }; - let (file_id, full_range, focus_range) = - orig_range_with_focus(db, file_id, value.syntax(), value.name()); - Some(NavigationTarget { - file_id, - name, - alias: None, - kind: Some(SymbolKind::ConstParam), - full_range, - focus_range, - container_name: None, - description: None, - docs: None, - }) + Some(orig_range_with_focus(db, file_id, value.syntax(), value.name()).map( + |(FileRange { file_id, range: full_range }, focus_range)| NavigationTarget { + file_id, + name: name.clone(), + alias: None, + kind: Some(SymbolKind::ConstParam), + full_range, + focus_range, + container_name: None, + description: None, + docs: None, + }, + )) + } +} + +#[derive(Debug)] +pub struct UpmappingResult<T> { + /// The macro call site. + pub call_site: T, + /// The macro definition site, if relevant. + pub def_site: Option<T>, +} + +impl<T> UpmappingResult<T> { + pub fn call_site(self) -> T { + self.call_site + } + + pub fn collect<FI: FromIterator<T>>(self) -> FI { + FI::from_iter(self.into_iter()) + } +} + +impl<T> IntoIterator for UpmappingResult<T> { + type Item = T; + + type IntoIter = <ArrayVec<T, 2> as IntoIterator>::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.def_site + .into_iter() + .chain(Some(self.call_site)) + .collect::<ArrayVec<_, 2>>() + .into_iter() + } +} + +impl<T> UpmappingResult<T> { + fn map<U>(self, f: impl Fn(T) -> U) -> UpmappingResult<U> { + UpmappingResult { call_site: f(self.call_site), def_site: self.def_site.map(f) } } } /// Returns the original range of the syntax node, and the range of the name mapped out of macro expansions -/// Additionally verifies that the name span is in bounds and related to the original range. +/// May return two results if the mapped node originates from a macro definition in which case the +/// second result is the creating macro call. fn orig_range_with_focus( db: &RootDatabase, hir_file: HirFileId, value: &SyntaxNode, name: Option<impl AstNode>, -) -> (FileId, TextRange, Option<TextRange>) { - let FileRange { file_id, range } = - match InFile::new(hir_file, value).original_file_range_opt(db) { - Some((range, ctxt)) if ctxt.is_root() => range, - _ => db - .lookup_intern_macro_call(hir_file.macro_file().unwrap().macro_call_id) - .kind - .original_call_range(db), +) -> UpmappingResult<(FileRange, Option<TextRange>)> { + let Some(name) = name else { return orig_range(db, hir_file, value) }; + + let call_range = || { + db.lookup_intern_macro_call(hir_file.macro_file().unwrap().macro_call_id) + .kind + .original_call_range(db) + }; + + let def_range = || { + db.lookup_intern_macro_call(hir_file.macro_file().unwrap().macro_call_id) + .def + .definition_range(db) + }; + + // FIXME What about include!d things + + let value_range = InFile::new(hir_file, value).original_file_range_opt(db); + let ((call_site_range, call_site_focus), def_site) = + match InFile::new(hir_file, name.syntax()).original_file_range_opt(db) { + // call site name + Some((focus_range, ctxt)) if ctxt.is_root() => { + // Try to upmap the node as well, if it ends up in the def site, go back to the call site + ( + ( + match value_range { + // name is in the node in the macro input so we can return it + Some((range, ctxt)) + if ctxt.is_root() + && range.file_id == focus_range.file_id + && range.range.contains_range(focus_range.range) => + { + range + } + // name lies outside the node, so instead point to the macro call which + // *should* contain the name + _ => call_range(), + }, + Some(focus_range), + ), + // no def site relevant + None, + ) + } + + // def site name + // FIXME: This can be de improved + Some((focus_range, _ctxt)) => { + match value_range { + // but overall node is in macro input + Some((range, ctxt)) if ctxt.is_root() => ( + // node mapped up in call site, show the node + (range, None), + // def site, if the name is in the (possibly) upmapped def site range, show the + // def site + { + let (def_site, _) = def_range().original_node_file_range(db); + (def_site.file_id == focus_range.file_id + && def_site.range.contains_range(focus_range.range)) + .then_some((def_site, Some(focus_range))) + }, + ), + // node is in macro def, just show the focus + _ => ( + // show the macro call + (call_range(), None), + Some((focus_range, Some(focus_range))), + ), + } + } + // lost name? can't happen for single tokens + None => return orig_range(db, hir_file, value), }; - let focus_range = name - .and_then(|it| InFile::new(hir_file, it.syntax()).original_file_range_opt(db)) - .filter(|(frange, ctxt)| { - ctxt.is_root() && frange.file_id == file_id && frange.range.contains_range(frange.range) - }) - .map(|(frange, _ctxt)| frange.range); - - (file_id, range, focus_range) + + UpmappingResult { + call_site: ( + call_site_range, + call_site_focus.and_then(|FileRange { file_id, range }| { + if call_site_range.file_id == file_id && call_site_range.range.contains_range(range) + { + Some(range) + } else { + None + } + }), + ), + def_site: def_site.map(|(def_site_range, def_site_focus)| { + ( + def_site_range, + def_site_focus.and_then(|FileRange { file_id, range }| { + if def_site_range.file_id == file_id + && def_site_range.range.contains_range(range) + { + Some(range) + } else { + None + } + }), + ) + }), + } +} + +fn orig_range( + db: &RootDatabase, + hir_file: HirFileId, + value: &SyntaxNode, +) -> UpmappingResult<(FileRange, Option<TextRange>)> { + UpmappingResult { + call_site: (InFile::new(hir_file, value).original_file_range(db), None), + def_site: None, + } } #[cfg(test)] diff --git a/crates/ide/src/parent_module.rs b/crates/ide/src/parent_module.rs index 506f9452cf..413dbf9c5d 100644 --- a/crates/ide/src/parent_module.rs +++ b/crates/ide/src/parent_module.rs @@ -45,11 +45,11 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<Na Some(module) => sema .to_def(&module) .into_iter() - .map(|module| NavigationTarget::from_module_to_decl(db, module)) + .flat_map(|module| NavigationTarget::from_module_to_decl(db, module)) .collect(), None => sema .to_module_defs(position.file_id) - .map(|module| NavigationTarget::from_module_to_decl(db, module)) + .flat_map(|module| NavigationTarget::from_module_to_decl(db, module)) .collect(), } } diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index b805ddfa26..6c0fb0baf2 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs @@ -9,6 +9,8 @@ //! at the index that the match starts at and its tree parent is //! resolved to the search element definition, we get a reference. +use std::collections::HashMap; + use hir::{DescendPreference, PathResolution, Semantics}; use ide_db::{ base_db::FileId, @@ -60,19 +62,6 @@ pub(crate) fn find_all_refs( let syntax = sema.parse(position.file_id).syntax().clone(); let make_searcher = |literal_search: bool| { move |def: Definition| { - let declaration = match def { - Definition::Module(module) => { - Some(NavigationTarget::from_module_to_decl(sema.db, module)) - } - def => def.try_to_nav(sema.db), - } - .map(|nav| { - let decl_range = nav.focus_or_full_range(); - Declaration { - is_mut: decl_mutability(&def, sema.parse(nav.file_id).syntax(), decl_range), - nav, - } - }); let mut usages = def.usages(sema).set_scope(search_scope.as_ref()).include_self_refs().all(); @@ -80,7 +69,7 @@ pub(crate) fn find_all_refs( retain_adt_literal_usages(&mut usages, def, sema); } - let references = usages + let mut references = usages .into_iter() .map(|(file_id, refs)| { ( @@ -91,8 +80,30 @@ pub(crate) fn find_all_refs( .collect(), ) }) - .collect(); - + .collect::<HashMap<_, Vec<_>, _>>(); + let declaration = match def { + Definition::Module(module) => { + Some(NavigationTarget::from_module_to_decl(sema.db, module)) + } + def => def.try_to_nav(sema.db), + } + .map(|nav| { + let (nav, extra_ref) = match nav.def_site { + Some(call) => (call, Some(nav.call_site)), + None => (nav.call_site, None), + }; + if let Some(extra_ref) = extra_ref { + references + .entry(extra_ref.file_id) + .or_default() + .push((extra_ref.focus_or_full_range(), None)); + } + let decl_range = nav.focus_or_full_range(); + Declaration { + is_mut: decl_mutability(&def, sema.parse(nav.file_id).syntax(), decl_range), + nav, + } + }); ReferenceSearchResult { declaration, references } } }; @@ -882,7 +893,7 @@ pub(super) struct Foo$0 { check_with_scope( code, - Some(SearchScope::single_file(FileId(2))), + Some(SearchScope::single_file(FileId::from_raw(2))), expect![[r#" quux Function FileId(0) 19..35 26..30 @@ -1181,7 +1192,7 @@ fn foo<'a, 'b: 'a>(x: &'a$0 ()) -> &'a () where &'a (): Foo<'a> { } "#, expect![[r#" - 'a LifetimeParam FileId(0) 55..57 55..57 + 'a LifetimeParam FileId(0) 55..57 FileId(0) 63..65 FileId(0) 71..73 @@ -1199,7 +1210,7 @@ fn foo<'a, 'b: 'a>(x: &'a$0 ()) -> &'a () where &'a (): Foo<'a> { type Foo<'a, T> where T: 'a$0 = &'a T; "#, expect![[r#" - 'a LifetimeParam FileId(0) 9..11 9..11 + 'a LifetimeParam FileId(0) 9..11 FileId(0) 25..27 FileId(0) 31..33 @@ -1221,7 +1232,7 @@ impl<'a> Foo<'a> for &'a () { } "#, expect![[r#" - 'a LifetimeParam FileId(0) 47..49 47..49 + 'a LifetimeParam FileId(0) 47..49 FileId(0) 55..57 FileId(0) 64..66 diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index d487a538bb..c0b556f544 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs @@ -335,7 +335,8 @@ pub(crate) fn runnable_fn( sema.db, def.source(sema.db)?.as_ref().map(|it| it as &dyn ast::HasName), SymbolKind::Function, - ); + ) + .call_site(); let cfg = def.attrs(sema.db).cfg(); Some(Runnable { use_name_in_title: false, nav, kind, cfg }) } @@ -357,7 +358,7 @@ pub(crate) fn runnable_mod( let attrs = def.attrs(sema.db); let cfg = attrs.cfg(); - let nav = NavigationTarget::from_module_to_decl(sema.db, def); + let nav = NavigationTarget::from_module_to_decl(sema.db, def).call_site(); Some(Runnable { use_name_in_title: false, nav, kind: RunnableKind::TestMod { path }, cfg }) } @@ -370,7 +371,7 @@ pub(crate) fn runnable_impl( return None; } let cfg = attrs.cfg(); - let nav = def.try_to_nav(sema.db)?; + let nav = def.try_to_nav(sema.db)?.call_site(); let ty = def.self_ty(sema.db); let adt_name = ty.as_adt()?.name(sema.db); let mut ty_args = ty.generic_parameters(sema.db).peekable(); @@ -407,7 +408,7 @@ fn runnable_mod_outline_definition( match def.definition_source(sema.db).value { hir::ModuleSource::SourceFile(_) => Some(Runnable { use_name_in_title: false, - nav: def.to_nav(sema.db), + nav: def.to_nav(sema.db).call_site(), kind: RunnableKind::TestMod { path }, cfg, }), @@ -465,7 +466,8 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option<Runnable> { let mut nav = match def { Definition::Module(def) => NavigationTarget::from_module_to_decl(db, def), def => def.try_to_nav(db)?, - }; + } + .call_site(); nav.focus_range = None; nav.description = None; nav.docs = None; diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index 49e165d3dc..3724dc2822 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -13,6 +13,7 @@ use ide_db::{ use syntax::{AstNode, SyntaxKind::*, TextRange, T}; use crate::inlay_hints::InlayFieldsToResolve; +use crate::navigation_target::UpmappingResult; use crate::{ hover::hover_for_definition, inlay_hints::AdjustmentHintsMode, @@ -166,9 +167,8 @@ impl StaticIndex<'_> { } else { let it = self.tokens.insert(TokenStaticData { hover: hover_for_definition(&sema, file_id, def, &node, &hover_config), - definition: def.try_to_nav(self.db).map(|it| FileRange { - file_id: it.file_id, - range: it.focus_or_full_range(), + definition: def.try_to_nav(self.db).map(UpmappingResult::call_site).map(|it| { + FileRange { file_id: it.file_id, range: it.focus_or_full_range() } }), references: vec![], moniker: current_crate.and_then(|cc| def_to_moniker(self.db, def, cc)), @@ -179,7 +179,7 @@ impl StaticIndex<'_> { let token = self.tokens.get_mut(id).unwrap(); token.references.push(ReferenceData { range: FileRange { range, file_id }, - is_definition: match def.try_to_nav(self.db) { + is_definition: match def.try_to_nav(self.db).map(UpmappingResult::call_site) { Some(it) => it.file_id == file_id && it.focus_or_full_range() == range, None => false, }, diff --git a/crates/proc-macro-api/src/msg.rs b/crates/proc-macro-api/src/msg.rs index dd882d82fb..1d3e45aff3 100644 --- a/crates/proc-macro-api/src/msg.rs +++ b/crates/proc-macro-api/src/msg.rs @@ -147,8 +147,10 @@ mod tests { use super::*; fn fixture_token_tree() -> Subtree<SpanData> { - let anchor = - SpanAnchor { file_id: FileId(0), ast_id: ErasedFileAstId::from_raw(RawIdx::from(0)) }; + let anchor = SpanAnchor { + file_id: FileId::from_raw(0), + ast_id: ErasedFileAstId::from_raw(RawIdx::from(0)), + }; let mut subtree = Subtree { delimiter: Delimiter { open: SpanData { diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs index 98f3063bb9..4887b29815 100644 --- a/crates/project-model/src/tests.rs +++ b/crates/project-model/src/tests.rs @@ -58,7 +58,7 @@ fn load_cargo_with_sysroot( &mut { |path| { let len = file_map.len(); - Some(*file_map.entry(path.to_path_buf()).or_insert(FileId(len as u32))) + Some(*file_map.entry(path.to_path_buf()).or_insert(FileId::from_raw(len as u32))) } }, &Default::default(), @@ -142,7 +142,7 @@ fn to_crate_graph(project_workspace: ProjectWorkspace) -> (CrateGraph, ProcMacro let mut counter = 0; move |_path| { counter += 1; - Some(FileId(counter)) + Some(FileId::from_raw(counter)) } }, &Default::default(), diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index 06c27332d4..49c88702fa 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs @@ -1438,7 +1438,7 @@ pub(crate) fn handle_inlay_hints_resolve( }; let resolve_data: lsp_ext::InlayHintResolveData = serde_json::from_value(data)?; - let file_id = FileId(resolve_data.file_id); + let file_id = FileId::from_raw(resolve_data.file_id); anyhow::ensure!(snap.file_exists(file_id), "Invalid LSP resolve data"); let line_index = snap.file_line_index(file_id)?; diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs index fb366fd5cc..dae560c5de 100644 --- a/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/crates/rust-analyzer/src/lsp/to_proto.rs @@ -457,7 +457,7 @@ pub(crate) fn inlay_hint( inlay_hint.text_edit.map(|it| text_edit_vec(line_index, it)) }; let data = if needs_resolve && something_to_resolve { - Some(to_value(lsp_ext::InlayHintResolveData { file_id: file_id.0 }).unwrap()) + Some(to_value(lsp_ext::InlayHintResolveData { file_id: file_id.index() }).unwrap()) } else { None }; diff --git a/crates/syntax/src/ptr.rs b/crates/syntax/src/ptr.rs index 71762996cd..07641b203b 100644 --- a/crates/syntax/src/ptr.rs +++ b/crates/syntax/src/ptr.rs @@ -22,12 +22,17 @@ use crate::{syntax_node::RustLanguage, AstNode, SyntaxNode}; pub type SyntaxNodePtr = rowan::ast::SyntaxNodePtr<RustLanguage>; /// Like `SyntaxNodePtr`, but remembers the type of node. -#[derive(Debug)] pub struct AstPtr<N: AstNode> { raw: SyntaxNodePtr, _ty: PhantomData<fn() -> N>, } +impl<N: AstNode + std::fmt::Debug> std::fmt::Debug for AstPtr<N> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("AstPtr").field(&self.raw).finish() + } +} + impl<N: AstNode> Clone for AstPtr<N> { fn clone(&self) -> AstPtr<N> { AstPtr { raw: self.raw.clone(), _ty: PhantomData } diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs index 7360f68c73..8ffda5d78d 100644 --- a/crates/vfs/src/lib.rs +++ b/crates/vfs/src/lib.rs @@ -60,11 +60,21 @@ pub use paths::{AbsPath, AbsPathBuf}; /// /// Most functions in rust-analyzer use this when they need to refer to a file. #[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] -pub struct FileId(pub u32); +pub struct FileId(u32); impl FileId { /// Think twice about using this outside of tests. If this ends up in a wrong place it will cause panics! - pub const BOGUS: FileId = FileId(u32::MAX); + pub const BOGUS: FileId = FileId(0xe4e4e); + + #[inline] + pub fn from_raw(raw: u32) -> FileId { + FileId(raw) + } + + #[inline] + pub fn index(self) -> u32 { + self.0 + } } /// safe because `FileId` is a newtype of `u32` |