Skip to content

Commit 126321e

Browse files
committed
Auto merge of #43230 - alexcrichton:more-tokenstream, r=nrc,jseyfried
Implement tokenization for some items in proc_macro This PR is a partial implementation of #43081 targeted towards preserving span information in attribute-like procedural macros. Currently all attribute-like macros will lose span information with the input token stream if it's iterated over due to the inability of the compiler to losslessly tokenize an AST node. This PR takes a strategy of saving off a list of tokens in particular AST nodes to return a lossless tokenized version. There's a few limitations with this PR, however, so the old fallback remains in place.
2 parents eba9d7f + 4886ec8 commit 126321e

File tree

18 files changed

+489
-24
lines changed

18 files changed

+489
-24
lines changed

src/libproc_macro/lib.rs

+71-8
Original file line numberDiff line numberDiff line change
@@ -509,14 +509,49 @@ impl TokenTree {
509509
Ident(ident) | Lifetime(ident) => TokenNode::Term(Term(ident.name)),
510510
Literal(..) | DocComment(..) => TokenNode::Literal(self::Literal(token)),
511511

512-
Interpolated(ref nt) => __internal::with_sess(|(sess, _)| {
513-
TokenNode::Group(Delimiter::None, TokenStream(nt.1.force(|| {
514-
// FIXME(jseyfried): Avoid this pretty-print + reparse hack
515-
let name = "<macro expansion>".to_owned();
516-
let source = pprust::token_to_string(&token);
517-
parse_stream_from_source_str(name, source, sess, Some(span))
518-
})))
519-
}),
512+
Interpolated(ref nt) => {
513+
// An `Interpolated` token means that we have a `Nonterminal`
514+
// which is often a parsed AST item. At this point we now need
515+
// to convert the parsed AST to an actual token stream, e.g.
516+
// un-parse it basically.
517+
//
518+
// Unfortunately there's not really a great way to do that in a
519+
// guaranteed lossless fashion right now. The fallback here is
520+
// to just stringify the AST node and reparse it, but this loses
521+
// all span information.
522+
//
523+
// As a result, some AST nodes are annotated with the token
524+
// stream they came from. Attempt to extract these lossless
525+
// token streams before we fall back to the stringification.
526+
let mut tokens = None;
527+
528+
match nt.0 {
529+
Nonterminal::NtItem(ref item) => {
530+
tokens = prepend_attrs(&item.attrs, item.tokens.as_ref(), span);
531+
}
532+
Nonterminal::NtTraitItem(ref item) => {
533+
tokens = prepend_attrs(&item.attrs, item.tokens.as_ref(), span);
534+
}
535+
Nonterminal::NtImplItem(ref item) => {
536+
tokens = prepend_attrs(&item.attrs, item.tokens.as_ref(), span);
537+
}
538+
_ => {}
539+
}
540+
541+
tokens.map(|tokens| {
542+
TokenNode::Group(Delimiter::None,
543+
TokenStream(tokens.clone()))
544+
}).unwrap_or_else(|| {
545+
__internal::with_sess(|(sess, _)| {
546+
TokenNode::Group(Delimiter::None, TokenStream(nt.1.force(|| {
547+
// FIXME(jseyfried): Avoid this pretty-print + reparse hack
548+
let name = "<macro expansion>".to_owned();
549+
let source = pprust::token_to_string(&token);
550+
parse_stream_from_source_str(name, source, sess, Some(span))
551+
})))
552+
})
553+
})
554+
}
520555

521556
OpenDelim(..) | CloseDelim(..) => unreachable!(),
522557
Whitespace | Comment | Shebang(..) | Eof => unreachable!(),
@@ -580,6 +615,34 @@ impl TokenTree {
580615
}
581616
}
582617

