Skip to content

Commit 9fae153

Browse files
committed
Auto merge of #49835 - da-x:literal-fragment-pr, r=petrochenkov
Macros: Add a 'literal' fragment specifier See: #35625 ```rust macro_rules! test_literal { ($l:literal) => { println!("literal: {}", $l); }; ($e:expr) => { println!("expr: {}", $e); }; } fn main() { let a = 1; test_literal!(a); test_literal!(2); test_literal!(-3); } ``` Output: ``` expr: 1 literal: 2 literal: -3 ``` ToDo: * [x] Feature gate * [x] Basic tests * [x] Tests for range patterns * [x] Tests for attributes * [x] Documentation * [x] Fix for `true`/`false` * [x] Fix for negative number literals
2 parents 3e955a0 + 37ed2ab commit 9fae153

File tree

14 files changed

+251
-15
lines changed

14 files changed

+251
-15
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# `macro_literal_matcher`
2+
3+
The tracking issue for this feature is: [#35625]
4+
5+
The RFC is: [rfc#1576].
6+
7+
With this feature gate enabled, the [list of fragment specifiers][frags] gains one more entry:
8+
9+
* `literal`: a literal. Examples: 2, "string", 'c'
10+
11+
A `literal` may be followed by anything, similarly to the `ident` specifier.
12+
13+
[rfc#1576]: http://rust-lang.github.io/rfcs/1576-macros-literal-matcher.html
14+
[#35625]: https://github.com/rust-lang/rust/issues/35625
15+
[frags]: ../book/first-edition/macros.html#syntactic-requirements
16+
17+
------------------------

src/librustc_passes/ast_validation.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ impl<'a> AstValidator<'a> {
114114
}
115115
}
116116

117-
/// matches '-' lit | lit (cf. parser::Parser::parse_pat_literal_maybe_minus),
117+
/// matches '-' lit | lit (cf. parser::Parser::parse_literal_maybe_minus),
118118
/// or path for ranges.
119119
///
120120
/// FIXME: do we want to allow expr -> pattern conversion to create path expressions?

src/libsyntax/attr.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1348,7 +1348,7 @@ impl LitKind {
13481348
Token::Ident(ident, false) if ident.name == "true" => Some(LitKind::Bool(true)),
13491349
Token::Ident(ident, false) if ident.name == "false" => Some(LitKind::Bool(false)),
13501350
Token::Interpolated(ref nt) => match nt.0 {
1351-
token::NtExpr(ref v) => match v.node {
1351+
token::NtExpr(ref v) | token::NtLiteral(ref v) => match v.node {
13521352
ExprKind::Lit(ref lit) => Some(lit.node.clone()),
13531353
_ => None,
13541354
},

src/libsyntax/ext/tt/macro_parser.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -735,6 +735,7 @@ fn may_begin_with(name: &str, token: &Token) -> bool {
735735
"expr" => token.can_begin_expr(),
736736
"ty" => token.can_begin_type(),
737737
"ident" => get_macro_ident(token).is_some(),
738+
"literal" => token.can_begin_literal_or_bool(),
738739
"vis" => match *token {
739740
// The follow-set of :vis + "priv" keyword + interpolated
740741
Token::Comma | Token::Ident(..) | Token::Interpolated(_) => true,
@@ -821,6 +822,7 @@ fn parse_nt<'a>(p: &mut Parser<'a>, sp: Span, name: &str) -> Nonterminal {
821822
},
822823
"pat" => token::NtPat(panictry!(p.parse_pat())),
823824
"expr" => token::NtExpr(panictry!(p.parse_expr())),
825+
"literal" => token::NtLiteral(panictry!(p.parse_literal_maybe_minus())),
824826
"ty" => token::NtTy(panictry!(p.parse_ty())),
825827
// this could be handled like a token, since it is one
826828
"ident" => if let Some((ident, is_raw)) = get_macro_ident(&p.token) {

src/libsyntax/ext/tt/macro_rules.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -647,7 +647,7 @@ fn check_matcher_core(sess: &ParseSess,
647647
let msg = format!("invalid fragment specifier `{}`", bad_frag);
648648
sess.span_diagnostic.struct_span_err(token.span(), &msg)
649649
.help("valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, \
650-
`pat`, `ty`, `path`, `meta`, `tt`, `item` and `vis`")
650+
`pat`, `ty`, `literal`, `path`, `meta`, `tt`, `item` and `vis`")
651651
.emit();
652652
// (This eliminates false positives and duplicates
653653
// from error messages.)
@@ -784,6 +784,7 @@ fn frag_can_be_followed_by_any(frag: &str) -> bool {
784784
"item" | // always terminated by `}` or `;`
785785
"block" | // exactly one token tree
786786
"ident" | // exactly one token tree
787+
"literal" | // exactly one token tree
787788
"meta" | // exactly one token tree
788789
"lifetime" | // exactly one token tree
789790
"tt" => // exactly one token tree
@@ -850,6 +851,10 @@ fn is_in_follow(tok: &quoted::TokenTree, frag: &str) -> Result<bool, (String, &'
850851
// being a single token, idents and lifetimes are harmless
851852
Ok(true)
852853
},
854+
"literal" => {
855+
// literals may be of a single token, or two tokens (negative numbers)
856+
Ok(true)
857+
},
853858
"meta" | "tt" => {
854859
// being either a single token or a delimited sequence, tt is
855860
// harmless
@@ -873,7 +878,7 @@ fn is_in_follow(tok: &quoted::TokenTree, frag: &str) -> Result<bool, (String, &'
873878
_ => Err((format!("invalid fragment specifier `{}`", frag),
874879
"valid fragment specifiers are `ident`, `block`, \
875880
`stmt`, `expr`, `pat`, `ty`, `path`, `meta`, `tt`, \
876-
`item` and `vis`"))
881+
`literal`, `item` and `vis`"))
877882
}
878883
}
879884
}
@@ -913,6 +918,18 @@ fn is_legal_fragment_specifier(sess: &ParseSess,
913918
}
914919
true
915920
},
921+
"literal" => {
922+
if !features.macro_literal_matcher &&
923+
!attr::contains_name(attrs, "allow_internal_unstable") {
924+
let explain = feature_gate::EXPLAIN_LITERAL_MATCHER;
925+
emit_feature_err(sess,
926+
"macro_literal_matcher",
927+
frag_span,
928+
GateIssue::Language,
929+
explain);
930+
}
931+
true
932+
},
916933
"vis" => {
917934
if !features.macro_vis_matcher &&
918935
!attr::contains_name(attrs, "allow_internal_unstable") {

src/libsyntax/feature_gate.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,9 @@ declare_features! (
463463

464464
// Scoped attributes
465465
(active, tool_attributes, "1.25.0", Some(44690), None),
466+
467+
// Allows use of the :literal macro fragment specifier (RFC 1576)
468+
(active, macro_literal_matcher, "1.27.0", Some(35625), None),
466469
);
467470

468471
declare_features! (
@@ -1331,6 +1334,9 @@ pub const EXPLAIN_VIS_MATCHER: &'static str =
13311334
pub const EXPLAIN_LIFETIME_MATCHER: &'static str =
13321335
":lifetime fragment specifier is experimental and subject to change";
13331336

1337+
pub const EXPLAIN_LITERAL_MATCHER: &'static str =
1338+
":literal fragment specifier is experimental and subject to change";
1339+
13341340
pub const EXPLAIN_UNSIZED_TUPLE_COERCION: &'static str =
13351341
"Unsized tuple coercion is not stable enough for use and is subject to change";
13361342

src/libsyntax/fold.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,7 @@ pub fn noop_fold_interpolated<T: Folder>(nt: token::Nonterminal, fld: &mut T)
635635
token::NtTy(ty) => token::NtTy(fld.fold_ty(ty)),
636636
token::NtIdent(ident, is_raw) => token::NtIdent(fld.fold_ident(ident), is_raw),
637637
token::NtLifetime(ident) => token::NtLifetime(fld.fold_ident(ident)),
638+
token::NtLiteral(expr) => token::NtLiteral(fld.fold_expr(expr)),
638639
token::NtMeta(meta) => token::NtMeta(fld.fold_meta_item(meta)),
639640
token::NtPath(path) => token::NtPath(fld.fold_path(path)),
640641
token::NtTT(tt) => token::NtTT(fld.fold_tt(tt)),

src/libsyntax/parse/parser.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ macro_rules! maybe_whole_expr {
115115
($p:expr) => {
116116
if let token::Interpolated(nt) = $p.token.clone() {
117117
match nt.0 {
118-
token::NtExpr(ref e) => {
118+
token::NtExpr(ref e) | token::NtLiteral(ref e) => {
119119
$p.bump();
120120
return Ok((*e).clone());
121121
}
@@ -1823,7 +1823,7 @@ impl<'a> Parser<'a> {
18231823
pub fn parse_lit_token(&mut self) -> PResult<'a, LitKind> {
18241824
let out = match self.token {
18251825
token::Interpolated(ref nt) => match nt.0 {
1826-
token::NtExpr(ref v) => match v.node {
1826+
token::NtExpr(ref v) | token::NtLiteral(ref v) => match v.node {
18271827
ExprKind::Lit(ref lit) => { lit.node.clone() }
18281828
_ => { return self.unexpected_last(&self.token); }
18291829
},
@@ -1862,7 +1862,7 @@ impl<'a> Parser<'a> {
18621862
}
18631863

18641864
/// matches '-' lit | lit (cf. ast_validation::AstValidator::check_expr_within_pat)
1865-
pub fn parse_pat_literal_maybe_minus(&mut self) -> PResult<'a, P<Expr>> {
1865+
pub fn parse_literal_maybe_minus(&mut self) -> PResult<'a, P<Expr>> {
18661866
maybe_whole_expr!(self);
18671867

18681868
let minus_lo = self.span;
@@ -2407,10 +2407,10 @@ impl<'a> Parser<'a> {
24072407
hi = pth.span;
24082408
ex = ExprKind::Path(None, pth);
24092409
} else {
2410-
match self.parse_lit() {
2411-
Ok(lit) => {
2412-
hi = lit.span;
2413-
ex = ExprKind::Lit(P(lit));
2410+
match self.parse_literal_maybe_minus() {
2411+
Ok(expr) => {
2412+
hi = expr.span;
2413+
ex = expr.node.clone();
24142414
}
24152415
Err(mut err) => {
24162416
self.cancel(&mut err);
@@ -3724,7 +3724,7 @@ impl<'a> Parser<'a> {
37243724
let hi = self.prev_span;
37253725
Ok(self.mk_expr(lo.to(hi), ExprKind::Path(qself, path), ThinVec::new()))
37263726
} else {
3727-
self.parse_pat_literal_maybe_minus()
3727+
self.parse_literal_maybe_minus()
37283728
}
37293729
}
37303730

@@ -3914,7 +3914,7 @@ impl<'a> Parser<'a> {
39143914
}
39153915
} else {
39163916
// Try to parse everything else as literal with optional minus
3917-
match self.parse_pat_literal_maybe_minus() {
3917+
match self.parse_literal_maybe_minus() {
39183918
Ok(begin) => {
39193919
if self.eat(&token::DotDotDot) {
39203920
let end = self.parse_pat_range_end()?;

src/libsyntax/parse/token.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,12 @@ impl Token {
280280
Lifetime(..) | // labeled loop
281281
Pound => true, // expression attributes
282282
Interpolated(ref nt) => match nt.0 {
283-
NtIdent(..) | NtExpr(..) | NtBlock(..) | NtPath(..) | NtLifetime(..) => true,
283+
NtLiteral(..) |
284+
NtIdent(..) |
285+
NtExpr(..) |
286+
NtBlock(..) |
287+
NtPath(..) |
288+
NtLifetime(..) => true,
284289
_ => false,
285290
},
286291
_ => false,
@@ -324,6 +329,18 @@ impl Token {
324329
}
325330
}
326331

332+
/// Returns `true` if the token is any literal, a minus (which can follow a literal,
333+
/// for example a '-42', or one of the boolean idents).
334+
pub fn can_begin_literal_or_bool(&self) -> bool {
335+
match *self {
336+
Literal(..) => true,
337+
BinOp(Minus) => true,
338+
Ident(ident, false) if ident.name == keywords::True.name() => true,
339+
Ident(ident, false) if ident.name == keywords::False.name() => true,
340+
_ => false,
341+
}
342+
}
343+
327344
/// Returns an identifier if this token is an identifier.
328345
pub fn ident(&self) -> Option<(ast::Ident, /* is_raw */ bool)> {
329346
match *self {
@@ -672,6 +689,7 @@ pub enum Nonterminal {
672689
NtTy(P<ast::Ty>),
673690
NtIdent(ast::Ident, /* is_raw */ bool),
674691
NtLifetime(ast::Ident),
692+
NtLiteral(P<ast::Expr>),
675693
/// Stuff inside brackets for attributes
676694
NtMeta(ast::MetaItem),
677695
NtPath(ast::Path),
@@ -713,6 +731,7 @@ impl fmt::Debug for Nonterminal {
713731
NtExpr(..) => f.pad("NtExpr(..)"),
714732
NtTy(..) => f.pad("NtTy(..)"),
715733
NtIdent(..) => f.pad("NtIdent(..)"),
734+
NtLiteral(..) => f.pad("NtLiteral(..)"),
716735
NtMeta(..) => f.pad("NtMeta(..)"),
717736
NtPath(..) => f.pad("NtPath(..)"),
718737
NtTT(..) => f.pad("NtTT(..)"),

src/libsyntax/print/pprust.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ pub fn token_to_string(tok: &Token) -> String {
273273
token::NtIdent(e, false) => ident_to_string(e),
274274
token::NtIdent(e, true) => format!("r#{}", ident_to_string(e)),
275275
token::NtLifetime(e) => ident_to_string(e),
276+
token::NtLiteral(ref e) => expr_to_string(e),
276277
token::NtTT(ref tree) => tt_to_string(tree.clone()),
277278
token::NtArm(ref e) => arm_to_string(e),
278279
token::NtImplItem(ref e) => impl_item_to_string(e),

0 commit comments

Comments
 (0)