about summary refs log tree commit diff
path: root/src/libsyntax/ext
diff options
context:
space:
mode:
authorDan Aloni <alonid@gmail.com>2018-04-10 02:08:47 +0300
committerDan Aloni <alonid@gmail.com>2018-05-13 19:17:02 +0300
commit37ed2ab91038567bafe3fd2e545c7d1631ff2ab0 (patch)
tree27b8125e37ecd34720f6cee3286e821c6a6df96d /src/libsyntax/ext
parent3e955a058108fcadf0a8222de5868b0c905534d5 (diff)
downloadrust-37ed2ab91038567bafe3fd2e545c7d1631ff2ab0.tar.gz
rust-37ed2ab91038567bafe3fd2e545c7d1631ff2ab0.zip
Macros: Add a 'literal' fragment specifier
Implements RFC 1576.

See: https://github.com/rust-lang/rfcs/blob/master/text/1576-macros-literal-matcher.md

Changes are mostly in libsyntax, docs, and tests. Feature gate is
enabled for 1.27.0.

Many thanks to Vadim Petrochenkov for following through code reviews
and suggestions.

Example:

````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
```
Diffstat (limited to 'src/libsyntax/ext')
-rw-r--r--src/libsyntax/ext/tt/macro_parser.rs2
-rw-r--r--src/libsyntax/ext/tt/macro_rules.rs21
2 files changed, 21 insertions, 2 deletions
diff --git a/src/libsyntax/ext/tt/macro_parser.rs b/src/libsyntax/ext/tt/macro_parser.rs
index 71634ada894..f0339b89839 100644
--- a/src/libsyntax/ext/tt/macro_parser.rs
+++ b/src/libsyntax/ext/tt/macro_parser.rs
@@ -735,6 +735,7 @@ fn may_begin_with(name: &str, token: &Token) -> bool {
         "expr" => token.can_begin_expr(),
         "ty" => token.can_begin_type(),
         "ident" => get_macro_ident(token).is_some(),
+        "literal" => token.can_begin_literal_or_bool(),
         "vis" => match *token {
             // The follow-set of :vis + "priv" keyword + interpolated
             Token::Comma | Token::Ident(..) | Token::Interpolated(_) => true,
@@ -821,6 +822,7 @@ fn parse_nt<'a>(p: &mut Parser<'a>, sp: Span, name: &str) -> Nonterminal {
         },
         "pat" => token::NtPat(panictry!(p.parse_pat())),
         "expr" => token::NtExpr(panictry!(p.parse_expr())),
+        "literal" => token::NtLiteral(panictry!(p.parse_literal_maybe_minus())),
         "ty" => token::NtTy(panictry!(p.parse_ty())),
         // this could be handled like a token, since it is one
         "ident" => if let Some((ident, is_raw)) = get_macro_ident(&p.token) {
diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs
index ffe68289d52..1fc5aed7e7a 100644
--- a/src/libsyntax/ext/tt/macro_rules.rs
+++ b/src/libsyntax/ext/tt/macro_rules.rs
@@ -647,7 +647,7 @@ fn check_matcher_core(sess: &ParseSess,
                     let msg = format!("invalid fragment specifier `{}`", bad_frag);
                     sess.span_diagnostic.struct_span_err(token.span(), &msg)
                         .help("valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, \
-                              `pat`, `ty`, `path`, `meta`, `tt`, `item` and `vis`")
+                              `pat`, `ty`, `literal`, `path`, `meta`, `tt`, `item` and `vis`")
                         .emit();
                     // (This eliminates false positives and duplicates
                     // from error messages.)
@@ -784,6 +784,7 @@ fn frag_can_be_followed_by_any(frag: &str) -> bool {
         "item"     | // always terminated by `}` or `;`
         "block"    | // exactly one token tree
         "ident"    | // exactly one token tree
+        "literal"  | // exactly one token tree
         "meta"     | // exactly one token tree
         "lifetime" | // exactly one token tree
         "tt" =>   // exactly one token tree
@@ -850,6 +851,10 @@ fn is_in_follow(tok: &quoted::TokenTree, frag: &str) -> Result<bool, (String, &'
                 // being a single token, idents and lifetimes are harmless
                 Ok(true)
             },
+            "literal" => {
+                // literals may be of a single token, or two tokens (negative numbers)
+                Ok(true)
+            },
             "meta" | "tt" => {
                 // being either a single token or a delimited sequence, tt is
                 // harmless
@@ -873,7 +878,7 @@ fn is_in_follow(tok: &quoted::TokenTree, frag: &str) -> Result<bool, (String, &'
             _ => Err((format!("invalid fragment specifier `{}`", frag),
                      "valid fragment specifiers are `ident`, `block`, \
                       `stmt`, `expr`, `pat`, `ty`, `path`, `meta`, `tt`, \
-                      `item` and `vis`"))
+                      `literal`, `item` and `vis`"))
         }
     }
 }
@@ -913,6 +918,18 @@ fn is_legal_fragment_specifier(sess: &ParseSess,
             }
             true
         },
+        "literal" => {
+            if !features.macro_literal_matcher &&
+               !attr::contains_name(attrs, "allow_internal_unstable") {
+                let explain = feature_gate::EXPLAIN_LITERAL_MATCHER;
+                emit_feature_err(sess,
+                                 "macro_literal_matcher",
+                                 frag_span,
+                                 GateIssue::Language,
+                                 explain);
+            }
+            true
+        },
         "vis" => {
             if !features.macro_vis_matcher &&
                !attr::contains_name(attrs, "allow_internal_unstable") {