From c3af98b5cde49995db424bfbb1cc2fbd7147b84d Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Tue, 27 May 2025 09:01:18 +0200 Subject: [PATCH 1/2] Rust: skip unexpanded stuff in library emission This will skip all unexpanded entities in library extraction, where we only really care about expanded things. This means skipping: * the token tree of macro calls * the unexpanded AST of attribute macros In the latter case, in order to replace the single `Item` with its expansion (which is a `MacroItems` entity), we wrap the `MacroItems` in a dummy `MacroCall` with null path. --- rust/extractor/src/translate/base.rs | 117 +++++++++++++++++---------- 1 file changed, 75 insertions(+), 42 deletions(-) diff --git a/rust/extractor/src/translate/base.rs b/rust/extractor/src/translate/base.rs index daf80fa45173..f19f39c75d10 100644 --- a/rust/extractor/src/translate/base.rs +++ b/rust/extractor/src/translate/base.rs @@ -25,7 +25,9 @@ use ra_ap_syntax::{ #[macro_export] macro_rules! pre_emit { (Item, $self:ident, $node:ident) => { - $self.setup_item_expansion($node); + if let Some(label) = $self.prepare_item_expansion($node) { + return Some(label); + } }; ($($_:tt)*) => {}; } @@ -687,6 +689,14 @@ impl<'a> Translator<'a> { { return true; } + if syntax + .parent() + .and_then(ast::MacroCall::cast) + .and_then(|x| x.token_tree()) + .is_some_and(|tt| tt.syntax() == syntax) + { + return true; + } } false } @@ -723,52 +733,75 @@ impl<'a> Translator<'a> { } } - pub(crate) fn setup_item_expansion(&mut self, node: &ast::Item) { - if self.semantics.is_some_and(|s| { - let file = s.hir_file_for(node.syntax()); - let node = InFile::new(file, node); - s.is_attr_macro_call(node) - }) { + pub(crate) fn prepare_item_expansion( + &mut self, + node: &ast::Item, + ) -> Option> { + if self.source_kind == SourceKind::Library { + // if the item expands via an attribute macro, we want to only emit the expansion + if let Some(expanded) = self.emit_attribute_macro_expansion(node) { + // we wrap it in a dummy MacroCall to get a single Item label that can replace + // the original Item + let label = self.trap.emit(generated::MacroCall { + id: TrapId::Star, + attrs: vec![], + path: None, + token_tree: None, + }); + generated::MacroCall::emit_macro_call_expansion( + label, + expanded.into(), + &mut self.trap.writer, + ); + return Some(label.into()); + } + } + let semantics = self.semantics.as_ref()?; + let file = semantics.hir_file_for(node.syntax()); + let node = InFile::new(file, node); + if semantics.is_attr_macro_call(node) { self.macro_context_depth += 1; } + None } - pub(crate) fn emit_item_expansion(&mut self, node: &ast::Item, label: Label) { - // TODO: remove this after fixing exponential expansion on libraries like funty-2.0.0 - if self.source_kind == SourceKind::Library { - return; + fn emit_attribute_macro_expansion( + &mut self, + node: &ast::Item, + ) -> Option> { + let semantics = self.semantics?; + let file = semantics.hir_file_for(node.syntax()); + let infile_node = InFile::new(file, node); + if !semantics.is_attr_macro_call(infile_node) { + return None; } - (|| { - let semantics = self.semantics?; - let file = semantics.hir_file_for(node.syntax()); - let infile_node = InFile::new(file, node); - if !semantics.is_attr_macro_call(infile_node) { - return None; - } - self.macro_context_depth -= 1; - if self.macro_context_depth > 0 { - // only expand the outermost attribute macro - return None; - } - let ExpandResult { - value: expanded, .. - } = semantics.expand_attr_macro(node)?; - self.emit_macro_expansion_parse_errors(node, &expanded); - let macro_items = ast::MacroItems::cast(expanded).or_else(|| { - let message = "attribute macro expansion cannot be cast to MacroItems".to_owned(); - let location = self.location_for_node(node); - self.emit_diagnostic( - DiagnosticSeverity::Warning, - "item_expansion".to_owned(), - message.clone(), - message, - location.unwrap_or(UNKNOWN_LOCATION), - ); - None - })?; - let expanded = self.emit_macro_items(¯o_items)?; + self.macro_context_depth -= 1; + if self.macro_context_depth > 0 { + // only expand the outermost attribute macro + return None; + } + let ExpandResult { + value: expanded, .. + } = semantics.expand_attr_macro(node)?; + self.emit_macro_expansion_parse_errors(node, &expanded); + let macro_items = ast::MacroItems::cast(expanded).or_else(|| { + let message = "attribute macro expansion cannot be cast to MacroItems".to_owned(); + let location = self.location_for_node(node); + self.emit_diagnostic( + DiagnosticSeverity::Warning, + "item_expansion".to_owned(), + message.clone(), + message, + location.unwrap_or(UNKNOWN_LOCATION), + ); + None + })?; + self.emit_macro_items(¯o_items) + } + + pub(crate) fn emit_item_expansion(&mut self, node: &ast::Item, label: Label) { + if let Some(expanded) = self.emit_attribute_macro_expansion(node) { generated::Item::emit_attribute_macro_expansion(label, expanded, &mut self.trap.writer); - Some(()) - })(); + } } } From fa3fcf0f959f558bece4cc754c0c985e937e0fe0 Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Mon, 2 Jun 2025 09:32:39 +0200 Subject: [PATCH 2/2] Rust: skip all token trees in library mode --- rust/extractor/src/translate/base.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/rust/extractor/src/translate/base.rs b/rust/extractor/src/translate/base.rs index f19f39c75d10..8d971900497e 100644 --- a/rust/extractor/src/translate/base.rs +++ b/rust/extractor/src/translate/base.rs @@ -689,12 +689,7 @@ impl<'a> Translator<'a> { { return true; } - if syntax - .parent() - .and_then(ast::MacroCall::cast) - .and_then(|x| x.token_tree()) - .is_some_and(|tt| tt.syntax() == syntax) - { + if syntax.kind() == SyntaxKind::TOKEN_TREE { return true; } }