@@ -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,17 @@ 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 ( inner_attrs, inner_docs) = inner_attributes ( owner. syntax ( ) )
114
+ . map_or ( ( None , None ) , |( attrs, docs) | ( ( Some ( attrs) , Some ( docs) ) ) ) ;
115
+
116
+ let outer_attrs = owner. attrs ( ) . filter ( |attr| attr. excl_token ( ) . is_none ( ) ) ;
117
+ let attrs = outer_attrs
118
+ . chain ( inner_attrs. into_iter ( ) . flatten ( ) )
119
+ . map ( |attr| ( attr. syntax ( ) . text_range ( ) . start ( ) , Attr :: from_src ( attr, hygiene) ) ) ;
120
+
121
+ let outer_docs =
122
+ ast:: CommentIter :: from_syntax_node ( owner. syntax ( ) ) . filter ( ast:: Comment :: is_outer) ;
123
+ let docs = outer_docs. chain ( inner_docs. into_iter ( ) . flatten ( ) ) . map ( |docs_text| {
114
124
(
115
125
docs_text. syntax ( ) . text_range ( ) . start ( ) ,
116
126
docs_text. doc_comment ( ) . map ( |doc| Attr {
@@ -119,9 +129,6 @@ impl Attrs {
119
129
} ) ,
120
130
)
121
131
} ) ;
122
- let attrs = owner
123
- . attrs ( )
124
- . map ( |attr| ( attr. syntax ( ) . text_range ( ) . start ( ) , Attr :: from_src ( attr, hygiene) ) ) ;
125
132
// sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved
126
133
let attrs: Vec < _ > = docs. chain ( attrs) . sorted_by_key ( |& ( offset, _) | offset) . collect ( ) ;
127
134
let entries = if attrs. is_empty ( ) {
@@ -184,6 +191,41 @@ impl Attrs {
184
191
}
185
192
}
186
193
194
+ fn inner_attributes (
195
+ syntax : & SyntaxNode ,
196
+ ) -> Option < ( impl Iterator < Item = ast:: Attr > , impl Iterator < Item = ast:: Comment > ) > {
197
+ let ( attrs, docs) = match_ast ! {
198
+ match syntax {
199
+ ast:: SourceFile ( it) => ( it. attrs( ) , ast:: CommentIter :: from_syntax_node( it. syntax( ) ) ) ,
200
+ ast:: ExternBlock ( it) => {
201
+ let extern_item_list = it. extern_item_list( ) ?;
202
+ ( extern_item_list. attrs( ) , ast:: CommentIter :: from_syntax_node( extern_item_list. syntax( ) ) )
203
+ } ,
204
+ ast:: Fn ( it) => {
205
+ let body = it. body( ) ?;
206
+ ( body. attrs( ) , ast:: CommentIter :: from_syntax_node( body. syntax( ) ) )
207
+ } ,
208
+ ast:: Impl ( it) => {
209
+ let assoc_item_list = it. assoc_item_list( ) ?;
210
+ ( assoc_item_list. attrs( ) , ast:: CommentIter :: from_syntax_node( assoc_item_list. syntax( ) ) )
211
+ } ,
212
+ ast:: Module ( it) => {
213
+ let item_list = it. item_list( ) ?;
214
+ ( item_list. attrs( ) , ast:: CommentIter :: from_syntax_node( item_list. syntax( ) ) )
215
+ } ,
216
+ // FIXME: BlockExpr's only accept inner attributes in specific cases
217
+ // Excerpt from the reference:
218
+ // Block expressions accept outer and inner attributes, but only when they are the outer
219
+ // expression of an expression statement or the final expression of another block expression.
220
+ ast:: BlockExpr ( it) => return None ,
221
+ _ => return None ,
222
+ }
223
+ } ;
224
+ let attrs = attrs. filter ( |attr| attr. excl_token ( ) . is_some ( ) ) ;
225
+ let docs = docs. filter ( |doc| doc. is_inner ( ) ) ;
226
+ Some ( ( attrs, docs) )
227
+ }
228
+
187
229
#[ derive( Debug , Clone , PartialEq , Eq ) ]
188
230
pub struct Attr {
189
231
pub ( crate ) path : ModPath ,
0 commit comments