618+
fn prepend_attrs(attrs: &[ast::Attribute],
619+
tokens: Option<&tokenstream::TokenStream>,
620+
span: syntax_pos::Span)
621+
-> Option<tokenstream::TokenStream>
622+
{
623+
let tokens = match tokens {
624+
Some(tokens) => tokens,
625+
None => return None,
626+
};
627+
if attrs.len() == 0 {
628+
return Some(tokens.clone())
629+
}
630+
let mut builder = tokenstream::TokenStreamBuilder::new();
631+
for attr in attrs {
632+
assert_eq!(attr.style, ast::AttrStyle::Outer,
633+
"inner attributes should prevent cached tokens from existing");
634+
let stream = __internal::with_sess(|(sess, _)| {
635+
// FIXME: Avoid this pretty-print + reparse hack as bove
636+
let name = "<macro expansion>".to_owned();
637+
let source = pprust::attr_to_string(attr);
638+
parse_stream_from_source_str(name, source, sess, Some(span))
639+
});
640+
builder.push(stream);
641+
}
642+
builder.push(tokens.clone());
643+
Some(builder.build())
644+
}
645+
583646
/// Permanently unstable internal implementation details of this crate. This
584647
/// should not be used.
585648
///

src/librustc_metadata/cstore_impl.rs

+1
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,7 @@ impl CrateStore for cstore::CStore {
389389
legacy: def.legacy,
390390
}),
391391
vis: ast::Visibility::Inherited,
392+
tokens: None,
392393
})
393394
}
394395

src/libsyntax/ast.rs

+13
Original file line numberDiff line numberDiff line change
@@ -1149,6 +1149,8 @@ pub struct TraitItem {
11491149
pub attrs: Vec<Attribute>,
11501150
pub node: TraitItemKind,
11511151
pub span: Span,
1152+
/// See `Item::tokens` for what this is
1153+
pub tokens: Option<TokenStream>,
11521154
}
11531155

11541156
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
@@ -1168,6 +1170,8 @@ pub struct ImplItem {
11681170
pub attrs: Vec<Attribute>,
11691171
pub node: ImplItemKind,
11701172
pub span: Span,
1173+
/// See `Item::tokens` for what this is
1174+
pub tokens: Option<TokenStream>,
11711175
}
11721176

11731177
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
@@ -1812,6 +1816,15 @@ pub struct Item {
18121816
pub node: ItemKind,
18131817
pub vis: Visibility,
18141818
pub span: Span,
1819+
1820+
/// Original tokens this item was parsed from. This isn't necessarily
1821+
/// available for all items, although over time more and more items should
1822+
/// have this be `Some`. Right now this is primarily used for procedural
1823+
/// macros, notably custom attributes.
1824+
///
1825+
/// Note that the tokens here do not include the outer attributes, but will
1826+
/// include inner attributes.
1827+
pub tokens: Option<TokenStream>,
18151828
}
18161829

18171830
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]

src/libsyntax/diagnostics/plugin.rs

+1
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ pub fn expand_build_diagnostic_array<'cx>(ecx: &'cx mut ExtCtxt,
236236
),
237237
vis: ast::Visibility::Public,
238238
span: span,
239+
tokens: None,
239240
})
240241
]))
241242
}

src/libsyntax/ext/build.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -979,7 +979,8 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
979979
id: ast::DUMMY_NODE_ID,
980980
node: node,
981981
vis: ast::Visibility::Inherited,
982-
span: span
982+
span: span,
983+
tokens: None,
983984
})
984985
}
985986

@@ -1147,7 +1148,8 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
11471148
attrs: vec![],
11481149
node: ast::ItemKind::Use(vp),
11491150
vis: vis,
1150-
span: sp
1151+
span: sp,
1152+
tokens: None,
11511153
})
11521154
}
11531155

src/libsyntax/ext/expand.rs

+1
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
214214
ident: keywords::Invalid.ident(),
215215
id: ast::DUMMY_NODE_ID,
216216
vis: ast::Visibility::Public,
217+
tokens: None,
217218
})));
218219

