librustc: Always parse macro!()/macro![] as expressions if not
followed by a semicolon.
This allows code like `vec![1i, 2, 3].len();` to work.
This breaks code that uses macros as statements without putting
semicolons after them, such as:
fn main() {
...
assert!(a == b)
assert!(c == d)
println(...);
}
It also breaks code that uses macros as items without semicolons:
local_data_key!(foo)
fn main() {
println("hello world")
}
Add semicolons to fix this code. Those two examples can be fixed as
follows:
fn main() {
...
assert!(a == b);
assert!(c == d);
println(...);
}
local_data_key!(foo);
fn main() {
println("hello world")
}
RFC #378.
Closes #18635.
[breaking-change]
This commit is contained in:
committed by
Jorge Aparicio
parent
c0b2885ee1
commit
ddb2466f6a
@@ -11,7 +11,8 @@ use self::Either::*;
|
||||
|
||||
use ast::{Block, Crate, DeclLocal, ExprMac, PatMac};
|
||||
use ast::{Local, Ident, MacInvocTT};
|
||||
use ast::{ItemMac, Mrk, Stmt, StmtDecl, StmtMac, StmtExpr, StmtSemi};
|
||||
use ast::{ItemMac, MacStmtWithSemicolon, Mrk, Stmt, StmtDecl, StmtMac};
|
||||
use ast::{StmtExpr, StmtSemi};
|
||||
use ast::TokenTree;
|
||||
use ast;
|
||||
use ext::mtwt;
|
||||
@@ -354,7 +355,7 @@ fn expand_loop_block(loop_block: P<Block>,
|
||||
|
||||
// eval $e with a new exts frame.
|
||||
// must be a macro so that $e isn't evaluated too early.
|
||||
macro_rules! with_exts_frame (
|
||||
macro_rules! with_exts_frame {
|
||||
($extsboxexpr:expr,$macros_escape:expr,$e:expr) =>
|
||||
({$extsboxexpr.push_frame();
|
||||
$extsboxexpr.info().macros_escape = $macros_escape;
|
||||
@@ -362,7 +363,7 @@ macro_rules! with_exts_frame (
|
||||
$extsboxexpr.pop_frame();
|
||||
result
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
// When we enter a module, record it, for the sake of `module!`
|
||||
pub fn expand_item(it: P<ast::Item>, fld: &mut MacroExpander)
|
||||
@@ -636,8 +637,8 @@ pub fn expand_item_mac(it: P<ast::Item>, fld: &mut MacroExpander)
|
||||
// I don't understand why this returns a vector... it looks like we're
|
||||
// half done adding machinery to allow macros to expand into multiple statements.
|
||||
fn expand_stmt(s: Stmt, fld: &mut MacroExpander) -> SmallVector<P<Stmt>> {
|
||||
let (mac, semi) = match s.node {
|
||||
StmtMac(mac, semi) => (mac, semi),
|
||||
let (mac, style) = match s.node {
|
||||
StmtMac(mac, style) => (mac, style),
|
||||
_ => return expand_non_macro_stmt(s, fld)
|
||||
};
|
||||
let expanded_stmt = match expand_mac_invoc(mac, s.span,
|
||||
@@ -653,7 +654,7 @@ fn expand_stmt(s: Stmt, fld: &mut MacroExpander) -> SmallVector<P<Stmt>> {
|
||||
let fully_expanded = fld.fold_stmt(expanded_stmt);
|
||||
fld.cx.bt_pop();
|
||||
|
||||
if semi {
|
||||
if style == MacStmtWithSemicolon {
|
||||
fully_expanded.into_iter().map(|s| s.map(|Spanned {node, span}| {
|
||||
Spanned {
|
||||
node: match node {
|
||||
@@ -1324,7 +1325,7 @@ mod test {
|
||||
// make sure that macros can't escape fns
|
||||
#[should_fail]
|
||||
#[test] fn macros_cant_escape_fns_test () {
|
||||
let src = "fn bogus() {macro_rules! z (() => (3+4))}\
|
||||
let src = "fn bogus() {macro_rules! z (() => (3+4));}\
|
||||
fn inty() -> int { z!() }".to_string();
|
||||
let sess = parse::new_parse_sess();
|
||||
let crate_ast = parse::parse_crate_from_source_str(
|
||||
@@ -1338,7 +1339,7 @@ mod test {
|
||||
// make sure that macros can't escape modules
|
||||
#[should_fail]
|
||||
#[test] fn macros_cant_escape_mods_test () {
|
||||
let src = "mod foo {macro_rules! z (() => (3+4))}\
|
||||
let src = "mod foo {macro_rules! z (() => (3+4));}\
|
||||
fn inty() -> int { z!() }".to_string();
|
||||
let sess = parse::new_parse_sess();
|
||||
let crate_ast = parse::parse_crate_from_source_str(
|
||||
@@ -1350,7 +1351,7 @@ mod test {
|
||||
|
||||
// macro_escape modules should allow macros to escape
|
||||
#[test] fn macros_can_escape_flattened_mods_test () {
|
||||
let src = "#[macro_escape] mod foo {macro_rules! z (() => (3+4))}\
|
||||
let src = "#[macro_escape] mod foo {macro_rules! z (() => (3+4));}\
|
||||
fn inty() -> int { z!() }".to_string();
|
||||
let sess = parse::new_parse_sess();
|
||||
let crate_ast = parse::parse_crate_from_source_str(
|
||||
@@ -1402,13 +1403,13 @@ mod test {
|
||||
|
||||
#[test] fn macro_tokens_should_match(){
|
||||
expand_crate_str(
|
||||
"macro_rules! m((a)=>(13)) fn main(){m!(a);}".to_string());
|
||||
"macro_rules! m((a)=>(13)) ;fn main(){m!(a);}".to_string());
|
||||
}
|
||||
|
||||
// should be able to use a bound identifier as a literal in a macro definition:
|
||||
#[test] fn self_macro_parsing(){
|
||||
expand_crate_str(
|
||||
"macro_rules! foo ((zz) => (287u;))
|
||||
"macro_rules! foo ((zz) => (287u;));
|
||||
fn f(zz : int) {foo!(zz);}".to_string()
|
||||
);
|
||||
}
|
||||
@@ -1451,16 +1452,16 @@ mod test {
|
||||
("fn main () {let x: int = 13;x;}",
|
||||
vec!(vec!(0)), false),
|
||||
// the use of b after the + should be renamed, the other one not:
|
||||
("macro_rules! f (($x:ident) => (b + $x)) fn a() -> int { let b = 13; f!(b)}",
|
||||
("macro_rules! f (($x:ident) => (b + $x)); fn a() -> int { let b = 13; f!(b)}",
|
||||
vec!(vec!(1)), false),
|
||||
// the b before the plus should not be renamed (requires marks)
|
||||
("macro_rules! f (($x:ident) => ({let b=9; ($x + b)})) fn a() -> int { f!(b)}",
|
||||
("macro_rules! f (($x:ident) => ({let b=9; ($x + b)})); fn a() -> int { f!(b)}",
|
||||
vec!(vec!(1)), false),
|
||||
// the marks going in and out of letty should cancel, allowing that $x to
|
||||
// capture the one following the semicolon.
|
||||
// this was an awesome test case, and caught a *lot* of bugs.
|
||||
("macro_rules! letty(($x:ident) => (let $x = 15;))
|
||||
macro_rules! user(($x:ident) => ({letty!($x); $x}))
|
||||
("macro_rules! letty(($x:ident) => (let $x = 15;));
|
||||
macro_rules! user(($x:ident) => ({letty!($x); $x}));
|
||||
fn main() -> int {user!(z)}",
|
||||
vec!(vec!(0)), false)
|
||||
);
|
||||
@@ -1488,7 +1489,7 @@ mod test {
|
||||
#[test] fn issue_6994(){
|
||||
run_renaming_test(
|
||||
&("macro_rules! g (($x:ident) =>
|
||||
({macro_rules! f(($y:ident)=>({let $y=3;$x}));f!($x)}))
|
||||
({macro_rules! f(($y:ident)=>({let $y=3;$x}));f!($x)}));
|
||||
fn a(){g!(z)}",
|
||||
vec!(vec!(0)),false),
|
||||
0)
|
||||
@@ -1498,7 +1499,7 @@ mod test {
|
||||
// fn z() {match 8 {x_1 => {match 9 {x_2 | x_2 if x_2 == x_1 => x_2 + x_1}}}}
|
||||
#[test] fn issue_9384(){
|
||||
run_renaming_test(
|
||||
&("macro_rules! bad_macro (($ex:expr) => ({match 9 {x | x if x == $ex => x + $ex}}))
|
||||
&("macro_rules! bad_macro (($ex:expr) => ({match 9 {x | x if x == $ex => x + $ex}}));
|
||||
fn z() {match 8 {x => bad_macro!(x)}}",
|
||||
// NB: the third "binding" is the repeat of the second one.
|
||||
vec!(vec!(1,3),vec!(0,2),vec!(0,2)),
|
||||
@@ -1511,8 +1512,8 @@ mod test {
|
||||
// fn main(){let g1_1 = 13; g1_1}}
|
||||
#[test] fn pat_expand_issue_15221(){
|
||||
run_renaming_test(
|
||||
&("macro_rules! inner ( ($e:pat ) => ($e))
|
||||
macro_rules! outer ( ($e:pat ) => (inner!($e)))
|
||||
&("macro_rules! inner ( ($e:pat ) => ($e));
|
||||
macro_rules! outer ( ($e:pat ) => (inner!($e)));
|
||||
fn main() { let outer!(g) = 13; g;}",
|
||||
vec!(vec!(0)),
|
||||
true),
|
||||
@@ -1527,8 +1528,8 @@ mod test {
|
||||
// method expands to fn get_x(&self_0, x_1:int) {self_0 + self_2 + x_3 + x_1}
|
||||
#[test] fn method_arg_hygiene(){
|
||||
run_renaming_test(
|
||||
&("macro_rules! inject_x (()=>(x))
|
||||
macro_rules! inject_self (()=>(self))
|
||||
&("macro_rules! inject_x (()=>(x));
|
||||
macro_rules! inject_self (()=>(self));
|
||||
struct A;
|
||||
impl A{fn get_x(&self, x: int) {self + inject_self!() + inject_x!() + x;} }",
|
||||
vec!(vec!(0),vec!(3)),
|
||||
@@ -1542,8 +1543,8 @@ mod test {
|
||||
run_renaming_test(
|
||||
&("struct A;
|
||||
macro_rules! add_method (($T:ty) =>
|
||||
(impl $T { fn thingy(&self) {self;} }))
|
||||
add_method!(A)",
|
||||
(impl $T { fn thingy(&self) {self;} }));
|
||||
add_method!(A);",
|
||||
vec!(vec!(0)),
|
||||
true),
|
||||
0)
|
||||
@@ -1553,7 +1554,7 @@ mod test {
|
||||
// expands to fn q(x_1:int){fn g(x_2:int){x_2 + x_1};}
|
||||
#[test] fn issue_9383(){
|
||||
run_renaming_test(
|
||||
&("macro_rules! bad_macro (($ex:expr) => (fn g(x:int){ x + $ex }))
|
||||
&("macro_rules! bad_macro (($ex:expr) => (fn g(x:int){ x + $ex }));
|
||||
fn q(x:int) { bad_macro!(x); }",
|
||||
vec!(vec!(1),vec!(0)),true),
|
||||
0)
|
||||
@@ -1563,7 +1564,7 @@ mod test {
|
||||
// expands to fn f(){(|x_1 : int| {(x_2 + x_1)})(3);}
|
||||
#[test] fn closure_arg_hygiene(){
|
||||
run_renaming_test(
|
||||
&("macro_rules! inject_x (()=>(x))
|
||||
&("macro_rules! inject_x (()=>(x));
|
||||
fn f(){(|x : int| {(inject_x!() + x)})(3);}",
|
||||
vec!(vec!(1)),
|
||||
true),
|
||||
@@ -1573,9 +1574,9 @@ mod test {
|
||||
// macro_rules in method position. Sadly, unimplemented.
|
||||
#[test] fn macro_in_method_posn(){
|
||||
expand_crate_str(
|
||||
"macro_rules! my_method (() => (fn thirteen(&self) -> int {13}))
|
||||
"macro_rules! my_method (() => (fn thirteen(&self) -> int {13}));
|
||||
struct A;
|
||||
impl A{ my_method!()}
|
||||
impl A{ my_method!(); }
|
||||
fn f(){A.thirteen;}".to_string());
|
||||
}
|
||||
|
||||
@@ -1586,7 +1587,7 @@ mod test {
|
||||
&("macro_rules! item { ($i:item) => {$i}}
|
||||
struct Entries;
|
||||
macro_rules! iterator_impl {
|
||||
() => { item!( impl Entries { fn size_hint(&self) { self;}})}}
|
||||
() => { item!( impl Entries { fn size_hint(&self) { self;}});}}
|
||||
iterator_impl! { }",
|
||||
vec!(vec!(0)), true),
|
||||
0)
|
||||
@@ -1666,9 +1667,9 @@ mod test {
|
||||
}
|
||||
|
||||
#[test] fn fmt_in_macro_used_inside_module_macro() {
|
||||
let crate_str = "macro_rules! fmt_wrap(($b:expr)=>($b.to_string()))
|
||||
macro_rules! foo_module (() => (mod generated { fn a() { let xx = 147; fmt_wrap!(xx);}}))
|
||||
foo_module!()
|
||||
let crate_str = "macro_rules! fmt_wrap(($b:expr)=>($b.to_string()));
|
||||
macro_rules! foo_module (() => (mod generated { fn a() { let xx = 147; fmt_wrap!(xx);}}));
|
||||
foo_module!();
|
||||
".to_string();
|
||||
let cr = expand_crate_str(crate_str);
|
||||
// find the xx binding
|
||||
|
||||
Reference in New Issue
Block a user