Skip to content

Commit c106125

Browse files
committed
Represent lifetimes as two joint tokens in proc macros
1 parent 5b820a6 commit c106125

File tree

12 files changed

+158
-21
lines changed

12 files changed

+158
-21
lines changed

src/libproc_macro/lib.rs

+18-16
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,7 @@ impl PartialEq<FileName> for SourceFile {
487487
pub enum TokenTree {
488488
/// A token stream surrounded by bracket delimiters.
489489
Group(Group),
490-
/// An identifier or lifetime identifier.
490+
/// An identifier.
491491
Ident(Ident),
492492
/// A single punctuation character (`+`, `,`, `$`, etc.).
493493
Punct(Punct),
@@ -702,9 +702,10 @@ impl !Sync for Punct {}
702702
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
703703
#[unstable(feature = "proc_macro", issue = "38356")]
704704
pub enum Spacing {
705-
/// e.g. `+` is `Alone` in `+ =`, `+ident` or `+()`.
705+
/// E.g. `+` is `Alone` in `+ =`, `+ident` or `+()`.
706706
Alone,
707-
/// e.g. `+` is `Joint` in `+=` or `+#`.
707+
/// E.g. `+` is `Joint` in `+=` or `'#`.
708+
/// Additionally, single quote `'` can join with identifiers to form lifetimes `'ident`.
708709
Joint,
709710
}
710711

@@ -717,8 +718,8 @@ impl Punct {
717718
/// which can be further configured with the `set_span` method below.
718719
#[unstable(feature = "proc_macro", issue = "38356")]
719720
pub fn new(ch: char, spacing: Spacing) -> Punct {
720-
const LEGAL_CHARS: &[char] = &['=', '<', '>', '!', '~', '+', '-', '*', '/', '%',
721-
'^', '&', '|', '@', '.', ',', ';', ':', '#', '$', '?'];
721+
const LEGAL_CHARS: &[char] = &['=', '<', '>', '!', '~', '+', '-', '*', '/', '%', '^',
722+
'&', '|', '@', '.', ',', ';', ':', '#', '$', '?', '\''];
722723
if !LEGAL_CHARS.contains(&ch) {
723724
panic!("unsupported character `{:?}`", ch)
724725
}
@@ -766,7 +767,7 @@ impl fmt::Display for Punct {
766767
}
767768
}
768769

769-
/// An identifier (`ident`) or lifetime identifier (`'ident`).
770+
/// An identifier (`ident`).
770771
#[derive(Clone, Debug)]
771772
#[unstable(feature = "proc_macro", issue = "38356")]
772773
pub struct Ident {
@@ -783,7 +784,7 @@ impl !Sync for Ident {}
783784
impl Ident {
784785
/// Creates a new `Ident` with the given `string` as well as the specified
785786
/// `span`.
786-
/// The `string` argument must be a valid identifier or lifetime identifier permitted by the
787+
/// The `string` argument must be a valid identifier permitted by the
787788
/// language, otherwise the function will panic.
788789
///
789790
/// Note that `span`, currently in rustc, configures the hygiene information
@@ -817,8 +818,7 @@ impl Ident {
817818
pub fn new_raw(string: &str, span: Span) -> Ident {
818819
let mut ident = Ident::new(string, span);
819820
if ident.sym == keywords::Underscore.name() ||
820-
token::is_path_segment_keyword(ast::Ident::with_empty_ctxt(ident.sym)) ||
821-
ident.sym.as_str().starts_with("\'") {
821+
token::is_path_segment_keyword(ast::Ident::with_empty_ctxt(ident.sym)) {
822822
panic!("`{:?}` is not a valid raw identifier", string)
823823
}
824824
ident.is_raw = true;
@@ -1211,13 +1211,19 @@ impl TokenTree {
12111211
Pound => op!('#'),
12121212
Dollar => op!('$'),
12131213
Question => op!('?'),
1214+
SingleQuote => op!('\''),
12141215

1215-
Ident(ident, false) | Lifetime(ident) => {
1216+
Ident(ident, false) => {
12161217
tt!(self::Ident::new(&ident.name.as_str(), Span(span)))
12171218
}
12181219
Ident(ident, true) => {
12191220
tt!(self::Ident::new_raw(&ident.name.as_str(), Span(span)))
12201221
}
1222+
Lifetime(ident) => {
1223+
let ident = ident.without_first_quote();
1224+
stack.push(tt!(self::Ident::new(&ident.name.as_str(), Span(span))));
1225+
tt!(Punct::new('\'', Spacing::Joint))
1226+
}
12211227
Literal(lit, suffix) => tt!(self::Literal { lit, suffix, span: Span(span) }),
12221228
DocComment(c) => {
12231229
let style = comments::doc_comment_style(&c.as_str());
@@ -1260,12 +1266,7 @@ impl TokenTree {
12601266
}).into();
12611267
},
12621268
self::TokenTree::Ident(tt) => {
1263-
let ident = ast::Ident::new(tt.sym, tt.span.0);
1264-
let token = if tt.sym.as_str().starts_with("'") {
1265-
Lifetime(ident)
1266-
} else {
1267-
Ident(ident, tt.is_raw)
1268-
};
1269+
let token = Ident(ast::Ident::new(tt.sym, tt.span.0), tt.is_raw);
12691270
return TokenTree::Token(tt.span.0, token).into();
12701271
}
12711272
self::TokenTree::Literal(self::Literal {
@@ -1324,6 +1325,7 @@ impl TokenTree {
13241325
'#' => Pound,
13251326
'$' => Dollar,
13261327
'?' => Question,
1328+
'\'' => SingleQuote,
13271329
_ => unreachable!(),
13281330
};
13291331

src/librustc/ich/impls_syntax.rs

+1
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ fn hash_token<'a, 'gcx, W: StableHasherResult>(
314314
token::Token::Pound |
315315
token::Token::Dollar |
316316
token::Token::Question |
317+
token::Token::SingleQuote |
317318
token::Token::Whitespace |
318319
token::Token::Comment |
319320
token::Token::Eof => {}

src/librustdoc/html/highlight.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ impl<'a> Classifier<'a> {
353353
token::Lifetime(..) => Class::Lifetime,
354354

355355
token::Eof | token::Interpolated(..) |
356-
token::Tilde | token::At | token::DotEq => Class::None,
356+
token::Tilde | token::At | token::DotEq | token::SingleQuote => Class::None,
357357
};
358358

359359
// Anything that didn't return above is the simple case where we the

src/libsyntax/ext/quote.rs

+1
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,7 @@ fn expr_mk_token(cx: &ExtCtxt, sp: Span, tok: &token::Token) -> P<ast::Expr> {
711711
token::Pound => "Pound",
712712
token::Dollar => "Dollar",
713713
token::Question => "Question",
714+
token::SingleQuote => "SingleQuote",
714715
token::Eof => "Eof",
715716

716717
token::Whitespace | token::Comment | token::Shebang(_) => {

src/libsyntax/parse/lexer/mod.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -1773,10 +1773,7 @@ fn ident_continue(c: Option<char>) -> bool {
17731773
// The string is a valid identifier or a lifetime identifier.
17741774
pub fn is_valid_ident(s: &str) -> bool {
17751775
let mut chars = s.chars();
1776-
match chars.next() {
1777-
Some('\'') => ident_start(chars.next()) && chars.all(|ch| ident_continue(Some(ch))),
1778-
ch => ident_start(ch) && chars.all(|ch| ident_continue(Some(ch)))
1779-
}
1776+
ident_start(chars.next()) && chars.all(|ch| ident_continue(Some(ch)))
17801777
}
17811778

17821779
#[cfg(test)]

src/libsyntax/parse/token.rs

+6
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,8 @@ pub enum Token {
210210
Pound,
211211
Dollar,
212212
Question,
213+
/// Used by proc macros for representing lifetimes, not generated by lexer right now.
214+
SingleQuote,
213215
/// An opening delimiter, eg. `{`
214216
OpenDelim(DelimToken),
215217
/// A closing delimiter, eg. `}`
@@ -513,6 +515,10 @@ impl Token {
513515
Colon => ModSep,
514516
_ => return None,
515517
},
518+
SingleQuote => match joint {
519+
Ident(ident, false) => Lifetime(ident),
520+
_ => return None,
521+
},
516522

517523
Le | EqEq | Ne | Ge | AndAnd | OrOr | Tilde | BinOpEq(..) | At | DotDotDot | DotEq |
518524
DotDotEq | Comma | Semi | ModSep | RArrow | LArrow | FatArrow | Pound | Dollar |

src/libsyntax/print/pprust.rs

+1
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ pub fn token_to_string(tok: &Token) -> String {
224224
token::Pound => "#".to_string(),
225225
token::Dollar => "$".to_string(),
226226
token::Question => "?".to_string(),
227+
token::SingleQuote => "'".to_string(),
227228

228229
/* Literals */
229230
token::Literal(lit, suf) => {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// no-prefer-dynamic
12+
13+
#![feature(proc_macro)]
14+
#![crate_type = "proc-macro"]
15+
16+
extern crate proc_macro;
17+
18+
use proc_macro::*;
19+
20+
#[proc_macro]
21+
pub fn lifetimes_bang(input: TokenStream) -> TokenStream {
22+
// Roundtrip through token trees
23+
input.into_iter().collect()
24+
}
25+
26+
#[proc_macro_attribute]
27+
pub fn lifetimes_attr(_: TokenStream, input: TokenStream) -> TokenStream {
28+
// Roundtrip through AST
29+
input
30+
}
31+
32+
#[proc_macro_derive(Lifetimes)]
33+
pub fn lifetimes_derive(input: TokenStream) -> TokenStream {
34+
// Roundtrip through a string
35+
format!("mod m {{ {} }}", input).parse().unwrap()
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// aux-build:lifetimes.rs
12+
// ignore-stage1
13+
14+
#![feature(proc_macro)]
15+
16+
extern crate lifetimes;
17+
use lifetimes::*;
18+
19+
lifetimes_bang! {
20+
fn bang<'a>() -> &'a u8 { &0 }
21+
}
22+
23+
#[lifetimes_attr]
24+
fn attr<'a>() -> &'a u8 { &1 }
25+
26+
#[derive(Lifetimes)]
27+
pub struct Lifetimes<'a> {
28+
pub field: &'a u8,
29+
}
30+
31+
fn main() {
32+
assert_eq!(bang::<'static>(), &0);
33+
assert_eq!(attr::<'static>(), &1);
34+
let l1 = Lifetimes { field: &0 };
35+
let l2 = m::Lifetimes { field: &1 };
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// no-prefer-dynamic
12+
13+
#![feature(proc_macro)]
14+
#![crate_type = "proc-macro"]
15+
16+
extern crate proc_macro;
17+
18+
use proc_macro::*;
19+
20+
#[proc_macro]
21+
pub fn single_quote_alone(_: TokenStream) -> TokenStream {
22+
// `&'a u8`, but the `'` token is not joint
23+
let trees: Vec<TokenTree> = vec![
24+
Punct::new('&', Spacing::Alone).into(),
25+
Punct::new('\'', Spacing::Alone).into(),
26+
Ident::new("a", Span::call_site()).into(),
27+
Ident::new("u8", Span::call_site()).into(),
28+
];
29+
trees.into_iter().collect()
30+
}

src/test/ui-fulldeps/lifetimes.rs

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// aux-build:lifetimes.rs
12+
13+
#![feature(proc_macro, proc_macro_non_items)]
14+
15+
extern crate lifetimes;
16+
17+
use lifetimes::*;
18+
19+
type A = single_quote_alone!(); //~ ERROR expected type, found `'`

src/test/ui-fulldeps/lifetimes.stderr

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: expected type, found `'`
2+
--> $DIR/lifetimes.rs:19:10
3+
|
4+
LL | type A = single_quote_alone!(); //~ ERROR expected type, found `'`
5+
| ^^^^^^^^^^^^^^^^^^^^^
6+
7+
error: aborting due to previous error
8+

0 commit comments

Comments
 (0)