@@ -6,16 +6,19 @@ use crate::trap::{Label, TrapClass};
66use ra_ap_base_db:: EditionedFileId ;
77use ra_ap_hir:: Semantics ;
88use 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} ;
1012use ra_ap_ide_db:: RootDatabase ;
1113use ra_ap_ide_db:: line_index:: { LineCol , LineIndex } ;
12- use ra_ap_parser:: SyntaxKind ;
14+ use ra_ap_parser:: { SyntaxKind , TopEntryPoint } ;
1315use ra_ap_span:: TextSize ;
1416use ra_ap_syntax:: ast:: HasAttrs ;
1517use 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
2023impl 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
133140const 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,
0 commit comments