@@ -7,16 +7,13 @@ extern crate syntax;
77use itertools:: Itertools ;
88use rustc_plugin:: Registry ;
99use syntax:: ast:: { self , Ident , TraitRef , Ty , TyKind } ;
10- use syntax:: ast:: LitKind :: Str ;
11- use syntax:: ast:: MetaItemKind :: NameValue ;
12- use syntax:: codemap:: Spanned ;
1310use syntax:: ext:: base:: { ExtCtxt , MacResult , DummyResult , MacEager } ;
1411use syntax:: ext:: quote:: rt:: Span ;
15- use syntax:: parse:: { self , token, PResult } ;
12+ use syntax:: parse:: { self , token, str_lit , PResult } ;
1613use syntax:: parse:: parser:: { Parser , PathStyle } ;
1714use syntax:: symbol:: Symbol ;
1815use syntax:: ptr:: P ;
19- use syntax:: tokenstream:: TokenTree ;
16+ use syntax:: tokenstream:: { TokenTree , TokenStream } ;
2017use syntax:: util:: small_vector:: SmallVector ;
2118
2219fn 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
4340 // so this is the hacky workaround.
4441 //
4542 // 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 ( ) ) ;
5581
5682 MacEager :: trait_items ( SmallVector :: one ( item) )
5783}
0 commit comments