+
+// Helper methods for building HIR.
+
+fn arm(pats: Vec<P<hir::Pat>>, expr: P<hir::Expr>) -> hir::Arm {
+ hir::Arm {
+ attrs: vec!(),
+ pats: pats,
+ guard: None,
+ body: expr,
+ }
+}
+
+fn expr_break(lctx: &LoweringContext, span: Span) -> P<hir::Expr> {
+ expr(lctx, span, hir::ExprBreak(None))
+}
+
+fn expr_call(lctx: &LoweringContext,
+ span: Span,
+ e: P<hir::Expr>,
+ args: Vec<P<hir::Expr>>)
+ -> P<hir::Expr> {
+ expr(lctx, span, hir::ExprCall(e, args))
+}
+
+fn expr_ident(lctx: &LoweringContext, span: Span, id: Ident) -> P<hir::Expr> {
+ expr_path(lctx, path_ident(span, id))
+}
+
+fn expr_mut_addr_of(lctx: &LoweringContext, span: Span, e: P<hir::Expr>) -> P<hir::Expr> {
+ expr(lctx, span, hir::ExprAddrOf(hir::MutMutable, e))
+}
+
+fn expr_path(lctx: &LoweringContext, path: hir::Path) -> P<hir::Expr> {
+ expr(lctx, path.span, hir::ExprPath(None, path))
+}
+
+fn expr_match(lctx: &LoweringContext,
+ span: Span,
+ arg: P<hir::Expr>,
+ arms: Vec<hir::Arm>,
+ source: hir::MatchSource)
+ -> P<hir::Expr> {
+ expr(lctx,
+ span,
+ hir::ExprMatch(arg, arms, source))
+}
+
+fn expr_block(lctx: &LoweringContext, b: P<hir::Block>) -> P<hir::Expr> {
+ expr(lctx, b.span, hir::ExprBlock(b))
+}
+
+fn expr_tuple(lctx: &LoweringContext, sp: Span, exprs: Vec<P<hir::Expr>>) -> P<hir::Expr> {
+ expr(lctx, sp, hir::ExprTup(exprs))
+}
+
+fn expr(lctx: &LoweringContext, span: Span, node: hir::Expr_) -> P<hir::Expr> {
+ P(hir::Expr {
+ id: lctx.next_id(),
+ node: node,
+ span: span,
+ })
+}
+
+fn stmt_let(lctx: &LoweringContext,
+ sp: Span,
+ mutbl: bool,
+ ident: Ident,
+ ex: P<hir::Expr>)
+ -> P<hir::Stmt> {
+ let pat = if mutbl {
+ pat_ident_binding_mode(lctx, sp, ident, hir::BindByValue(hir::MutMutable))
+ } else {
+ pat_ident(lctx, sp, ident)
+ };
+ let local = P(hir::Local {
+ pat: pat,
+ ty: None,
+ init: Some(ex),
+ id: lctx.next_id(),
+ span: sp,
+ });
+ let decl = respan(sp, hir::DeclLocal(local));
+ P(respan(sp, hir::StmtDecl(P(decl), lctx.next_id())))
+}
+
+fn block_expr(lctx: &LoweringContext, expr: P<hir::Expr>) -> P<hir::Block> {
+ block_all(lctx, expr.span, Vec::new(), Some(expr))
+}
+
+fn block_all(lctx: &LoweringContext,
+ span: Span,
+ stmts: Vec<P<hir::Stmt>>,
+ expr: Option<P<hir::Expr>>)
+ -> P<hir::Block> {
+ P(hir::Block {
+ stmts: stmts,
+ expr: expr,
+ id: lctx.next_id(),
+ rules: hir::DefaultBlock,
+ span: span,
+ })
+}
+
+fn pat_some(lctx: &LoweringContext, span: Span, pat: P<hir::Pat>) -> P<hir::Pat> {
+ let some = std_path(lctx, &["option", "Option", "Some"]);
+ let path = path_global(span, some);
+ pat_enum(lctx, span, path, vec!(pat))
+}
+
+fn pat_none(lctx: &LoweringContext, span: Span) -> P<hir::Pat> {
+ let none = std_path(lctx, &["option", "Option", "None"]);
+ let path = path_global(span, none);
+ pat_enum(lctx, span, path, vec![])
+}
+
+fn pat_enum(lctx: &LoweringContext,
+ span: Span,
+ path: hir::Path,
+ subpats: Vec<P<hir::Pat>>)
+ -> P<hir::Pat> {
+ let pt = hir::PatEnum(path, Some(subpats));
+ pat(lctx, span, pt)
+}
+
+fn pat_ident(lctx: &LoweringContext, span: Span, ident: Ident) -> P<hir::Pat> {
+ pat_ident_binding_mode(lctx, span, ident, hir::BindByValue(hir::MutImmutable))
+}
+
+fn pat_ident_binding_mode(lctx: &LoweringContext,
+ span: Span,
+ ident: Ident,
+ bm: hir::BindingMode)
+ -> P<hir::Pat> {
+ let pat_ident = hir::PatIdent(bm,
+ Spanned {
+ span: span,
+ node: ident,
+ },
+ None);
+ pat(lctx, span, pat_ident)
+}
+
+fn pat_wild(lctx: &LoweringContext, span: Span) -> P<hir::Pat> {
+ pat(lctx, span, hir::PatWild(hir::PatWildSingle))
+}
+
+fn pat(lctx: &LoweringContext, span: Span, pat: hir::Pat_) -> P<hir::Pat> {
+ P(hir::Pat {
+ id: lctx.next_id(),
+ node: pat,
+ span: span,
+ })
+}
+
+fn path_ident(span: Span, id: Ident) -> hir::Path {
+ path(span, vec!(id))
+}
+
+fn path(span: Span, strs: Vec<Ident>) -> hir::Path {
+ path_all(span, false, strs, Vec::new(), Vec::new(), Vec::new())
+}
+
+fn path_global(span: Span, strs: Vec<Ident>) -> hir::Path {
+ path_all(span, true, strs, Vec::new(), Vec::new(), Vec::new())
+}
+
+fn path_all(sp: Span,
+ global: bool,
+ mut idents: Vec<Ident>,
+ lifetimes: Vec<hir::Lifetime>,
+ types: Vec<P<hir::Ty>>,
+ bindings: Vec<P<hir::TypeBinding>>)
+ -> hir::Path {
+ let last_identifier = idents.pop().unwrap();
+ let mut segments: Vec<hir::PathSegment> = idents.into_iter()
+ .map(|ident| {
+ hir::PathSegment {
+ identifier: ident,
+ parameters: hir::PathParameters::none(),
+ }
+ })
+ .collect();
+ segments.push(hir::PathSegment {
+ identifier: last_identifier,
+ parameters: hir::AngleBracketedParameters(hir::AngleBracketedParameterData {
+ lifetimes: lifetimes,
+ types: OwnedSlice::from_vec(types),
+ bindings: OwnedSlice::from_vec(bindings),
+ }),
+ });
+ hir::Path {
+ span: sp,
+ global: global,
+ segments: segments,
+ }
+}
+
+fn std_path(lctx: &LoweringContext, components: &[&str]) -> Vec<Ident> {
+ let mut v = Vec::new();
+ if let Some(s) = lctx.crate_root {
+ v.push(str_to_ident(s));
+ }
+ v.extend(components.iter().map(|s| str_to_ident(s)));
+ return v
+}
+
+// Given suffix ["b","c","d"], returns path `::std::b::c::d` when
+// `fld.cx.use_std`, and `::core::b::c::d` otherwise.
+fn core_path(lctx: &LoweringContext, span: Span, components: &[&str]) -> hir::Path {
+ let idents = std_path(lctx, components);
+ path_global(span, idents)
+}
+
+fn signal_block_expr(lctx: &LoweringContext,
+ stmts: Vec<P<hir::Stmt>>,
+ expr: P<hir::Expr>,
+ span: Span,
+ rule: hir::BlockCheckMode)
+ -> P<hir::Expr> {
+ expr_block(lctx,
+ P(hir::Block {
+ rules: rule,
+ span: span,
+ id: lctx.next_id(),
+ stmts: stmts,
+ expr: Some(expr),
+ }))
+}
+
+
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use syntax::ast::{self, NodeId, NodeIdAssigner};
+ use syntax::{parse, codemap};
+ use syntax::fold::Folder;
+ use std::cell::Cell;
+
+ struct MockAssigner {
+ next_id: Cell<NodeId>,
+ }
+
+ impl MockAssigner {
+ fn new() -> MockAssigner {
+ MockAssigner {
+ next_id: Cell::new(0),
+ }
+ }
+ }
+
+ trait FakeExtCtxt {
+ fn call_site(&self) -> codemap::Span;
+ fn cfg(&self) -> ast::CrateConfig;
+ fn ident_of(&self, st: &str) -> ast::Ident;
+ fn name_of(&self, st: &str) -> ast::Name;
+ fn parse_sess(&self) -> &parse::ParseSess;
+ }
+
+ impl FakeExtCtxt for parse::ParseSess {
+ fn call_site(&self) -> codemap::Span {
+ codemap::Span {
+ lo: codemap::BytePos(0),
+ hi: codemap::BytePos(0),
+ expn_id: codemap::NO_EXPANSION,
+ }
+ }
+ fn cfg(&self) -> ast::CrateConfig { Vec::new() }
+ fn ident_of(&self, st: &str) -> ast::Ident {
+ parse::token::str_to_ident(st)
+ }
+ fn name_of(&self, st: &str) -> ast::Name {
+ parse::token::intern(st)
+ }
+ fn parse_sess(&self) -> &parse::ParseSess { self }
+ }
+
+ impl NodeIdAssigner for MockAssigner {
+ fn next_node_id(&self) -> NodeId {
+ let result = self.next_id.get();
+ self.next_id.set(result + 1);
+ result
+ }
+
+ fn peek_node_id(&self) -> NodeId {
+ self.next_id.get()
+ }
+ }
+
+ impl Folder for MockAssigner {
+ fn new_id(&mut self, old_id: NodeId) -> NodeId {
+ assert_eq!(old_id, ast::DUMMY_NODE_ID);
+ self.next_node_id()
+ }
+ }
+
+ #[test]
+ fn test_preserves_ids() {
+ let cx = parse::ParseSess::new();
+ let mut assigner = MockAssigner::new();
+
+ let ast_if_let = quote_expr!(&cx, if let Some(foo) = baz { bar(foo); });
+ let ast_if_let = assigner.fold_expr(ast_if_let);
+ let ast_while_let = quote_expr!(&cx, while let Some(foo) = baz { bar(foo); });
+ let ast_while_let = assigner.fold_expr(ast_while_let);
+ let ast_for = quote_expr!(&cx, for i in 0..10 { foo(i); });
+ let ast_for = assigner.fold_expr(ast_for);
+ let ast_in = quote_expr!(&cx, in HEAP { foo() });
+ let ast_in = assigner.fold_expr(ast_in);
+
+ let lctx = LoweringContext::new(&assigner, None);
+ let hir1 = lower_expr(&lctx, &ast_if_let);
+ let hir2 = lower_expr(&lctx, &ast_if_let);
+ assert!(hir1 == hir2);
+
+ let hir1 = lower_expr(&lctx, &ast_while_let);
+ let hir2 = lower_expr(&lctx, &ast_while_let);
+ assert!(hir1 == hir2);
+
+ let hir1 = lower_expr(&lctx, &ast_for);
+ let hir2 = lower_expr(&lctx, &ast_for);
+ assert!(hir1 == hir2);
+
+ let hir1 = lower_expr(&lctx, &ast_in);
+ let hir2 = lower_expr(&lctx, &ast_in);
+ assert!(hir1 == hir2);
+ }
+}