Skip to content

Commit c8be9b6

Browse files
jonhootikue
authored andcommitted
Fix following removal of Attribute.value (#136)
Since rust-lang/rust#40346 has now been merged, Attribute no longer has a .value field. Instead, we must follow the token stream and modify the tokens directly. For Docstring attributes, there should only be one token, the docstring value.
1 parent f4018a4 commit c8be9b6

File tree

1 file changed

+40
-14
lines changed

1 file changed

+40
-14
lines changed

src/plugins/src/lib.rs

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,13 @@ extern crate syntax;
77
use itertools::Itertools;
88
use rustc_plugin::Registry;
99
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;
1310
use syntax::ext::base::{ExtCtxt, MacResult, DummyResult, MacEager};
1411
use syntax::ext::quote::rt::Span;
15-
use syntax::parse::{self, token, PResult};
12+
use syntax::parse::{self, token, str_lit, PResult};
1613
use syntax::parse::parser::{Parser, PathStyle};
1714
use syntax::symbol::Symbol;
1815
use syntax::ptr::P;
19-
use syntax::tokenstream::TokenTree;
16+
use syntax::tokenstream::{TokenTree, TokenStream};
2017
use syntax::util::small_vector::SmallVector;
2118

2219
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
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

Comments
 (0)