diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs index 3e4c67c4d1ff5..e03e24e606d81 100644 --- a/src/libcore/cell.rs +++ b/src/libcore/cell.rs @@ -482,6 +482,7 @@ mod test { } #[test] + #[allow(experimental)] fn clone_ref_updates_flag() { let x = RefCell::new(0); { diff --git a/src/librustc/middle/cfg/construct.rs b/src/librustc/middle/cfg/construct.rs index acf2442f6c16b..3f7bfefe27e57 100644 --- a/src/librustc/middle/cfg/construct.rs +++ b/src/librustc/middle/cfg/construct.rs @@ -142,6 +142,10 @@ impl<'a> CFGBuilder<'a> { self.pats_all(post.iter().map(|p| *p), vec_exit); self.add_node(pat.id, [post_exit]) } + + ast::PatMac(_) => { + self.tcx.sess.span_bug(pat.span, "unexpanded macro"); + } } } diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index ffc9ee7ec767a..bbea1349c1474 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -392,6 +392,7 @@ fn pat_ctor_id(cx: &MatchCheckCtxt, p: @Pat) -> Option { None => Some(vec(before.len() + after.len())) } } + PatMac(_) => cx.tcx.sess.bug("unexpanded macro"), } } @@ -849,6 +850,10 @@ fn specialize(cx: &MatchCheckCtxt, _ => None } } + PatMac(_) => { + cx.tcx.sess.span_err(pat_span, "unexpanded macro"); + None + } } } } @@ -947,6 +952,7 @@ fn find_refutable(cx: &MatchCheckCtxt, pat: &Pat, spans: &mut Vec) { } PatEnum(_,_) => {} PatVec(..) => { this_pattern!() } + PatMac(_) => cx.tcx.sess.bug("unexpanded macro"), } } diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 5787657d6396f..10357ef3d5677 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -1088,6 +1088,10 @@ impl<'t,TYPER:Typer> MemCategorizationContext<'t,TYPER> { ast::PatLit(_) | ast::PatRange(_, _) => { /*always ok*/ } + + ast::PatMac(_) => { + self.tcx().sess.span_bug(pat.span, "unexpanded macro"); + } } Ok(()) diff --git a/src/librustc/middle/trans/_match.rs b/src/librustc/middle/trans/_match.rs index a10b31e923b19..8df57e7adfbe8 100644 --- a/src/librustc/middle/trans/_match.rs +++ b/src/librustc/middle/trans/_match.rs @@ -2282,6 +2282,9 @@ fn bind_irrefutable_pat<'a>( bcx.sess().span_bug(pat.span, "vector patterns are never irrefutable!"); } + ast::PatMac(..) => { + bcx.sess().span_bug(pat.span, "unexpanded macro"); + } ast::PatWild | ast::PatWildMulti | ast::PatLit(_) | ast::PatRange(_, _) => () } return bcx; diff --git a/src/librustc/middle/trans/debuginfo.rs b/src/librustc/middle/trans/debuginfo.rs index 225c3c48cf406..744200c80e8a4 100644 --- a/src/librustc/middle/trans/debuginfo.rs +++ b/src/librustc/middle/trans/debuginfo.rs @@ -2664,6 +2664,11 @@ fn populate_scope_map(cx: &CrateContext, walk_pattern(cx, sub_pat, scope_stack, scope_map); } } + + ast::PatMac(_) => { + cx.sess().span_bug(pat.span, "debuginfo::populate_scope_map() - \ + Found unexpanded macro."); + } } } diff --git a/src/librustc/middle/typeck/check/_match.rs b/src/librustc/middle/typeck/check/_match.rs index 3d37de38e4523..e223f4001dfd6 100644 --- a/src/librustc/middle/typeck/check/_match.rs +++ b/src/librustc/middle/typeck/check/_match.rs @@ -722,6 +722,8 @@ pub fn check_pat(pcx: &pat_ctxt, pat: &ast::Pat, expected: ty::t) { } fcx.write_ty(pat.id, expected); } + + ast::PatMac(_) => tcx.sess.bug("unexpanded macro"), } } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 33e3e5370e69e..48e390b35fb10 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1731,7 +1731,12 @@ fn name_from_pat(p: &ast::Pat) -> String { PatRange(..) => fail!("tried to get argument name from PatRange, \ which is not allowed in function arguments"), PatVec(..) => fail!("tried to get argument name from pat_vec, \ - which is not allowed in function arguments") + which is not allowed in function arguments"), + PatMac(..) => { + warn!("can't document the name of a function argument \ + produced by a pattern macro"); + "(argument produced by macro)".to_string() + } } } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 69a92a871855c..edeff1229bd91 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -353,7 +353,8 @@ pub enum Pat_ { PatRange(@Expr, @Expr), // [a, b, ..i, y, z] is represented as // PatVec(~[a, b], Some(i), ~[y, z]) - PatVec(Vec<@Pat> , Option<@Pat>, Vec<@Pat> ) + PatVec(Vec<@Pat> , Option<@Pat>, Vec<@Pat> ), + PatMac(Mac), } #[deriving(Clone, Eq, TotalEq, Encodable, Decodable, Hash, Show)] diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs index 3e41e58fbe218..5b61cd45483ac 100644 --- a/src/libsyntax/ast_util.rs +++ b/src/libsyntax/ast_util.rs @@ -665,6 +665,7 @@ pub fn walk_pat(pat: &Pat, it: |&Pat| -> bool) -> bool { slice.iter().advance(|&p| walk_pat(p, |p| it(p))) && after.iter().advance(|&p| walk_pat(p, |p| it(p))) } + PatMac(_) => fail!("attempted to analyze unexpanded pattern"), PatWild | PatWildMulti | PatLit(_) | PatRange(_, _) | PatIdent(_, _, _) | PatEnum(_, _) => { true diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 26c1945db885e..e4c7fbb1debdd 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -114,6 +114,10 @@ pub trait MacResult { fn make_items(&self) -> Option> { None } + /// Create a pattern. + fn make_pat(&self) -> Option<@ast::Pat> { + None + } /// Create a statement. /// @@ -139,6 +143,20 @@ impl MacResult for MacExpr { Some(self.e) } } +/// A convenience type for macros that return a single pattern. +pub struct MacPat { + p: @ast::Pat +} +impl MacPat { + pub fn new(p: @ast::Pat) -> Box { + box MacPat { p: p } as Box + } +} +impl MacResult for MacPat { + fn make_pat(&self) -> Option<@ast::Pat> { + Some(self.p) + } +} /// A convenience type for macros that return a single item. pub struct MacItem { i: @ast::Item @@ -194,12 +212,24 @@ impl DummyResult { span: sp, } } + + /// A plain dummy pattern. + pub fn raw_pat(sp: Span) -> @ast::Pat { + @ast::Pat { + id: ast::DUMMY_NODE_ID, + node: ast::PatWild, + span: sp, + } + } } impl MacResult for DummyResult { fn make_expr(&self) -> Option<@ast::Expr> { Some(DummyResult::raw_expr(self.span)) } + fn make_pat(&self) -> Option<@ast::Pat> { + Some(DummyResult::raw_pat(self.span)) + } fn make_items(&self) -> Option> { if self.expr_only { None diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 0c885c32b76c7..03001acc5d04d 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use ast::{P, Block, Crate, DeclLocal, ExprMac}; +use ast::{P, Block, Crate, DeclLocal, ExprMac, PatMac}; use ast::{Local, Ident, MacInvocTT}; use ast::{ItemMac, Mrk, Stmt, StmtDecl, StmtMac, StmtExpr, StmtSemi}; use ast::TokenTree; @@ -92,8 +92,7 @@ pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr { None => { fld.cx.span_err( pth.span, - format!("non-expr macro in expr pos: \ - {}", + format!("non-expression macro in expression position: {}", extnamestr.get().as_slice() ).as_slice()); return DummyResult::raw_expr(e.span); @@ -487,7 +486,7 @@ pub fn expand_item_mac(it: @ast::Item, fld: &mut MacroExpander) } None => { fld.cx.span_err(pth.span, - format!("expr macro in item position: {}", + format!("non-item macro in item position: {}", extnamestr.get()).as_slice()); return SmallVector::zero(); } @@ -639,7 +638,7 @@ pub fn expand_stmt(s: &Stmt, fld: &mut MacroExpander) -> SmallVector<@Stmt> { Some(stmt) => stmt, None => { fld.cx.span_err(pth.span, - format!("non-stmt macro in stmt pos: {}", + format!("non-statement macro in statement position: {}", extnamestr).as_slice()); return SmallVector::zero(); } @@ -842,6 +841,83 @@ pub fn expand_block_elts(b: &Block, fld: &mut MacroExpander) -> P { }) } +pub fn expand_pat(p: @ast::Pat, fld: &mut MacroExpander) -> @ast::Pat { + let (pth, tts) = match p.node { + PatMac(ref mac) => { + match mac.node { + MacInvocTT(ref pth, ref tts, _) => { + (pth, (*tts).clone()) + } + } + } + _ => return noop_fold_pat(p, fld), + }; + if pth.segments.len() > 1u { + fld.cx.span_err(pth.span, "expected macro name without module separators"); + return DummyResult::raw_pat(p.span); + } + let extname = pth.segments.get(0).identifier; + let extnamestr = token::get_ident(extname); + let marked_after = match fld.extsbox.find(&extname.name) { + None => { + fld.cx.span_err(pth.span, + format!("macro undefined: '{}!'", + extnamestr).as_slice()); + // let compilation continue + return DummyResult::raw_pat(p.span); + } + + Some(&NormalTT(ref expander, span)) => { + fld.cx.bt_push(ExpnInfo { + call_site: p.span, + callee: NameAndSpan { + name: extnamestr.get().to_string(), + format: MacroBang, + span: span + } + }); + + let fm = fresh_mark(); + let marked_before = mark_tts(tts.as_slice(), fm); + let mac_span = original_span(fld.cx); + let expanded = match expander.expand(fld.cx, + mac_span.call_site, + marked_before.as_slice()).make_pat() { + Some(e) => e, + None => { + fld.cx.span_err( + pth.span, + format!( + "non-pattern macro in pattern position: {}", + extnamestr.get() + ).as_slice() + ); + return DummyResult::raw_pat(p.span); + } + }; + + // mark after: + mark_pat(expanded,fm) + } + _ => { + fld.cx.span_err(p.span, + format!("{}! is not legal in pattern position", + extnamestr.get()).as_slice()); + return DummyResult::raw_pat(p.span); + } + }; + + let fully_expanded = + fld.fold_pat(marked_after).node.clone(); + fld.cx.bt_pop(); + + @ast::Pat { + id: ast::DUMMY_NODE_ID, + node: fully_expanded, + span: p.span, + } +} + pub struct IdentRenamer<'a> { renames: &'a mut RenameList, } @@ -885,6 +961,10 @@ impl<'a, 'b> Folder for MacroExpander<'a, 'b> { expand_expr(expr, self) } + fn fold_pat(&mut self, pat: @ast::Pat) -> @ast::Pat { + expand_pat(pat, self) + } + fn fold_item(&mut self, item: @ast::Item) -> SmallVector<@ast::Item> { expand_item(item, self) } @@ -974,6 +1054,11 @@ fn mark_expr(expr: @ast::Expr, m: Mrk) -> @ast::Expr { new_mark_folder(m).fold_expr(expr) } +// apply a given mark to the given pattern. Used following the expansion of a macro. +fn mark_pat(pat: @ast::Pat, m: Mrk) -> @ast::Pat { + new_mark_folder(m).fold_pat(pat) +} + // apply a given mark to the given stmt. Used following the expansion of a macro. fn mark_stmt(expr: &ast::Stmt, m: Mrk) -> @ast::Stmt { new_mark_folder(m).fold_stmt(expr) diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 1bc0c7f7959d0..c69e5f9ba0f11 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -63,6 +63,11 @@ impl<'a> MacResult for ParserAnyMacro<'a> { self.ensure_complete_parse(true); Some(ret) } + fn make_pat(&self) -> Option<@ast::Pat> { + let ret = self.parser.borrow_mut().parse_pat(); + self.ensure_complete_parse(false); + Some(ret) + } fn make_items(&self) -> Option> { let mut ret = SmallVector::zero(); loop { diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 1607820326bb5..b66d1f7eae582 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -770,6 +770,7 @@ pub fn noop_fold_pat(p: @Pat, folder: &mut T) -> @Pat { slice.map(|x| folder.fold_pat(x)), after.iter().map(|x| folder.fold_pat(*x)).collect()) } + PatMac(ref mac) => PatMac(folder.fold_mac(mac)), }; @Pat { diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 6832555f728c0..65ad83d4b4fb1 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2911,15 +2911,28 @@ impl<'a> Parser<'a> { pat = PatRange(start, end); } else if is_plain_ident(&self.token) && !can_be_enum_or_struct { let name = self.parse_path(NoTypesAllowed).path; - let sub; - if self.eat(&token::AT) { - // parse foo @ pat - sub = Some(self.parse_pat()); + if self.eat(&token::NOT) { + // macro invocation + let ket = token::close_delimiter_for(&self.token) + .unwrap_or_else(|| self.fatal("expected open delimiter")); + self.bump(); + + let tts = self.parse_seq_to_end(&ket, + seq_sep_none(), + |p| p.parse_token_tree()); + + let mac = MacInvocTT(name, tts, EMPTY_CTXT); + pat = ast::PatMac(codemap::Spanned {node: mac, span: self.span}); } else { - // or just foo - sub = None; + let sub = if self.eat(&token::AT) { + // parse foo @ pat + Some(self.parse_pat()) + } else { + // or just foo + None + }; + pat = PatIdent(BindByValue(MutImmutable), name, sub); } - pat = PatIdent(BindByValue(MutImmutable), name, sub); } else { // parse an enum pat let enum_path = self.parse_path(LifetimeAndTypesWithColons) diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 3cb2d0b421c2f..3127085ffedca 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1757,6 +1757,7 @@ impl<'a> State<'a> { |s, &p| s.print_pat(p))); try!(word(&mut self.s, "]")); } + ast::PatMac(ref m) => try!(self.print_mac(m)), } self.ann.post(self, NodePat(pat)) } diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index ce10d0db3ba75..eb7aeb0e327a0 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -457,6 +457,7 @@ pub fn walk_pat>(visitor: &mut V, pattern: &Pat, env: E) visitor.visit_pat(*postpattern, env.clone()) } } + PatMac(ref macro) => visitor.visit_mac(macro, env), } } diff --git a/src/test/compile-fail/macro-incomplete-parse.rs b/src/test/compile-fail/macro-incomplete-parse.rs index 43ba2c8a8a5e7..94386858d2949 100644 --- a/src/test/compile-fail/macro-incomplete-parse.rs +++ b/src/test/compile-fail/macro-incomplete-parse.rs @@ -22,8 +22,16 @@ macro_rules! ignored_expr { () => ( 1, 2 ) //~ ERROR macro expansion ignores token `,` } +macro_rules! ignored_pat { + () => ( 1, 2 ) //~ ERROR macro expansion ignores token `,` +} + ignored_item!() fn main() { ignored_expr!() + match 1 { + ignored_pat!() => (), + _ => (), + } } diff --git a/src/test/compile-fail/pattern-macro-hygeine.rs b/src/test/compile-fail/pattern-macro-hygeine.rs new file mode 100644 index 0000000000000..0b6a14c0fc931 --- /dev/null +++ b/src/test/compile-fail/pattern-macro-hygeine.rs @@ -0,0 +1,18 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(macro_rules)] + +macro_rules! foo ( () => ( x ) ) + +fn main() { + let foo!() = 2; + x + 1; //~ ERROR unresolved name `x` +} diff --git a/src/test/run-pass/macro-pat.rs b/src/test/run-pass/macro-pat.rs new file mode 100644 index 0000000000000..6dc663c56205b --- /dev/null +++ b/src/test/run-pass/macro-pat.rs @@ -0,0 +1,75 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(macro_rules)] + +macro_rules! mypat( + () => ( + Some('y') + ) +) + +macro_rules! char_x( + () => ( + 'x' + ) +) + +macro_rules! some( + ($x:pat) => ( + Some($x) + ) +) + +macro_rules! indirect( + () => ( + some!(char_x!()) + ) +) + +macro_rules! ident_pat( + ($x:ident) => ( + $x + ) +) + +fn f(c: Option) -> uint { + match c { + Some('x') => 1, + mypat!() => 2, + _ => 3, + } +} + +pub fn main() { + assert_eq!(1, f(Some('x'))); + assert_eq!(2, f(Some('y'))); + assert_eq!(3, f(None)); + + assert_eq!(1, match Some('x') { + Some(char_x!()) => 1, + _ => 2, + }); + + assert_eq!(1, match Some('x') { + some!(char_x!()) => 1, + _ => 2, + }); + + assert_eq!(1, match Some('x') { + indirect!() => 1, + _ => 2, + }); + + assert_eq!(3, { + let ident_pat!(x) = 2; + x+1 + }); +}