1
1
//! Format attributes and meta items.
2
2
3
3
use rustc_ast:: ast;
4
+ use rustc_ast:: ast:: { AttrKind , Lit , MetaItem , MetaItemKind , StrStyle } ;
5
+ use rustc_ast:: token;
6
+ use rustc_ast:: token:: { Token , TokenKind } ;
7
+ use rustc_ast:: tokenstream:: TokenStream ;
8
+ use rustc_ast:: tokenstream:: TokenTree ;
9
+ use rustc_ast:: AttrItem ;
4
10
use rustc_ast:: HasAttrs ;
11
+ use rustc_ast:: MacArgs ;
12
+ use rustc_ast:: MacArgsEq ;
13
+ use rustc_ast:: MacArgsEq :: { Ast , Hir } ;
14
+ use rustc_ast:: NestedMetaItem ;
15
+ use rustc_ast:: Path ;
5
16
use rustc_span:: { symbol:: sym, Span , Symbol } ;
6
17
7
18
use self :: doc_comment:: DocCommentFormatter ;
@@ -329,6 +340,137 @@ impl Rewrite for ast::MetaItem {
329
340
}
330
341
}
331
342
343
+ // A simple workaround for `ast::Attribute::meta()` for generating MetaItem from AttrItem that
344
+ // includes constants (`CONST`) or macro metavariables (`$name`) as attribute value (issue #5489).
345
+ fn meta_from_attr_item (
346
+ item : & AttrItem ,
347
+ context : & RewriteContext < ' _ > ,
348
+ shape : Shape ,
349
+ ) -> Option < MetaItem > {
350
+ let meta = match & item. args {
351
+ MacArgs :: Delimited ( _, _, token_stream) => meta_from_delimited_attr ( token_stream. clone ( ) ) ,
352
+ MacArgs :: Eq ( _, mac_args_eq) => meta_from_eq_attr ( mac_args_eq, context, shape) ,
353
+ MacArgs :: Empty => None , // Should not get here - properly handled by `Attribute::meta()`
354
+ } ;
355
+
356
+ match meta {
357
+ Some ( meta) => Some ( MetaItem {
358
+ path : item. clone ( ) . path ,
359
+ kind : meta,
360
+ span : item. span ( ) ,
361
+ } ) ,
362
+ None => None ,
363
+ }
364
+ } // meta_from_item()
365
+
366
+ // Generating `MetaItemKind` from attribute `MacArgsEq`.
367
+ fn meta_from_eq_attr (
368
+ mac_args_eq : & MacArgsEq ,
369
+ context : & RewriteContext < ' _ > ,
370
+ shape : Shape ,
371
+ ) -> Option < rustc_ast:: MetaItemKind > {
372
+ let lit = match mac_args_eq {
373
+ Hir ( lit) => lit. clone ( ) ,
374
+ Ast ( expr) => {
375
+ let lit_symbol = Symbol :: intern ( & expr. rewrite ( context, shape) ?) ;
376
+ Lit {
377
+ token : token:: Lit :: new ( token:: Str , lit_symbol, None ) ,
378
+ kind : rustc_ast:: LitKind :: Str ( lit_symbol, StrStyle :: Cooked ) ,
379
+ span : expr. span ,
380
+ }
381
+ }
382
+ } ;
383
+
384
+ Some ( MetaItemKind :: NameValue ( lit. clone ( ) ) )
385
+ } // meta_from_eq_attr()
386
+
387
+ // Generating `MetaItemKind` from `Delimited` attribute `TokenStream``, assiming the tokens are
388
+ // gouped into 3-tokens groups, followed by an optional `,`. The 3 grop tokens are assumed to be:
389
+ // Ident `=` Ident or Lit
390
+ fn meta_from_delimited_attr ( token_stream : TokenStream ) -> Option < rustc_ast:: MetaItemKind > {
391
+ let mut token_count = token_stream. len ( ) ;
392
+ if token_count < 3 {
393
+ return None ;
394
+ }
395
+ let mut trees = token_stream. into_trees ( ) ;
396
+ let mut meta_items_list: Vec < NestedMetaItem > = Vec :: new ( ) ;
397
+
398
+ // Closure t get the next token from a TokenTree
399
+ let mut next_token = || -> Option < Token > {
400
+ if let TokenTree :: Token ( token, _) = trees. next ( ) ? {
401
+ Some ( token)
402
+ } else {
403
+ None
404
+ }
405
+ } ; // next_token() = ||
406
+
407
+ // Loop over the 3-tokens groups of the ToekenTree
408
+ while token_count >= 3 {
409
+ // 1st token is the lhs Ident
410
+ let attr_name_token = next_token ( ) ?;
411
+ if let TokenKind :: Ident ( attr_name_sym, _) = attr_name_token. kind {
412
+ // 2nd token is the "="
413
+ if !matches ! ( next_token( ) ?. kind, TokenKind :: Eq ) {
414
+ return None ;
415
+ } else {
416
+ // 3rd token is the rhs Ident/Lit
417
+ let token = next_token ( ) ?;
418
+ token_count -= 3 ;
419
+ if token_count % 4 == 1 {
420
+ // Only comma or next 3-tokens group can follow
421
+ if !matches ! ( next_token( ) ?. kind, TokenKind :: Comma ) {
422
+ return None ;
423
+ }
424
+ token_count -= 1 ;
425
+ }
426
+
427
+ // rhs can be either Lit or Ident
428
+ let lit = match token. kind {
429
+ TokenKind :: Literal ( lit) => {
430
+ let lit_symbol = Symbol :: intern ( & lit. to_string ( ) ) ;
431
+ Lit {
432
+ token : lit,
433
+ kind : rustc_ast:: LitKind :: Str ( lit_symbol, StrStyle :: Cooked ) ,
434
+ span : token. span ,
435
+ }
436
+ }
437
+ TokenKind :: Ident ( _, _) => {
438
+ let ident_name = if let TokenKind :: Ident ( name, _) = token. kind {
439
+ name. to_ident_string ( )
440
+ } else {
441
+ "" . to_string ( )
442
+ } ;
443
+ let lit_symbol = Symbol :: intern ( & ident_name) ;
444
+ Lit {
445
+ token : token:: Lit :: new ( token:: Str , lit_symbol, None ) ,
446
+ kind : rustc_ast:: LitKind :: Str ( lit_symbol, StrStyle :: Cooked ) ,
447
+ span : token. span ,
448
+ }
449
+ }
450
+ _ => return None ,
451
+ } ;
452
+
453
+ let attr_name_ident = rustc_span:: symbol:: Ident {
454
+ name : attr_name_sym,
455
+ span : attr_name_token. span ,
456
+ } ;
457
+
458
+ let meta_item = MetaItem {
459
+ path : Path :: from_ident ( attr_name_ident) ,
460
+ kind : MetaItemKind :: NameValue ( lit. clone ( ) ) ,
461
+ span : lit. span ,
462
+ } ;
463
+
464
+ meta_items_list. push ( NestedMetaItem :: MetaItem ( meta_item) ) ;
465
+ }
466
+ } else {
467
+ return None ;
468
+ }
469
+ } // while
470
+
471
+ return Some ( MetaItemKind :: List ( meta_items_list) ) ;
472
+ } // meta_from_delimited_attr()
473
+
332
474
impl Rewrite for ast:: Attribute {
333
475
fn rewrite ( & self , context : & RewriteContext < ' _ > , shape : Shape ) -> Option < String > {
334
476
let snippet = context. snippet ( self . span ) ;
@@ -345,35 +487,49 @@ impl Rewrite for ast::Attribute {
345
487
return Some ( snippet. to_owned ( ) ) ;
346
488
}
347
489
348
- if let Some ( ref meta) = self . meta ( ) {
349
- // This attribute is possibly a doc attribute needing normalization to a doc comment
350
- if context. config . normalize_doc_attributes ( ) && meta. has_name ( sym:: doc) {
351
- if let Some ( ref literal) = meta. value_str ( ) {
352
- let comment_style = match self . style {
353
- ast:: AttrStyle :: Inner => CommentStyle :: Doc ,
354
- ast:: AttrStyle :: Outer => CommentStyle :: TripleSlash ,
355
- } ;
356
-
357
- let literal_str = literal. as_str ( ) ;
358
- let doc_comment_formatter =
359
- DocCommentFormatter :: new ( literal_str, comment_style) ;
360
- let doc_comment = format ! ( "{}" , doc_comment_formatter) ;
361
- return rewrite_doc_comment (
362
- & doc_comment,
363
- shape. comment ( context. config ) ,
364
- context. config ,
365
- ) ;
490
+ let meta = match self . meta ( ) {
491
+ Some ( meta) => Some ( meta) ,
492
+ None => match self . kind {
493
+ // FIXME: `ast::Attribute::meta()` should be fixed to allow CONST and
494
+ // macro metavariables (`$name`) as valid attributes values.
495
+ //
496
+ // A simplified workaround for `ast::Attribute::meta()`that does not
497
+ // allows CONST and macro metavariables (`$name`) as attributes values.
498
+ AttrKind :: Normal ( ref item, _) => meta_from_attr_item ( & item, context, shape) ,
499
+ _ => None ,
500
+ } ,
501
+ } ;
502
+
503
+ match meta {
504
+ Some ( ref meta) => {
505
+ // This attribute is possibly a doc attr needing normalization to a doc comment
506
+ if context. config . normalize_doc_attributes ( ) && meta. has_name ( sym:: doc) {
507
+ if let Some ( ref literal) = meta. value_str ( ) {
508
+ let comment_style = match self . style {
509
+ ast:: AttrStyle :: Inner => CommentStyle :: Doc ,
510
+ ast:: AttrStyle :: Outer => CommentStyle :: TripleSlash ,
511
+ } ;
512
+
513
+ let literal_str = literal. as_str ( ) ;
514
+ let doc_comment_formatter =
515
+ DocCommentFormatter :: new ( literal_str, comment_style) ;
516
+ let doc_comment = format ! ( "{}" , doc_comment_formatter) ;
517
+ return rewrite_doc_comment (
518
+ & doc_comment,
519
+ shape. comment ( context. config ) ,
520
+ context. config ,
521
+ ) ;
522
+ }
366
523
}
367
- }
368
524
369
- // 1 = `[`
370
- let shape = shape. offset_left ( prefix. len ( ) + 1 ) ?;
371
- Some (
372
- meta. rewrite ( context, shape)
373
- . map_or_else ( || snippet. to_owned ( ) , |rw| format ! ( "{}[{}]" , prefix, rw) ) ,
374
- )
375
- } else {
376
- Some ( snippet. to_owned ( ) )
525
+ // 1 = `[`
526
+ let shape = shape. offset_left ( prefix. len ( ) + 1 ) ?;
527
+ Some (
528
+ meta. rewrite ( context, shape)
529
+ . map_or_else ( || snippet. to_owned ( ) , |rw| format ! ( "{}[{}]" , prefix, rw) ) ,
530
+ )
531
+ }
532
+ None => Some ( snippet. to_owned ( ) ) ,
377
533
}
378
534
}
379
535
}
0 commit comments