From 8ab8bb0603dfab5c2c65e5a5ed2b1a2baac23004 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 1 Jun 2024 20:57:29 +0200 Subject: [PATCH] fix: Fix container search failing for tokens originating within derive attributes --- .../rust-analyzer/crates/hir-expand/src/db.rs | 2 +- .../crates/hir-expand/src/files.rs | 22 ++++++--------- .../crates/hir-expand/src/lib.rs | 16 +++++++++-- .../rust-analyzer/crates/hir/src/semantics.rs | 28 +++++++------------ .../crates/hir/src/semantics/source_to_def.rs | 20 +++++++++---- 5 files changed, 48 insertions(+), 40 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs index 2e5fa6131a7..12421bbe702 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs @@ -298,7 +298,7 @@ pub fn expand_speculative( // prefer tokens of the same kind and text // Note the inversion of the score here, as we want to prefer the first token in case // of all tokens having the same score - (t.kind() != token_to_map.kind()) as u8 + (t.text() != token_to_map.text()) as u8 + (t.kind() != token_to_map.kind()) as u8 + 2 * ((t.text() != token_to_map.text()) as u8) })?; Some((node.syntax_node(), token)) } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/files.rs b/src/tools/rust-analyzer/crates/hir-expand/src/files.rs index 04a4851ddb7..1ba85c5c7ea 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/files.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/files.rs @@ -153,24 +153,20 @@ pub fn syntax(&self) -> InFileWrapper { // region:specific impls impl InFile<&SyntaxNode> { - /// Skips the attributed item that caused the macro invocation we are climbing up - pub fn ancestors_with_macros_skip_attr_item( + /// Traverse up macro calls and skips the macro invocation node + pub fn ancestors_with_macros( self, db: &dyn db::ExpandDatabase, ) -> impl Iterator> + '_ { let succ = move |node: &InFile| match node.value.parent() { Some(parent) => Some(node.with_value(parent)), - None => { - let macro_file_id = node.file_id.macro_file()?; - let parent_node = macro_file_id.call_node(db); - if macro_file_id.is_attr_macro(db) { - // macro call was an attributed item, skip it - // FIXME: does this fail if this is a direct expansion of another macro? - parent_node.map(|node| node.parent()).transpose() - } else { - Some(parent_node) - } - } + None => db + .lookup_intern_macro_call(node.file_id.macro_file()?.macro_call_id) + .to_node_item(db) + .syntax() + .cloned() + .map(|node| node.parent()) + .transpose(), }; iter::successors(succ(&self.cloned()), succ) } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index 4ab989bec2f..83e92565f4d 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -33,8 +33,8 @@ use base_db::{salsa::impl_intern_value_trivial, CrateId, FileId}; use either::Either; use span::{ - Edition, ErasedFileAstId, FileRange, HirFileIdRepr, Span, SpanAnchor, SyntaxContextData, - SyntaxContextId, + Edition, ErasedFileAstId, FileAstId, FileRange, HirFileIdRepr, Span, SpanAnchor, + SyntaxContextData, SyntaxContextId, }; use syntax::{ ast::{self, AstNode}, @@ -546,6 +546,18 @@ pub fn to_node(&self, db: &dyn ExpandDatabase) -> InFile { } } + pub fn to_node_item(&self, db: &dyn ExpandDatabase) -> InFile { + match self.kind { + MacroCallKind::FnLike { ast_id, .. } => { + InFile::new(ast_id.file_id, ast_id.map(FileAstId::upcast).to_node(db)) + } + MacroCallKind::Derive { ast_id, .. } => { + InFile::new(ast_id.file_id, ast_id.map(FileAstId::upcast).to_node(db)) + } + MacroCallKind::Attr { ast_id, .. } => InFile::new(ast_id.file_id, ast_id.to_node(db)), + } + } + fn expand_to(&self) -> ExpandTo { match self.kind { MacroCallKind::FnLike { expand_to, .. } => expand_to, diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 6c70cc4baf0..53242611f81 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -862,10 +862,9 @@ fn descend_into_macros_impl( // attribute we failed expansion for earlier, this might be a derive invocation // or derive helper attribute let attr = meta.parent_attr()?; - let adt = if let Some(adt) = attr.syntax().parent().and_then(ast::Adt::cast) { - // this might be a derive, or a derive helper on an ADT + // this might be a derive on an ADT let derive_call = self.with_ctx(|ctx| { // so try downmapping the token into the pseudo derive expansion // see [hir_expand::builtin_attr_macro] for how the pseudo derive expansion works @@ -882,7 +881,7 @@ fn descend_into_macros_impl( let file_id = call_id.as_macro_file(); let text_range = attr.syntax().text_range(); // remove any other token in this macro input, all their mappings are the - // same as this one + // same as this tokens.retain(|t| !text_range.contains_range(t.text_range())); return process_expansion_for_token(&mut stack, file_id); } @@ -890,21 +889,14 @@ fn descend_into_macros_impl( } } else { // Otherwise this could be a derive helper on a variant or field - if let Some(field) = - attr.syntax().parent().and_then(ast::RecordField::cast) - { - field.syntax().ancestors().take(4).find_map(ast::Adt::cast) - } else if let Some(field) = - attr.syntax().parent().and_then(ast::TupleField::cast) - { - field.syntax().ancestors().take(4).find_map(ast::Adt::cast) - } else if let Some(variant) = - attr.syntax().parent().and_then(ast::Variant::cast) - { - variant.syntax().ancestors().nth(2).and_then(ast::Adt::cast) - } else { - None - } + attr.syntax().ancestors().find_map(ast::Item::cast).and_then(|it| { + match it { + ast::Item::Struct(it) => Some(ast::Adt::Struct(it)), + ast::Item::Enum(it) => Some(ast::Adt::Enum(it)), + ast::Item::Union(it) => Some(ast::Adt::Union(it)), + _ => None, + } + }) }?; if !self.with_ctx(|ctx| ctx.has_derives(InFile::new(file_id, &adt))) { return None; diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs index d2bd8b0e799..77e7cdb58ab 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs @@ -139,7 +139,7 @@ pub(super) fn module_to_def(&mut self, src: InFile) -> Option::cast).transpose()) .map(|it| it.transpose()); @@ -366,7 +366,7 @@ pub(super) fn proc_macro_to_def(&mut self, src: InFile) -> Option) -> Option { - for container in src.ancestors_with_macros_skip_attr_item(self.db.upcast()) { + for container in src.ancestors_with_macros(self.db.upcast()) { if let Some(res) = self.container_to_def(container) { return Some(res); } @@ -420,7 +420,7 @@ fn container_to_def(&mut self, container: InFile) -> Option) -> Option { - let ancestors = src.ancestors_with_macros_skip_attr_item(self.db.upcast()); + let ancestors = src.ancestors_with_macros(self.db.upcast()); for InFile { file_id, value } in ancestors { let item = match ast::Item::cast(value) { Some(it) => it, @@ -429,6 +429,7 @@ fn find_generic_param_container(&mut self, src: InFile<&SyntaxNode>) -> Option self.fn_to_def(InFile::new(file_id, it))?.into(), ast::Item::Struct(it) => self.struct_to_def(InFile::new(file_id, it))?.into(), + ast::Item::Union(it) => self.union_to_def(InFile::new(file_id, it))?.into(), ast::Item::Enum(it) => self.enum_to_def(InFile::new(file_id, it))?.into(), ast::Item::Trait(it) => self.trait_to_def(InFile::new(file_id, it))?.into(), ast::Item::TraitAlias(it) => { @@ -446,11 +447,18 @@ fn find_generic_param_container(&mut self, src: InFile<&SyntaxNode>) -> Option) -> Option { - let ancestors = src.ancestors_with_macros_skip_attr_item(self.db.upcast()); + let ancestors = src.ancestors_with_macros(self.db.upcast()); for InFile { file_id, value } in ancestors { - let item = match ast::Item::cast(value) { + let item = match ast::Item::cast(value.clone()) { Some(it) => it, - None => continue, + None => { + if let Some(variant) = ast::Variant::cast(value.clone()) { + return self + .enum_variant_to_def(InFile::new(file_id, variant)) + .map(Into::into); + } + continue; + } }; let res: DefWithBodyId = match item { ast::Item::Const(it) => self.const_to_def(InFile::new(file_id, it))?.into(),