@@ -9,7 +9,7 @@ use itertools::Itertools;
9
9
use mbe:: ast_to_token_tree;
10
10
use syntax:: {
11
11
ast:: { self , AstNode , AttrsOwner } ,
12
- AstToken , SmolStr ,
12
+ match_ast , AstToken , SmolStr , SyntaxNode ,
13
13
} ;
14
14
use tt:: Subtree ;
15
15
@@ -110,7 +110,7 @@ impl Attrs {
110
110
}
111
111
112
112
pub ( crate ) fn new ( owner : & dyn AttrsOwner , hygiene : & Hygiene ) -> Attrs {
113
- let docs = ast:: CommentIter :: from_syntax_node ( owner. syntax ( ) ) . map ( |docs_text| {
113
+ let outer_docs = ast:: CommentIter :: from_syntax_node ( owner. syntax ( ) ) . map ( |docs_text| {
114
114
(
115
115
docs_text. syntax ( ) . text_range ( ) . start ( ) ,
116
116
docs_text. doc_comment ( ) . map ( |doc| Attr {
@@ -119,11 +119,13 @@ impl Attrs {
119
119
} ) ,
120
120
)
121
121
} ) ;
122
- let attrs = owner
123
- . attrs ( )
122
+ let outer_attrs = owner. attrs ( ) . filter ( |attr| attr. excl_token ( ) . is_none ( ) ) ;
123
+ let inner_attrs = inner_attributes ( owner. syntax ( ) ) . into_iter ( ) . flatten ( ) ;
124
+ let attrs = outer_attrs
125
+ . chain ( inner_attrs)
124
126
. map ( |attr| ( attr. syntax ( ) . text_range ( ) . start ( ) , Attr :: from_src ( attr, hygiene) ) ) ;
125
127
// sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved
126
- let attrs: Vec < _ > = docs . chain ( attrs) . sorted_by_key ( |& ( offset, _) | offset) . collect ( ) ;
128
+ let attrs: Vec < _ > = outer_docs . chain ( attrs) . sorted_by_key ( |& ( offset, _) | offset) . collect ( ) ;
127
129
let entries = if attrs. is_empty ( ) {
128
130
// Avoid heap allocation
129
131
None
@@ -184,6 +186,38 @@ impl Attrs {
184
186
}
185
187
}
186
188
189
+ fn inner_attributes ( syntax : & SyntaxNode ) -> Option < impl Iterator < Item = ast:: Attr > > {
190
+ let ( attrs, _docs) = match_ast ! {
191
+ match syntax {
192
+ ast:: SourceFile ( it) => ( it. attrs( ) , None :: <ast:: Comment >) ,
193
+ ast:: ExternBlock ( it) => {
194
+ let extern_item_list = it. extern_item_list( ) ?;
195
+ ( extern_item_list. attrs( ) , None )
196
+ } ,
197
+ ast:: Fn ( it) => {
198
+ let body = it. body( ) ?;
199
+ ( body. attrs( ) , None )
200
+ } ,
201
+ ast:: Impl ( it) => {
202
+ let assoc_item_list = it. assoc_item_list( ) ?;
203
+ ( assoc_item_list. attrs( ) , None )
204
+ } ,
205
+ ast:: Module ( it) => {
206
+ let item_list = it. item_list( ) ?;
207
+ ( item_list. attrs( ) , None )
208
+ } ,
209
+ // FIXME: BlockExpr's only accept inner attributes in specific cases
210
+ // Excerpt from the reference:
211
+ // Block expressions accept outer and inner attributes, but only when they are the outer
212
+ // expression of an expression statement or the final expression of another block expression.
213
+ ast:: BlockExpr ( it) => return None ,
214
+ _ => return None ,
215
+ }
216
+ } ;
217
+ let attrs = attrs. filter ( |attr| attr. excl_token ( ) . is_some ( ) ) ;
218
+ Some ( attrs)
219
+ }
220
+
187
221
#[ derive( Debug , Clone , PartialEq , Eq ) ]
188
222
pub struct Attr {
189
223
pub ( crate ) path : ModPath ,
0 commit comments