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.
This commit is contained in:
Jon Gjengset
2017-03-21 14:00:52 -04:00
committed by Tim
parent f4018a431e
commit c8be9b690b

View File

@@ -7,16 +7,13 @@ extern crate syntax;
use itertools::Itertools;
use rustc_plugin::Registry;
use syntax::ast::{self, Ident, TraitRef, Ty, TyKind};
use syntax::ast::LitKind::Str;
use syntax::ast::MetaItemKind::NameValue;
use syntax::codemap::Spanned;
use syntax::ext::base::{ExtCtxt, MacResult, DummyResult, MacEager};
use syntax::ext::quote::rt::Span;
use syntax::parse::{self, token, PResult};
use syntax::parse::{self, token, str_lit, PResult};
use syntax::parse::parser::{Parser, PathStyle};
use syntax::symbol::Symbol;
use syntax::ptr::P;
use syntax::tokenstream::TokenTree;
use syntax::tokenstream::{TokenTree, TokenStream};
use syntax::util::small_vector::SmallVector;
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
// so this is the hacky workaround.
//
// This code looks intimidating, but it's just iterating through the trait item's attributes
// (NameValues), filtering out non-doc attributes, and replacing any {} in the doc string with
// the original, snake_case ident.
for attr in item.attrs.iter_mut().filter(|attr| attr.is_sugared_doc) {
if let NameValue(Spanned { node: Str(ref mut doc, _), .. }) = attr.value.node {
*doc = Symbol::intern(&doc.as_str().replace("{}", &old_ident));
} else {
unreachable!()
};
}
// copying non-doc attributes, and modifying doc attributes such that replacing any {} in the
// doc string instead holds the original, snake_case ident.
let attrs: Vec<_> = item.attrs
.drain(..)
.map(|mut attr| {
if !attr.is_sugared_doc {
return attr;
}
// Getting at the underlying doc comment is surprisingly painful.
// The call-chain goes something like:
//
// - https://github.com/rust-lang/rust/blob/9c15de4fd59bee290848b5443c7e194fd5afb02c/src/libsyntax/attr.rs#L283
// - https://github.com/rust-lang/rust/blob/9c15de4fd59bee290848b5443c7e194fd5afb02c/src/libsyntax/attr.rs#L1067
// - https://github.com/rust-lang/rust/blob/9c15de4fd59bee290848b5443c7e194fd5afb02c/src/libsyntax/attr.rs#L1196
// - https://github.com/rust-lang/rust/blob/9c15de4fd59bee290848b5443c7e194fd5afb02c/src/libsyntax/parse/mod.rs#L399
// - https://github.com/rust-lang/rust/blob/9c15de4fd59bee290848b5443c7e194fd5afb02c/src/libsyntax/parse/mod.rs#L268
//
// Note that a docstring (i.e., something with is_sugared_doc) *always* has exactly two
// tokens: an Eq followed by a Literal, where the Literal contains a Str_. We therefore
// match against that, modifying the inner Str with our modified Symbol.
let mut tokens = attr.tokens.clone().into_trees();
if let Some(tt @ TokenTree::Token(_, token::Eq)) = tokens.next() {
let mut docstr = tokens.next().expect("Docstrings must have literal docstring");
if let TokenTree::Token(_, token::Literal(token::Str_(ref mut doc), _)) = docstr {
*doc = Symbol::intern(&str_lit(&doc.as_str()).replace("{}", &old_ident));
} else {
unreachable!();
}
attr.tokens = TokenStream::concat(vec![tt.into(), docstr.into()]);
} else {
unreachable!();
}
attr
})
.collect();
item.attrs.extend(attrs.into_iter());
MacEager::trait_items(SmallVector::one(item))
}