about summary refs log tree commit diff
path: root/src/libproc_macro
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2017-07-12 09:50:05 -0700
committerAlex Crichton <alex@alexcrichton.com>2017-07-28 10:47:01 -0700
commit4886ec86651a5eaae1ddc834a941842904a5db61 (patch)
treeb936eddcf2b8ca4d656ae1d93e6ab86e8774f298 /src/libproc_macro
parent036300aadd5b6eb309de32c1b07f57f3aa2a13cd (diff)
downloadrust-4886ec86651a5eaae1ddc834a941842904a5db61.tar.gz
rust-4886ec86651a5eaae1ddc834a941842904a5db61.zip
syntax: Capture a `TokenStream` when parsing items
This is then later used by `proc_macro` to generate a new
`proc_macro::TokenTree` which preserves span information. Unfortunately this
isn't a bullet-proof approach as it doesn't handle the case when there's still
other attributes on the item, especially inner attributes.

Despite this the intention here is to solve the primary use case for procedural
attributes, attached to functions as outer attributes, likely bare. In this
situation we should be able to now yield a lossless stream of tokens to preserve
span information.
Diffstat (limited to 'src/libproc_macro')
-rw-r--r--src/libproc_macro/lib.rs63
1 files changed, 57 insertions, 6 deletions
diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs
index 0f0d4138062..1bffffd6c9e 100644
--- a/src/libproc_macro/lib.rs
+++ b/src/libproc_macro/lib.rs
@@ -510,15 +510,38 @@ impl TokenTree {
             Literal(..) | DocComment(..) => TokenNode::Literal(self::Literal(token)),
 
             Interpolated(ref nt) => {
-                let mut node = None;
-                if let Nonterminal::NtItem(ref item) = nt.0 {
-                    if let Some(ref tokens) = item.tokens {
-                        node = Some(TokenNode::Group(Delimiter::None,
-                                                     TokenStream(tokens.clone())));
+                // An `Interpolated` token means that we have a `Nonterminal`
+                // which is often a parsed AST item. At this point we now need
+                // to convert the parsed AST to an actual token stream, e.g.
+                // un-parse it basically.
+                //
+                // Unfortunately there's not really a great way to do that in a
+                // guaranteed lossless fashion right now. The fallback here is
+                // to just stringify the AST node and reparse it, but this loses
+                // all span information.
+                //
+                // As a result, some AST nodes are annotated with the token
+                // stream they came from. Attempt to extract these lossless
+                // token streams before we fall back to the stringification.
+                let mut tokens = None;
+
+                match nt.0 {
+                    Nonterminal::NtItem(ref item) => {
+                        tokens = prepend_attrs(&item.attrs, item.tokens.as_ref(), span);
                     }
+                    Nonterminal::NtTraitItem(ref item) => {
+                        tokens = prepend_attrs(&item.attrs, item.tokens.as_ref(), span);
+                    }
+                    Nonterminal::NtImplItem(ref item) => {
+                        tokens = prepend_attrs(&item.attrs, item.tokens.as_ref(), span);
+                    }
+                    _ => {}
                 }
 
-                node.unwrap_or_else(|| {
+                tokens.map(|tokens| {
+                    TokenNode::Group(Delimiter::None,
+                                     TokenStream(tokens.clone()))
+                }).unwrap_or_else(|| {
                     __internal::with_sess(|(sess, _)| {
                         TokenNode::Group(Delimiter::None, TokenStream(nt.1.force(|| {
                             // FIXME(jseyfried): Avoid this pretty-print + reparse hack
@@ -592,6 +615,34 @@ impl TokenTree {
     }
 }
 
+fn prepend_attrs(attrs: &[ast::Attribute],
+                 tokens: Option<&tokenstream::TokenStream>,
+                 span: syntax_pos::Span)
+    -> Option<tokenstream::TokenStream>
+{
+    let tokens = match tokens {
+        Some(tokens) => tokens,
+        None => return None,
+    };
+    if attrs.len() == 0 {
+        return Some(tokens.clone())
+    }
+    let mut builder = tokenstream::TokenStreamBuilder::new();
+    for attr in attrs {
+        assert_eq!(attr.style, ast::AttrStyle::Outer,
+                   "inner attributes should prevent cached tokens from existing");
+        let stream = __internal::with_sess(|(sess, _)| {
+            // FIXME: Avoid this pretty-print + reparse hack as bove
+            let name = "<macro expansion>".to_owned();
+            let source = pprust::attr_to_string(attr);
+            parse_stream_from_source_str(name, source, sess, Some(span))
+        });
+        builder.push(stream);
+    }
+    builder.push(tokens.clone());
+    Some(builder.build())
+}
+
 /// Permanently unstable internal implementation details of this crate. This
 /// should not be used.
 ///