Skip to content

Commit bbd2102

Browse files
authored
Merge pull request #22273 from github/redsun82-rust-derive-macro-patch
Rust: Restore built-in derive macro expansion under rust-analyzer 0.0.328
2 parents 701cf10 + 1d6ac57 commit bbd2102

12 files changed

Lines changed: 750 additions & 128 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

MODULE.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ use_repo(
135135
"vendor_ts__ra_ap_span-0.0.328",
136136
"vendor_ts__ra_ap_stdx-0.0.328",
137137
"vendor_ts__ra_ap_syntax-0.0.328",
138+
"vendor_ts__ra_ap_syntax-bridge-0.0.328",
138139
"vendor_ts__ra_ap_vfs-0.0.328",
139140
"vendor_ts__rand-0.10.1",
140141
"vendor_ts__rayon-1.12.0",

misc/bazel/3rdparty/tree_sitter_extractors_deps/BUILD.bazel

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

misc/bazel/3rdparty/tree_sitter_extractors_deps/defs.bzl

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/extractor/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ ra_ap_load-cargo = "0.0.328"
2020
ra_ap_paths = "0.0.328"
2121
ra_ap_project_model = "0.0.328"
2222
ra_ap_syntax = "0.0.328"
23+
ra_ap_syntax-bridge = "0.0.328"
2324
ra_ap_vfs = "0.0.328"
2425
ra_ap_parser = "0.0.328"
2526
ra_ap_span = "0.0.328"

rust/extractor/src/translate/base.rs

Lines changed: 94 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,19 @@ use crate::trap::{Label, TrapClass};
66
use ra_ap_base_db::EditionedFileId;
77
use ra_ap_hir::Semantics;
88
use ra_ap_hir::db::ExpandDatabase;
9-
use ra_ap_hir_expand::{ExpandResult, ExpandTo, InFile};
9+
use ra_ap_hir_expand::builtin::{BuiltinDeriveExpander, find_builtin_derive};
10+
use ra_ap_hir_expand::span_map::ExpansionSpanMap;
11+
use ra_ap_hir_expand::{ExpandResult, ExpandTo, InFile, map_node_range_up_rooted};
1012
use ra_ap_ide_db::RootDatabase;
1113
use ra_ap_ide_db::line_index::{LineCol, LineIndex};
12-
use ra_ap_parser::SyntaxKind;
14+
use ra_ap_parser::{SyntaxKind, TopEntryPoint};
1315
use ra_ap_span::TextSize;
1416
use ra_ap_syntax::ast::HasAttrs;
1517
use ra_ap_syntax::{
1618
AstNode, NodeOrToken, SyntaxElementChildren, SyntaxError, SyntaxNode, SyntaxToken, TextRange,
1719
ast,
1820
};
21+
use ra_ap_syntax_bridge::{DocCommentDesugarMode, syntax_node_to_token_tree, token_tree_to_syntax_node};
1922

2023
impl Emission<ast::Item> for Translator<'_> {
2124
fn pre_emit(&mut self, node: &ast::Item) -> Option<Label<generated::Item>> {
@@ -128,6 +131,10 @@ pub struct Translator<'a> {
128131
source_kind: SourceKind,
129132
pub(crate) macro_context_depth: usize,
130133
diagnostic_count: usize,
134+
/// When emitting a reconstructed built-in derive expansion, holds the span map of the
135+
/// synthesized syntax tree. Those nodes are not registered in the semantics cache, so
136+
/// locations are resolved through this map instead of `Semantics::original_range`.
137+
builtin_derive_span_map: Option<ExpansionSpanMap>,
131138
}
132139

133140
const UNKNOWN_LOCATION: (LineCol, LineCol) =
@@ -154,6 +161,7 @@ impl<'a> Translator<'a> {
154161
source_kind,
155162
macro_context_depth: 0,
156163
diagnostic_count: 0,
164+
builtin_derive_span_map: None,
157165
}
158166
}
159167
fn location(&self, range: TextRange) -> Option<(LineCol, LineCol)> {
@@ -177,6 +185,15 @@ impl<'a> Translator<'a> {
177185
}
178186

179187
pub fn text_range_for_node(&mut self, node: &impl ast::AstNode) -> Option<TextRange> {
188+
if let Some(span_map) = self.builtin_derive_span_map.as_ref() {
189+
// Nodes synthesized by a reconstructed built-in derive expansion are not in the
190+
// semantics cache; resolve their original source range through the expansion span map.
191+
let semantics = self.semantics.as_ref()?;
192+
let file_id = self.file_id?;
193+
let file_range =
194+
map_node_range_up_rooted(semantics.db, span_map, node.syntax().text_range())?;
195+
return (file_id == file_range.file_id).then_some(file_range.range);
196+
}
180197
if let Some(semantics) = self.semantics.as_ref() {
181198
let file_range = semantics.original_range(node.syntax());
182199
let file_id = self.file_id?;
@@ -411,6 +428,11 @@ impl<'a> Translator<'a> {
411428
// way as from version 0.0.274 rust-analyser only expands in the context of an expansion
412429
return;
413430
}
431+
if self.builtin_derive_span_map.is_some() {
432+
// inside a reconstructed built-in derive expansion the macro call is not registered in
433+
// the semantics cache, so it cannot (and need not) be expanded further
434+
return;
435+
}
414436
if let Some(expanded) = self
415437
.semantics
416438
.as_ref()
@@ -582,7 +604,8 @@ impl<'a> Translator<'a> {
582604
fn is_attribute_macro_target(&self, node: &ast::Item) -> bool {
583605
// rust-analyzer considers as an `attr_macro_call` also a plain macro call, but we want to
584606
// process that differently (in `extract_macro_call_expanded`)
585-
!matches!(node, ast::Item::MacroCall(_))
607+
self.builtin_derive_span_map.is_none()
608+
&& !matches!(node, ast::Item::MacroCall(_))
586609
&& self.semantics.is_some_and(|semantics| {
587610
let file = semantics.hir_file_for(node.syntax());
588611
let node = InFile::new(file, node);
@@ -703,6 +726,51 @@ impl<'a> Translator<'a> {
703726
}
704727
}
705728

729+
/// Reconstructs the expansion of a built-in derive macro (e.g. `Debug`, `PartialEq`).
730+
///
731+
/// Since rust-analyzer 0.0.317 built-in derives are modeled as synthetic impls rather than
732+
/// syntactic macro expansions, `Semantics::expand_derive_macro` no longer returns anything for
733+
/// them. We re-run the still-public built-in derive expander over the ADT and emit the resulting
734+
/// `impl` items ourselves. The synthesized nodes are not registered in the semantics
735+
/// cache, so `builtin_derive_span_map` is set for the duration of the emission to route their
736+
/// locations through the expansion span map.
737+
fn emit_builtin_derive_expansion(
738+
&mut self,
739+
adt: &ast::Adt,
740+
expander: BuiltinDeriveExpander,
741+
) -> Option<Label<generated::MacroItems>> {
742+
let semantics = self.semantics?;
743+
let db = semantics.db;
744+
let file_id = semantics.hir_file_for(adt.syntax());
745+
let span_map = db.span_map(file_id);
746+
let call_site = span_map.span_for_range(adt.syntax().text_range());
747+
let input = syntax_node_to_token_tree(
748+
adt.syntax(),
749+
span_map.as_ref(),
750+
call_site,
751+
DocCommentDesugarMode::ProcMacro,
752+
);
753+
let ExpandResult { value: output, err } = expander.expander()(db, call_site, &input);
754+
let edition = self.file_id?.edition(db);
755+
let (parsed, output_span_map) =
756+
token_tree_to_syntax_node(&output, TopEntryPoint::MacroItems, &mut |_| edition);
757+
let items = ast::MacroItems::cast(parsed.syntax_node())?;
758+
if let Some(err) = err {
759+
let rendered = err.render_to_string(db);
760+
self.emit_diagnostic_for_node(
761+
adt,
762+
DiagnosticSeverity::Warning,
763+
"item_expansion".to_owned(),
764+
format!("built-in derive expansion failed ({})", rendered.kind),
765+
rendered.message,
766+
);
767+
}
768+
let previous = self.builtin_derive_span_map.replace(output_span_map);
769+
let result = self.emit_macro_items(&items);
770+
self.builtin_derive_span_map = previous;
771+
result
772+
}
773+
706774
pub(crate) fn emit_derive_expansion(
707775
&mut self,
708776
node: &(impl Into<ast::Adt> + Clone),
@@ -712,14 +780,29 @@ impl<'a> Translator<'a> {
712780
return;
713781
};
714782
let node: ast::Adt = node.clone().into();
715-
let expansions = node
716-
.attrs()
717-
.filter_map(|attr| attr.meta())
718-
.filter_map(|meta| semantics.expand_derive_macro(&meta))
719-
.flatten()
720-
.flatten()
721-
.filter_map(|expanded| self.process_item_macro_expansion(&node, expanded))
722-
.collect::<Vec<_>>();
783+
let mut expansions = Vec::new();
784+
for meta in node.attrs().filter_map(|attr| attr.meta()) {
785+
let Some(expanded) = semantics.expand_derive_macro(&meta) else {
786+
continue;
787+
};
788+
// `resolve_derive_macro` yields one entry per derive, including built-in ones for which
789+
// `expand_derive_macro` returns `None`; we use it to recover the built-in expander.
790+
let resolved = semantics.resolve_derive_macro(&meta).unwrap_or_default();
791+
for (i, expanded) in expanded.into_iter().enumerate() {
792+
let label = if let Some(expanded) = expanded {
793+
self.process_item_macro_expansion(&node, expanded)
794+
} else if let Some(expander) = resolved
795+
.get(i)
796+
.and_then(|m| m.as_ref())
797+
.and_then(|m| find_builtin_derive(&m.name(semantics.db)))
798+
{
799+
self.emit_builtin_derive_expansion(&node, expander)
800+
} else {
801+
None
802+
};
803+
expansions.extend(label);
804+
}
805+
}
723806
generated::TypeItem::emit_derive_macro_expansions(
724807
label.into(),
725808
expansions,

rust/ql/test/extractor-tests/canonical_path/canonical_paths.expected

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
| {EXTERNAL LOCATION} | fn trim | <core::str>::trim |
44
| lib.rs:1:1:1:18 | mod anonymous | test::anonymous |
55
| lib.rs:2:1:2:16 | mod regular | test::regular |
6+
| regular.rs:1:1:2:18 | fn eq | <test::regular::Struct as core::cmp::PartialEq>::eq |
7+
| regular.rs:1:1:2:18 | impl ...::Eq for Struct::<...> { ... } | <test::regular::Struct as core::cmp::Eq> |
8+
| regular.rs:1:1:2:18 | impl ...::PartialEq for Struct::<...> { ... } | <test::regular::Struct as core::cmp::PartialEq> |
69
| regular.rs:1:1:2:18 | struct Struct | test::regular::Struct |
710
| regular.rs:4:1:6:1 | trait Trait | test::regular::Trait |
811
| regular.rs:5:5:5:16 | fn f | <_ as test::regular::Trait>::f |
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
instances
22
| gen_macro_items.rs:5:5:5:12 | MacroItems |
3+
| gen_macro_items.rs:12:5:13:15 | MacroItems |
34
getItem
45
| gen_macro_items.rs:5:5:5:12 | MacroItems | 0 | gen_macro_items.rs:5:5:5:38 | use ...::Path |
56
| gen_macro_items.rs:5:5:5:12 | MacroItems | 1 | gen_macro_items.rs:5:5:5:38 | fn get_parent |
7+
| gen_macro_items.rs:12:5:13:15 | MacroItems | 0 | gen_macro_items.rs:12:5:13:15 | impl ...::Debug for Bar::<...> { ... } |

0 commit comments

Comments
 (0)