Do not use Ident to represent Lifetime
diff --git a/src/expr.rs b/src/expr.rs
index f64bc31..96a593d 100644
--- a/src/expr.rs
+++ b/src/expr.rs
@@ -139,7 +139,7 @@
pub While(ExprWhile {
pub cond: Box<Expr>,
pub body: Block,
- pub label: Option<Ident>,
+ pub label: Option<Lifetime>,
pub colon_token: Option<tokens::Colon>,
pub while_token: tokens::While,
}),
@@ -153,7 +153,7 @@
pub pat: Box<Pat>,
pub expr: Box<Expr>,
pub body: Block,
- pub label: Option<Ident>,
+ pub label: Option<Lifetime>,
pub colon_token: Option<tokens::Colon>,
pub while_token: tokens::While,
pub let_token: tokens::Let,
@@ -169,7 +169,7 @@
pub pat: Box<Pat>,
pub expr: Box<Expr>,
pub body: Block,
- pub label: Option<Ident>,
+ pub label: Option<Lifetime>,
pub for_token: tokens::For,
pub colon_token: Option<tokens::Colon>,
pub in_token: tokens::In,
@@ -180,7 +180,7 @@
/// E.g. `'label: loop { block }`
pub Loop(ExprLoop {
pub body: Block,
- pub label: Option<Ident>,
+ pub label: Option<Lifetime>,
pub loop_token: tokens::Loop,
pub colon_token: Option<tokens::Colon>,
}),
@@ -273,14 +273,14 @@
/// A `break`, with an optional label to break, and an optional expression
pub Break(ExprBreak {
- pub label: Option<Ident>,
+ pub label: Option<Lifetime>,
pub expr: Option<Box<Expr>>,
pub break_token: tokens::Break,
}),
/// A `continue`, with an optional label
pub Continue(ExprContinue {
- pub label: Option<Ident>,
+ pub label: Option<Lifetime>,
pub continue_token: tokens::Continue,
}),
@@ -978,7 +978,7 @@
impl Synom for ExprForLoop {
named!(parse -> Self, do_parse!(
- lbl: option!(tuple!(label, syn!(Colon))) >>
+ lbl: option!(tuple!(syn!(Lifetime), syn!(Colon))) >>
for_: syn!(For) >>
pat: syn!(Pat) >>
in_: syn!(In) >>
@@ -998,7 +998,7 @@
impl Synom for ExprLoop {
named!(parse -> Self, do_parse!(
- lbl: option!(tuple!(label, syn!(Colon))) >>
+ lbl: option!(tuple!(syn!(Lifetime), syn!(Colon))) >>
loop_: syn!(Loop) >>
loop_block: syn!(Block) >>
(ExprLoop {
@@ -1150,7 +1150,7 @@
impl Synom for ExprWhile {
named!(parse -> Self, do_parse!(
- lbl: option!(tuple!(label, syn!(Colon))) >>
+ lbl: option!(tuple!(syn!(Lifetime), syn!(Colon))) >>
while_: syn!(While) >>
cond: expr_no_struct >>
while_block: syn!(Block) >>
@@ -1166,7 +1166,7 @@
impl Synom for ExprWhileLet {
named!(parse -> Self, do_parse!(
- lbl: option!(tuple!(label, syn!(Colon))) >>
+ lbl: option!(tuple!(syn!(Lifetime), syn!(Colon))) >>
while_: syn!(While) >>
let_: syn!(Let) >>
pat: syn!(Pat) >>
@@ -1189,7 +1189,7 @@
impl Synom for ExprContinue {
named!(parse -> Self, do_parse!(
cont: syn!(Continue) >>
- lbl: option!(label) >>
+ lbl: option!(syn!(Lifetime)) >>
(ExprContinue {
continue_token: cont,
label: lbl,
@@ -1199,7 +1199,7 @@
named_ambiguous_expr!(expr_break -> ExprKind, allow_struct, do_parse!(
break_: syn!(Break) >>
- lbl: option!(label) >>
+ lbl: option!(syn!(Lifetime)) >>
val: option!(call!(ambiguous_expr, allow_struct, false)) >>
(ExprBreak {
label: lbl,
@@ -1800,8 +1800,6 @@
epsilon!() => { |_| CaptureBy::Ref }
));
}
-
- named!(label -> Ident, map!(syn!(Lifetime), |lt: Lifetime| lt.ident));
}
#[cfg(feature = "printing")]
diff --git a/src/generics.rs b/src/generics.rs
index 2c30a5f..60a94a6 100644
--- a/src/generics.rs
+++ b/src/generics.rs
@@ -67,24 +67,6 @@
}
ast_struct! {
- pub struct Lifetime {
- pub ident: Ident,
- }
-}
-
-impl Lifetime {
- pub fn new<T: Into<Ident>>(t: T) -> Self {
- let id = t.into();
- if !id.as_ref().starts_with('\'') {
- panic!("lifetime name must start with apostrophe as in \"'a\", \
- got {:?}",
- id.as_ref());
- }
- Lifetime { ident: id }
- }
-}
-
-ast_struct! {
/// A set of bound lifetimes, e.g. `for<'a, 'b, 'c>`
#[derive(Default)]
pub struct BoundLifetimes {
@@ -106,10 +88,10 @@
}
impl LifetimeDef {
- pub fn new<T: Into<Ident>>(t: T) -> Self {
+ pub fn new(lifetime: Lifetime) -> Self {
LifetimeDef {
attrs: Vec::new(),
- lifetime: Lifetime::new(t),
+ lifetime: lifetime,
colon_token: None,
bounds: Delimited::new(),
}
@@ -211,7 +193,7 @@
pub mod parsing {
use super::*;
- use synom::{PResult, Cursor, Synom, parse_error};
+ use synom::Synom;
use synom::tokens::*;
impl Synom for Generics {
@@ -240,25 +222,6 @@
));
}
- impl Synom for Lifetime {
- fn parse(input: Cursor) -> PResult<Self> {
- match input.word() {
- Some((rest, span, sym)) => {
- if sym.as_str().starts_with('\'') {
- return Ok((rest, Lifetime {
- ident: Ident {
- span: Span(span),
- sym: sym
- }
- }));
- }
- }
- _ => {}
- }
- parse_error()
- }
- }
-
impl Synom for LifetimeDef {
named!(parse -> Self, do_parse!(
attrs: many0!(call!(Attribute::parse_outer)) >>
@@ -448,12 +411,6 @@
}
}
- impl ToTokens for Lifetime {
- fn to_tokens(&self, tokens: &mut Tokens) {
- self.ident.to_tokens(tokens);
- }
- }
-
impl ToTokens for BoundLifetimes {
fn to_tokens(&self, tokens: &mut Tokens) {
self.for_token.to_tokens(tokens);
diff --git a/src/lib.rs b/src/lib.rs
index 8e63ac4..8c55d64 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -41,7 +41,7 @@
PatBox, PatRef, PatLit, PatRange, PatSlice};
mod generics;
-pub use generics::{Generics, Lifetime, LifetimeDef, TraitBoundModifier, TyParam, TyParamBound,
+pub use generics::{Generics, LifetimeDef, TraitBoundModifier, TyParam, TyParamBound,
WhereBoundPredicate, WhereClause, WhereEqPredicate, WherePredicate,
WhereRegionPredicate, BoundLifetimes};
#[cfg(feature = "printing")]
@@ -68,6 +68,9 @@
#[cfg(feature = "full")]
pub use krate::Crate;
+mod lifetime;
+pub use lifetime::Lifetime;
+
mod lit;
pub use lit::{Lit, LitKind};
diff --git a/src/lifetime.rs b/src/lifetime.rs
new file mode 100644
index 0000000..56582d2
--- /dev/null
+++ b/src/lifetime.rs
@@ -0,0 +1,129 @@
+use std::cmp::Ordering;
+use std::fmt::{self, Display};
+use std::hash::{Hash, Hasher};
+
+use proc_macro2::Symbol;
+use unicode_xid::UnicodeXID;
+
+use Span;
+
+#[cfg_attr(feature = "extra-traits", derive(Debug))]
+#[cfg_attr(feature = "clone-impls", derive(Clone))]
+pub struct Lifetime {
+ pub sym: Symbol,
+ pub span: Span,
+}
+
+impl Lifetime {
+ pub fn new(sym: Symbol, span: Span) -> Self {
+ let s = sym.as_str();
+
+ if !s.starts_with('\'') {
+ panic!("lifetime name must start with apostrophe as in \"'a\", \
+ got {:?}",
+ s);
+ }
+
+ if s == "'" {
+ panic!("lifetime name must not be empty");
+ }
+
+ if s == "'_" {
+ panic!("\"'_\" is not a valid lifetime name");
+ }
+
+ fn xid_ok(s: &str) -> bool {
+ let mut chars = s.chars();
+ let first = chars.next().unwrap();
+ if !(UnicodeXID::is_xid_start(first) || first == '_') {
+ return false;
+ }
+ for ch in chars {
+ if !UnicodeXID::is_xid_continue(ch) {
+ return false;
+ }
+ }
+ true
+ }
+
+ if !xid_ok(&s[1..]) {
+ panic!("{:?} is not a valid lifetime name");
+ }
+
+ Lifetime {
+ sym: sym,
+ span: span,
+ }
+ }
+}
+
+impl Display for Lifetime {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ self.sym.as_str().fmt(formatter)
+ }
+}
+
+impl PartialEq for Lifetime {
+ fn eq(&self, other: &Lifetime) -> bool {
+ self.sym.as_str() == other.sym.as_str()
+ }
+}
+
+impl Eq for Lifetime {}
+
+impl PartialOrd for Lifetime {
+ fn partial_cmp(&self, other: &Lifetime) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl Ord for Lifetime {
+ fn cmp(&self, other: &Lifetime) -> Ordering {
+ self.sym.as_str().cmp(other.sym.as_str())
+ }
+}
+
+impl Hash for Lifetime {
+ fn hash<H: Hasher>(&self, h: &mut H) {
+ self.sym.as_str().hash(h)
+ }
+}
+
+#[cfg(feature = "parsing")]
+pub mod parsing {
+ use super::*;
+ use synom::{Synom, PResult, Cursor, parse_error};
+
+ impl Synom for Lifetime {
+ fn parse(input: Cursor) -> PResult<Self> {
+ let (rest, span, sym) = match input.word() {
+ Some(word) => word,
+ _ => return parse_error(),
+ };
+ if !sym.as_str().starts_with('\'') {
+ return parse_error();
+ }
+
+ Ok((rest, Lifetime {
+ sym: sym,
+ span: Span(span),
+ }))
+ }
+ }
+}
+
+#[cfg(feature = "printing")]
+mod printing {
+ use super::*;
+ use quote::{Tokens, ToTokens};
+ use proc_macro2::{TokenTree, TokenKind};
+
+ impl ToTokens for Lifetime {
+ fn to_tokens(&self, tokens: &mut Tokens) {
+ tokens.append(TokenTree {
+ span: self.span.0,
+ kind: TokenKind::Word(self.sym),
+ })
+ }
+ }
+}
diff --git a/tests/test_expr.rs b/tests/test_expr.rs
index 749d50f..0e4b574 100644
--- a/tests/test_expr.rs
+++ b/tests/test_expr.rs
@@ -88,9 +88,9 @@
let actual = raw.parse::<Crate>().unwrap();
- assert_eq!(&actual.items[0].ident, "catch");
-
- assert_let!(ItemKind::Struct(..) = actual.items[0].node);
+ assert_let!(ItemKind::Struct(ItemStruct { ref ident, .. }) = actual.items[0].node; {
+ assert_eq!(ident, "catch");
+ });
assert_let!(Item { node: ItemKind::Fn(ItemFn { ref block, .. }), .. } = actual.items[1]; {
assert_let!(Stmt::Local(ref local) = block.stmts[0]; {
diff --git a/tests/test_generics.rs b/tests/test_generics.rs
index 6c37035..8bb6171 100644
--- a/tests/test_generics.rs
+++ b/tests/test_generics.rs
@@ -15,14 +15,14 @@
lifetimes: vec![
LifetimeDef {
attrs: Default::default(),
- lifetime: Lifetime::new("'a"),
+ lifetime: Lifetime::new("'a".into(), Span::default()),
bounds: Default::default(),
colon_token: None,
},
LifetimeDef {
attrs: Default::default(),
- lifetime: Lifetime::new("'b"),
- bounds: vec![Lifetime::new("'a")].into(),
+ lifetime: Lifetime::new("'b".into(), Span::default()),
+ bounds: vec![Lifetime::new("'a".into(), Span::default())].into(),
colon_token: Some(tokens::Colon::default()),
},
].into(),
@@ -37,7 +37,7 @@
is_sugared_doc: false,
}],
ident: "T".into(),
- bounds: vec![TyParamBound::Region(Lifetime::new("'a"))].into(),
+ bounds: vec![TyParamBound::Region(Lifetime::new("'a".into(), Span::default()))].into(),
default: Some(TyTup {
tys: Default::default(),
lone_comma: None,
@@ -92,7 +92,7 @@
#[test]
fn test_ty_param_bound() {
let tokens = quote!('a);
- let expected = TyParamBound::Region(Lifetime::new("'a"));
+ let expected = TyParamBound::Region(Lifetime::new("'a".into(), Span::default()));
assert_eq!(expected, tokens.to_string().parse().unwrap());
let tokens = quote!(Debug);
diff --git a/tests/test_macro_input.rs b/tests/test_macro_input.rs
index bddf52e..5c2715c 100644
--- a/tests/test_macro_input.rs
+++ b/tests/test_macro_input.rs
@@ -115,20 +115,20 @@
qself: None,
path: Path {
leading_colon: None,
- global: false,
segments: vec![
PathSegment {
ident: "Vec".into(),
parameters: PathParameters::AngleBracketed(
AngleBracketedParameterData {
- gt_token: Some(Default::default()),
- lt_token: Some(Default::default()),
+ turbofish: None,
+ lt_token: Default::default(),
lifetimes: Default::default(),
types: vec![Ty::from(TyPath {
qself: None,
path: "Attribute".into(),
})].into(),
bindings: Default::default(),
+ gt_token: Default::default(),
},
),
}
@@ -347,7 +347,6 @@
pound_token: Default::default(),
style: AttrStyle::Outer,
path: Path {
- global: true,
leading_colon: Some(Default::default()),
segments: vec![
PathSegment::from("attr_args"),
@@ -402,7 +401,6 @@
pound_token: Default::default(),
style: AttrStyle::Outer,
path: Path {
- global: false,
leading_colon: None,
segments: vec![
PathSegment::from("inert"),
@@ -445,7 +443,6 @@
pound_token: Default::default(),
style: AttrStyle::Outer,
path: Path {
- global: false,
leading_colon: None,
segments: vec![
PathSegment::from("foo"),
@@ -492,7 +489,6 @@
ident: None,
vis: Visibility::Restricted(VisRestricted {
path: Box::new(Path {
- global: false,
leading_colon: None,
segments: vec![
PathSegment::from("m"),