@@ -75,32 +75,31 @@ impl RawAttrs {
7575 pub ( crate ) const EMPTY : Self = Self { entries : None } ;
7676
7777 pub ( crate ) fn new ( owner : & dyn AttrsOwner , hygiene : & Hygiene ) -> Self {
78- let ( inner_attrs, inner_docs) = inner_attributes ( owner. syntax ( ) )
79- . map_or ( ( None , None ) , |( attrs, docs) | ( ( Some ( attrs) , Some ( docs) ) ) ) ;
80-
81- let outer_attrs = owner. attrs ( ) . filter ( |attr| attr. excl_token ( ) . is_none ( ) ) ;
82- let attrs = outer_attrs
83- . chain ( inner_attrs. into_iter ( ) . flatten ( ) )
84- . map ( |attr| ( attr. syntax ( ) . text_range ( ) . start ( ) , Attr :: from_src ( attr, hygiene) ) ) ;
85-
86- let outer_docs =
87- ast:: CommentIter :: from_syntax_node ( owner. syntax ( ) ) . filter ( ast:: Comment :: is_outer) ;
88- let docs = outer_docs. chain ( inner_docs. into_iter ( ) . flatten ( ) ) . map ( |docs_text| {
89- (
90- docs_text. syntax ( ) . text_range ( ) . start ( ) ,
91- docs_text. doc_comment ( ) . map ( |doc| Attr {
92- input : Some ( AttrInput :: Literal ( SmolStr :: new ( doc) ) ) ,
93- path : ModPath :: from ( hir_expand:: name!( doc) ) ,
94- } ) ,
95- )
96- } ) ;
97- // sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved
98- let attrs: Vec < _ > = docs. chain ( attrs) . sorted_by_key ( |& ( offset, _) | offset) . collect ( ) ;
78+ let attrs: Vec < _ > = collect_attrs ( owner) . collect ( ) ;
9979 let entries = if attrs. is_empty ( ) {
10080 // Avoid heap allocation
10181 None
10282 } else {
103- Some ( attrs. into_iter ( ) . flat_map ( |( _, attr) | attr) . collect ( ) )
83+ Some (
84+ attrs
85+ . into_iter ( )
86+ . enumerate ( )
87+ . flat_map ( |( i, attr) | match attr {
88+ Either :: Left ( attr) => Attr :: from_src ( attr, hygiene) . map ( |attr| ( i, attr) ) ,
89+ Either :: Right ( comment) => comment. doc_comment ( ) . map ( |doc| {
90+ (
91+ i,
92+ Attr {
93+ index : 0 ,
94+ input : Some ( AttrInput :: Literal ( SmolStr :: new ( doc) ) ) ,
95+ path : ModPath :: from ( hir_expand:: name!( doc) ) ,
96+ } ,
97+ )
98+ } ) ,
99+ } )
100+ . map ( |( i, attr) | Attr { index : i as u32 , ..attr } )
101+ . collect ( ) ,
102+ )
104103 } ;
105104 Self { entries }
106105 }
@@ -316,6 +315,7 @@ fn inner_attributes(
316315
317316#[ derive( Debug , Clone , PartialEq , Eq ) ]
318317pub struct Attr {
318+ index : u32 ,
319319 pub ( crate ) path : ModPath ,
320320 pub ( crate ) input : Option < AttrInput > ,
321321}
@@ -342,7 +342,19 @@ impl Attr {
342342 } else {
343343 None
344344 } ;
345- Some ( Attr { path, input } )
345+ Some ( Attr { index : 0 , path, input } )
346+ }
347+
348+ /// Maps this lowered `Attr` back to its original syntax node.
349+ ///
350+ /// `owner` must be the original owner of the attribute.
351+ ///
352+ /// Note that the returned syntax node might be a `#[cfg_attr]`, or a doc comment, instead of
353+ /// the attribute represented by `Attr`.
354+ pub fn to_src ( & self , owner : & dyn AttrsOwner ) -> Either < ast:: Attr , ast:: Comment > {
355+ collect_attrs ( owner) . nth ( self . index as usize ) . unwrap_or_else ( || {
356+ panic ! ( "cannot find `Attr` at index {} in {}" , self . index, owner. syntax( ) )
357+ } )
346358 }
347359
348360 /// Parses this attribute as a `#[derive]`, returns an iterator that yields all contained paths
@@ -432,3 +444,23 @@ fn attrs_from_item_tree<N: ItemTreeNode>(id: ItemTreeId<N>, db: &dyn DefDatabase
432444 let mod_item = N :: id_to_mod_item ( id. value ) ;
433445 tree. raw_attrs ( mod_item. into ( ) ) . clone ( )
434446}
447+
448+ fn collect_attrs ( owner : & dyn AttrsOwner ) -> impl Iterator < Item = Either < ast:: Attr , ast:: Comment > > {
449+ let ( inner_attrs, inner_docs) = inner_attributes ( owner. syntax ( ) )
450+ . map_or ( ( None , None ) , |( attrs, docs) | ( ( Some ( attrs) , Some ( docs) ) ) ) ;
451+
452+ let outer_attrs = owner. attrs ( ) . filter ( |attr| attr. excl_token ( ) . is_none ( ) ) ;
453+ let attrs = outer_attrs
454+ . chain ( inner_attrs. into_iter ( ) . flatten ( ) )
455+ . map ( |attr| ( attr. syntax ( ) . text_range ( ) . start ( ) , Either :: Left ( attr) ) ) ;
456+
457+ let outer_docs =
458+ ast:: CommentIter :: from_syntax_node ( owner. syntax ( ) ) . filter ( ast:: Comment :: is_outer) ;
459+ let docs = outer_docs
460+ . chain ( inner_docs. into_iter ( ) . flatten ( ) )
461+ . map ( |docs_text| ( docs_text. syntax ( ) . text_range ( ) . start ( ) , Either :: Right ( docs_text) ) ) ;
462+ // sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved
463+ let attrs: Vec < _ > = docs. chain ( attrs) . sorted_by_key ( |& ( offset, _) | offset) . collect ( ) ;
464+
465+ attrs. into_iter ( ) . map ( |( _, attr) | attr)
466+ }
0 commit comments