@@ -7,16 +7,13 @@ extern crate syntax;
7
7
use itertools:: Itertools ;
8
8
use rustc_plugin:: Registry ;
9
9
use syntax:: ast:: { self , Ident , TraitRef , Ty , TyKind } ;
10
- use syntax:: ast:: LitKind :: Str ;
11
- use syntax:: ast:: MetaItemKind :: NameValue ;
12
- use syntax:: codemap:: Spanned ;
13
10
use syntax:: ext:: base:: { ExtCtxt , MacResult , DummyResult , MacEager } ;
14
11
use syntax:: ext:: quote:: rt:: Span ;
15
- use syntax:: parse:: { self , token, PResult } ;
12
+ use syntax:: parse:: { self , token, str_lit , PResult } ;
16
13
use syntax:: parse:: parser:: { Parser , PathStyle } ;
17
14
use syntax:: symbol:: Symbol ;
18
15
use syntax:: ptr:: P ;
19
- use syntax:: tokenstream:: TokenTree ;
16
+ use syntax:: tokenstream:: { TokenTree , TokenStream } ;
20
17
use syntax:: util:: small_vector:: SmallVector ;
21
18
22
19
fn snake_to_camel ( cx : & mut ExtCtxt , sp : Span , tts : & [ TokenTree ] ) -> Box < MacResult + ' static > {
@@ -43,15 +40,44 @@ fn snake_to_camel(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResul
43
40
// so this is the hacky workaround.
44
41
//
45
42
// This code looks intimidating, but it's just iterating through the trait item's attributes
46
- // (NameValues), filtering out non-doc attributes, and replacing any {} in the doc string with
47
- // the original, snake_case ident.
48
- for attr in item. attrs . iter_mut ( ) . filter ( |attr| attr. is_sugared_doc ) {
49
- if let NameValue ( Spanned { node : Str ( ref mut doc, _) , .. } ) = attr. value . node {
50
- * doc = Symbol :: intern ( & doc. as_str ( ) . replace ( "{}" , & old_ident) ) ;
51
- } else {
52
- unreachable ! ( )
53
- } ;
54
- }
43
+ // copying non-doc attributes, and modifying doc attributes such that replacing any {} in the
44
+ // doc string instead holds the original, snake_case ident.
45
+ let attrs: Vec < _ > = item. attrs
46
+ . drain ( ..)
47
+ . map ( |mut attr| {
48
+ if !attr. is_sugared_doc {
49
+ return attr;
50
+ }
51
+
52
+ // Getting at the underlying doc comment is surprisingly painful.
53
+ // The call-chain goes something like:
54
+ //
55
+ // - https://github.com/rust-lang/rust/blob/9c15de4fd59bee290848b5443c7e194fd5afb02c/src/libsyntax/attr.rs#L283
56
+ // - https://github.com/rust-lang/rust/blob/9c15de4fd59bee290848b5443c7e194fd5afb02c/src/libsyntax/attr.rs#L1067
57
+ // - https://github.com/rust-lang/rust/blob/9c15de4fd59bee290848b5443c7e194fd5afb02c/src/libsyntax/attr.rs#L1196
58
+ // - https://github.com/rust-lang/rust/blob/9c15de4fd59bee290848b5443c7e194fd5afb02c/src/libsyntax/parse/mod.rs#L399
59
+ // - https://github.com/rust-lang/rust/blob/9c15de4fd59bee290848b5443c7e194fd5afb02c/src/libsyntax/parse/mod.rs#L268
60
+ //
61
+ // Note that a docstring (i.e., something with is_sugared_doc) *always* has exactly two
62
+ // tokens: an Eq followed by a Literal, where the Literal contains a Str_. We therefore
63
+ // match against that, modifying the inner Str with our modified Symbol.
64
+ let mut tokens = attr. tokens . clone ( ) . into_trees ( ) ;
65
+ if let Some ( tt @ TokenTree :: Token ( _, token:: Eq ) ) = tokens. next ( ) {
66
+ let mut docstr = tokens. next ( ) . expect ( "Docstrings must have literal docstring" ) ;
67
+ if let TokenTree :: Token ( _, token:: Literal ( token:: Str_ ( ref mut doc) , _) ) = docstr {
68
+ * doc = Symbol :: intern ( & str_lit ( & doc. as_str ( ) ) . replace ( "{}" , & old_ident) ) ;
69
+ } else {
70
+ unreachable ! ( ) ;
71
+ }
72
+ attr. tokens = TokenStream :: concat ( vec ! [ tt. into( ) , docstr. into( ) ] ) ;
73
+ } else {
74
+ unreachable ! ( ) ;
75
+ }
76
+
77
+ attr
78
+ } )
79
+ . collect ( ) ;
80
+ item. attrs . extend ( attrs. into_iter ( ) ) ;
55
81
56
82
MacEager :: trait_items ( SmallVector :: one ( item) )
57
83
}
0 commit comments