about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2014-05-28 14:21:40 -0700
committerbors <bors@rust-lang.org>2014-05-28 14:21:40 -0700
commitdc5ce0a9703451da7ecac062fbc239fe4890a996 (patch)
tree103419947fe6b00e0e0ea57904cff76efe70d316 /src
parente865415c2fe2c0e83fe1955238db082a1374c380 (diff)
parentfd40d0cf5b873ebc94b64fb1abcf2e14fd38dcf7 (diff)
downloadrust-dc5ce0a9703451da7ecac062fbc239fe4890a996.tar.gz
rust-dc5ce0a9703451da7ecac062fbc239fe4890a996.zip
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.
Diffstat (limited to 'src')
-rw-r--r--src/libcore/cell.rs1
-rw-r--r--src/librustc/middle/cfg/construct.rs4
-rw-r--r--src/librustc/middle/check_match.rs6
-rw-r--r--src/librustc/middle/mem_categorization.rs4
-rw-r--r--src/librustc/middle/trans/_match.rs3
-rw-r--r--src/librustc/middle/trans/debuginfo.rs5
-rw-r--r--src/librustc/middle/typeck/check/_match.rs2
-rw-r--r--src/librustdoc/clean/mod.rs7
-rw-r--r--src/libsyntax/ast.rs3
-rw-r--r--src/libsyntax/ast_util.rs1
-rw-r--r--src/libsyntax/ext/base.rs30
-rw-r--r--src/libsyntax/ext/expand.rs95
-rw-r--r--src/libsyntax/ext/tt/macro_rules.rs5
-rw-r--r--src/libsyntax/fold.rs1
-rw-r--r--src/libsyntax/parse/parser.rs27
-rw-r--r--src/libsyntax/print/pprust.rs1
-rw-r--r--src/libsyntax/visit.rs1
-rw-r--r--src/test/compile-fail/macro-incomplete-parse.rs8
-rw-r--r--src/test/compile-fail/pattern-macro-hygeine.rs18
-rw-r--r--src/test/run-pass/macro-pat.rs75
20 files changed, 283 insertions, 14 deletions
diff --git a/src/libcore/cell.rs b/src/libcore/cell.rs
index 3e4c67c4d1f..e03e24e606d 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 acf2442f6c1..3f7bfefe27e 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 ffc9ee7ec76..bbea1349c14 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<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"),
     }
 }
 
diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs
index 5787657d639..10357ef3d56 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 a10b31e923b..8df57e7adfb 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 225c3c48cf4..744200c80e8 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 3d37de38e45..e223f4001df 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 33e3e5370e6..48e390b35fb 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 69a92a87185..edeff1229bd 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 3e41e58fbe2..5b61cd45483 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 26c1945db88..e4c7fbb1deb 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<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
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 0c885c32b76..03001acc5d0 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<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)
diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs
index 1bc0c7f7959..c69e5f9ba0f 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<SmallVector<@ast::Item>> {
         let mut ret = SmallVector::zero();
         loop {
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index 1607820326b..b66d1f7eae5 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -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 {
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 6832555f728..65ad83d4b4f 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 3cb2d0b421c..3127085ffed 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 ce10d0db3ba..eb7aeb0e327 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -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),
     }
 }
 
diff --git a/src/test/compile-fail/macro-incomplete-parse.rs b/src/test/compile-fail/macro-incomplete-parse.rs
index 43ba2c8a8a5..94386858d29 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 00000000000..0b6a14c0fc9
--- /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 <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`
+}
diff --git a/src/test/run-pass/macro-pat.rs b/src/test/run-pass/macro-pat.rs
new file mode 100644
index 00000000000..6dc663c5620
--- /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 <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
+    });
+}