219220
match self.expand(krate_item).make_items().pop().map(P::unwrap) {

src/libsyntax/ext/placeholders.rs

+3
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,18 @@ pub fn placeholder(kind: ExpansionKind, id: ast::NodeId) -> Expansion {
4646
ExpansionKind::Items => Expansion::Items(SmallVector::one(P(ast::Item {
4747
id: id, span: span, ident: ident, vis: vis, attrs: attrs,
4848
node: ast::ItemKind::Mac(mac_placeholder()),
49+
tokens: None,
4950
}))),
5051
ExpansionKind::TraitItems => Expansion::TraitItems(SmallVector::one(ast::TraitItem {
5152
id: id, span: span, ident: ident, attrs: attrs,
5253
node: ast::TraitItemKind::Macro(mac_placeholder()),
54+
tokens: None,
5355
})),
5456
ExpansionKind::ImplItems => Expansion::ImplItems(SmallVector::one(ast::ImplItem {
5557
id: id, span: span, ident: ident, vis: vis, attrs: attrs,
5658
node: ast::ImplItemKind::Macro(mac_placeholder()),
5759
defaultness: ast::Defaultness::Final,
60+
tokens: None,
5861
})),
5962
ExpansionKind::Pat => Expansion::Pat(P(ast::Pat {
6063
id: id, span: span, node: ast::PatKind::Mac(mac_placeholder()),

src/libsyntax/fold.rs

+11-4
Original file line numberDiff line numberDiff line change
@@ -957,7 +957,8 @@ pub fn noop_fold_trait_item<T: Folder>(i: TraitItem, folder: &mut T)
957957
TraitItemKind::Macro(folder.fold_mac(mac))
958958
}
959959
},
960-
span: folder.new_span(i.span)
960+
span: folder.new_span(i.span),
961+
tokens: i.tokens,
961962
})
962963
}
963964

@@ -980,7 +981,8 @@ pub fn noop_fold_impl_item<T: Folder>(i: ImplItem, folder: &mut T)
980981
ast::ImplItemKind::Type(ty) => ast::ImplItemKind::Type(folder.fold_ty(ty)),
981982
ast::ImplItemKind::Macro(mac) => ast::ImplItemKind::Macro(folder.fold_mac(mac))
982983
},
983-
span: folder.new_span(i.span)
984+
span: folder.new_span(i.span),
985+
tokens: i.tokens,
984986
})
985987
}
986988

@@ -1000,6 +1002,7 @@ pub fn noop_fold_crate<T: Folder>(Crate {module, attrs, span}: Crate,
10001002
vis: ast::Visibility::Public,
10011003
span: span,
10021004
node: ast::ItemKind::Mod(module),
1005+
tokens: None,
10031006
})).into_iter();
10041007

10051008
let (module, attrs, span) = match items.next() {
@@ -1032,15 +1035,19 @@ pub fn noop_fold_item<T: Folder>(i: P<Item>, folder: &mut T) -> SmallVector<P<It
10321035
}
10331036

10341037
// fold one item into exactly one item
1035-
pub fn noop_fold_item_simple<T: Folder>(Item {id, ident, attrs, node, vis, span}: Item,
1038+
pub fn noop_fold_item_simple<T: Folder>(Item {id, ident, attrs, node, vis, span, tokens}: Item,
10361039
folder: &mut T) -> Item {
10371040
Item {
10381041
id: folder.new_id(id),
10391042
vis: folder.fold_vis(vis),
10401043
ident: folder.fold_ident(ident),
10411044
attrs: fold_attrs(attrs, folder),
10421045
node: folder.fold_item_kind(node),
1043-
span: folder.new_span(span)
1046+
span: folder.new_span(span),
1047+
1048+
// FIXME: if this is replaced with a call to `folder.fold_tts` it causes
1049+
// an ICE during resolve... odd!
1050+
tokens: tokens,
10441051
}
10451052
}
10461053

src/libsyntax/parse/mod.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -843,11 +843,18 @@ mod tests {
843843
// check the contents of the tt manually:
844844
#[test] fn parse_fundecl () {
845845
// this test depends on the intern order of "fn" and "i32"
846-
assert_eq!(string_to_item("fn a (b : i32) { b; }".to_string()),
846+
let item = string_to_item("fn a (b : i32) { b; }".to_string()).map(|m| {
847+
m.map(|mut m| {
848+
m.tokens = None;
849+
m
850+
})
851+
});
852+
assert_eq!(item,
847853
Some(
848854
P(ast::Item{ident:Ident::from_str("a"),
849855
attrs:Vec::new(),
850856
id: ast::DUMMY_NODE_ID,
857+
tokens: None,
851858
node: ast::ItemKind::Fn(P(ast::FnDecl {
852859
inputs: vec![ast::Arg{
853860
ty: P(ast::Ty{id: ast::DUMMY_NODE_ID,

0 commit comments

Comments
 (0)