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 ```
This commit is contained in:
parent
3e955a0581
commit
37ed2ab910
14 changed files with 251 additions and 15 deletions
143
src/test/run-pass/macro-literal.rs
Normal file
143
src/test/run-pass/macro-literal.rs
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(macro_literal_matcher)]
|
||||
|
||||
macro_rules! mtester {
|
||||
($l:literal) => {
|
||||
&format!("macro caught literal: {}", $l)
|
||||
};
|
||||
($e:expr) => {
|
||||
&format!("macro caught expr: {}", $e)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! two_negative_literals {
|
||||
($l1:literal $l2:literal) => {
|
||||
&format!("macro caught literals: {}, {}", $l1, $l2)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! only_expr {
|
||||
($e:expr) => {
|
||||
&format!("macro caught expr: {}", $e)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! mtester_dbg {
|
||||
($l:literal) => {
|
||||
&format!("macro caught literal: {:?}", $l)
|
||||
};
|
||||
($e:expr) => {
|
||||
&format!("macro caught expr: {:?}", $e)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! catch_range {
|
||||
($s:literal ... $e:literal) => {
|
||||
&format!("macro caught literal: {} ... {}", $s, $e)
|
||||
};
|
||||
(($s:expr) ... ($e:expr)) => { // Must use ')' before '...'
|
||||
&format!("macro caught expr: {} ... {}", $s, $e)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! pat_match {
|
||||
($s:literal ... $e:literal) => {
|
||||
match 3 {
|
||||
$s ... $e => "literal, in range",
|
||||
_ => "literal, other",
|
||||
}
|
||||
};
|
||||
($s:pat) => {
|
||||
match 3 {
|
||||
$s => "pat, single",
|
||||
_ => "pat, other",
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! match_attr {
|
||||
(#[$attr:meta] $e:literal) => {
|
||||
"attr matched literal"
|
||||
};
|
||||
(#[$attr:meta] $e:expr) => {
|
||||
"attr matched expr"
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! match_produced_attr {
|
||||
($lit: literal) => {
|
||||
// Struct with doc comment passed via $literal
|
||||
#[doc = $lit]
|
||||
struct LiteralProduced;
|
||||
};
|
||||
($expr: expr) => {
|
||||
struct ExprProduced;
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! test_user {
|
||||
($s:literal, $e:literal) => {
|
||||
{
|
||||
let mut v = Vec::new();
|
||||
for i in $s .. $e {
|
||||
v.push(i);
|
||||
}
|
||||
"literal"
|
||||
}
|
||||
};
|
||||
($s:expr, $e: expr) => {
|
||||
{
|
||||
let mut v = Vec::new();
|
||||
for i in $s .. $e {
|
||||
v.push(i);
|
||||
}
|
||||
"expr"
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
// Cases where 'literal' catches
|
||||
assert_eq!(mtester!("str"), "macro caught literal: str");
|
||||
assert_eq!(mtester!(2), "macro caught literal: 2");
|
||||
assert_eq!(mtester!(2.2), "macro caught literal: 2.2");
|
||||
assert_eq!(mtester!(1u32), "macro caught literal: 1");
|
||||
assert_eq!(mtester!(0x32), "macro caught literal: 50");
|
||||
assert_eq!(mtester!('c'), "macro caught literal: c");
|
||||
assert_eq!(mtester!(-1.2), "macro caught literal: -1.2");
|
||||
assert_eq!(two_negative_literals!(-2 -3), "macro caught literals: -2, -3");
|
||||
assert_eq!(catch_range!(2 ... 3), "macro caught literal: 2 ... 3");
|
||||
assert_eq!(match_attr!(#[attr] 1), "attr matched literal");
|
||||
assert_eq!(test_user!(10, 20), "literal");
|
||||
assert_eq!(mtester!(false), "macro caught literal: false");
|
||||
assert_eq!(mtester!(true), "macro caught literal: true");
|
||||
match_produced_attr!("a");
|
||||
let _a = LiteralProduced;
|
||||
assert_eq!(pat_match!(1 ... 3), "literal, in range");
|
||||
assert_eq!(pat_match!(4 ... 6), "literal, other");
|
||||
|
||||
// Cases where 'expr' catches
|
||||
assert_eq!(mtester!((-1.2)), "macro caught expr: -1.2");
|
||||
assert_eq!(only_expr!(-1.2), "macro caught expr: -1.2");
|
||||
assert_eq!(mtester!((1 + 3)), "macro caught expr: 4");
|
||||
assert_eq!(mtester_dbg!(()), "macro caught expr: ()");
|
||||
assert_eq!(catch_range!((1 + 1) ... (2 + 2)), "macro caught expr: 2 ... 4");
|
||||
assert_eq!(match_attr!(#[attr] (1 + 2)), "attr matched expr");
|
||||
assert_eq!(test_user!(10, (20 + 2)), "expr");
|
||||
|
||||
match_produced_attr!((3 + 2));
|
||||
let _b = ExprProduced;
|
||||
|
||||
// Cases where 'pat' matched
|
||||
assert_eq!(pat_match!(3), "pat, single");
|
||||
assert_eq!(pat_match!(6), "pat, other");
|
||||
}
|
||||
19
src/test/ui/feature-gate-macro-literal-matcher.rs
Normal file
19
src/test/ui/feature-gate-macro-literal-matcher.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Test that the :lifetime macro fragment cannot be used when macro_lifetime_matcher
|
||||
// feature gate is not used.
|
||||
|
||||
macro_rules! m { ($lt:literal) => {} }
|
||||
//~^ ERROR :literal fragment specifier is experimental and subject to change
|
||||
|
||||
fn main() {
|
||||
m!("some string literal");
|
||||
}
|
||||
11
src/test/ui/feature-gate-macro-literal-matcher.stderr
Normal file
11
src/test/ui/feature-gate-macro-literal-matcher.stderr
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
error[E0658]: :literal fragment specifier is experimental and subject to change (see issue #35625)
|
||||
--> $DIR/feature-gate-macro-literal-matcher.rs:14:19
|
||||
|
|
||||
LL | macro_rules! m { ($lt:literal) => {} }
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= help: add #![feature(macro_literal_matcher)] to the crate attributes to enable
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0658`.
|
||||
|
|
@ -4,7 +4,7 @@ error: invalid fragment specifier `foo`
|
|||
LL | ($x:foo) => ()
|
||||
| ^^^^^^
|
||||
|
|
||||
= help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `path`, `meta`, `tt`, `item` and `vis`
|
||||
= help: valid fragment specifiers are `ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `literal`, `path`, `meta`, `tt`, `item` and `vis`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue