auto merge of #14298 : kmcallister/rust/pattern-macros, r=huonw
First commit is an unrelated fix for a test suite warning I introduced last week.
This commit is contained in:
commit
dc5ce0a970
@ -482,6 +482,7 @@ mod test {
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[allow(experimental)]
|
||||
fn clone_ref_updates_flag() {
|
||||
let x = RefCell::new(0);
|
||||
{
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -392,6 +392,7 @@ fn pat_ctor_id(cx: &MatchCheckCtxt, p: @Pat) -> Option<ctor> {
|
||||
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<Span>) {
|
||||
}
|
||||
PatEnum(_,_) => {}
|
||||
PatVec(..) => { this_pattern!() }
|
||||
PatMac(_) => cx.tcx.sess.bug("unexpanded macro"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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(())
|
||||
|
@ -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;
|
||||
|
@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)]
|
||||
|
@ -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
|
||||
|
@ -114,6 +114,10 @@ pub trait MacResult {
|
||||
fn make_items(&self) -> Option<SmallVector<@ast::Item>> {
|
||||
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<MacResult> {
|
||||
box MacPat { p: p } as Box<MacResult>
|
||||
}
|
||||
}
|
||||
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<SmallVector<@ast::Item>> {
|
||||
if self.expr_only {
|
||||
None
|
||||
|
@ -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<Block> {
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -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<SmallVector<@ast::Item>> {
|
||||
let mut ret = SmallVector::zero();
|
||||
loop {
|
||||
|
@ -770,6 +770,7 @@ pub fn noop_fold_pat<T: Folder>(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 {
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -457,6 +457,7 @@ pub fn walk_pat<E: Clone, V: Visitor<E>>(visitor: &mut V, pattern: &Pat, env: E)
|
||||
visitor.visit_pat(*postpattern, env.clone())
|
||||
}
|
||||
}
|
||||
PatMac(ref macro) => visitor.visit_mac(macro, env),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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!() => (),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
18
src/test/compile-fail/pattern-macro-hygeine.rs
Normal file
18
src/test/compile-fail/pattern-macro-hygeine.rs
Normal file
@ -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 <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_rules)]
|
||||
|
||||
macro_rules! foo ( () => ( x ) )
|
||||
|
||||
fn main() {
|
||||
let foo!() = 2;
|
||||
x + 1; //~ ERROR unresolved name `x`
|
||||
}
|
75
src/test/run-pass/macro-pat.rs
Normal file
75
src/test/run-pass/macro-pat.rs
Normal file
@ -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 <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_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<char>) -> 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
|
||||
});
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user