2014-02-12 06:43:23 +00:00
|
|
|
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
2012-12-04 00:48:01 +00:00
|
|
|
// 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.
|
|
|
|
|
2014-02-06 22:38:33 +00:00
|
|
|
use ast::{P, Block, Crate, DeclLocal, ExprMac};
|
2014-01-09 13:05:33 +00:00
|
|
|
use ast::{Local, Ident, MacInvocTT};
|
|
|
|
use ast::{ItemMac, Mrk, Stmt, StmtDecl, StmtMac, StmtExpr, StmtSemi};
|
2014-02-24 20:47:19 +00:00
|
|
|
use ast::TokenTree;
|
2012-12-23 22:41:37 +00:00
|
|
|
use ast;
|
2014-02-24 20:47:19 +00:00
|
|
|
use ext::mtwt;
|
2013-09-08 12:46:20 +00:00
|
|
|
use ext::build::AstBuilder;
|
2013-02-11 16:36:42 +00:00
|
|
|
use attr;
|
2013-07-19 11:51:37 +00:00
|
|
|
use attr::AttrMetaMethods;
|
2013-02-25 19:11:21 +00:00
|
|
|
use codemap;
|
2013-12-07 02:41:11 +00:00
|
|
|
use codemap::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
|
log: Introduce liblog, the old std::logging
This commit moves all logging out of the standard library into an external
crate. This crate is the new crate which is responsible for all logging macros
and logging implementation. A few reasons for this change are:
* The crate map has always been a bit of a code smell among rust programs. It
has difficulty being loaded on almost all platforms, and it's used almost
exclusively for logging and only logging. Removing the crate map is one of the
end goals of this movement.
* The compiler has a fair bit of special support for logging. It has the
__log_level() expression as well as generating a global word per module
specifying the log level. This is unfairly favoring the built-in logging
system, and is much better done purely in libraries instead of the compiler
itself.
* Initialization of logging is much easier to do if there is no reliance on a
magical crate map being available to set module log levels.
* If the logging library can be written outside of the standard library, there's
no reason that it shouldn't be. It's likely that we're not going to build the
highest quality logging library of all time, so third-party libraries should
be able to provide just as high-quality logging systems as the default one
provided in the rust distribution.
With a migration such as this, the change does not come for free. There are some
subtle changes in the behavior of liblog vs the previous logging macros:
* The core change of this migration is that there is no longer a physical
log-level per module. This concept is still emulated (it is quite useful), but
there is now only a global log level, not a local one. This global log level
is a reflection of the maximum of all log levels specified. The previously
generated logging code looked like:
if specified_level <= __module_log_level() {
println!(...)
}
The newly generated code looks like:
if specified_level <= ::log::LOG_LEVEL {
if ::log::module_enabled(module_path!()) {
println!(...)
}
}
Notably, the first layer of checking is still intended to be "super fast" in
that it's just a load of a global word and a compare. The second layer of
checking is executed to determine if the current module does indeed have
logging turned on.
This means that if any module has a debug log level turned on, all modules
with debug log levels get a little bit slower (they all do more expensive
dynamic checks to determine if they're turned on or not).
Semantically, this migration brings no change in this respect, but
runtime-wise, this will have a perf impact on some code.
* A `RUST_LOG=::help` directive will no longer print out a list of all modules
that can be logged. This is because the crate map will no longer specify the
log levels of all modules, so the list of modules is not known. Additionally,
warnings can no longer be provided if a malformed logging directive was
supplied.
The new "hello world" for logging looks like:
#[phase(syntax, link)]
extern crate log;
fn main() {
debug!("Hello, world!");
}
2014-03-09 06:11:44 +00:00
|
|
|
use crateid::CrateId;
|
2012-09-04 18:37:29 +00:00
|
|
|
use ext::base::*;
|
2012-12-23 22:41:37 +00:00
|
|
|
use fold::*;
|
2013-02-25 19:11:21 +00:00
|
|
|
use parse;
|
2014-01-31 23:26:59 +00:00
|
|
|
use parse::token::{fresh_mark, fresh_name, intern};
|
2013-07-30 00:25:00 +00:00
|
|
|
use parse::token;
|
2013-06-04 21:56:33 +00:00
|
|
|
use visit;
|
2013-06-08 16:21:11 +00:00
|
|
|
use visit::Visitor;
|
2013-11-25 07:08:53 +00:00
|
|
|
use util::small_vector::SmallVector;
|
2011-08-05 20:06:11 +00:00
|
|
|
|
2014-02-12 06:43:23 +00:00
|
|
|
use std::cast;
|
2013-12-25 18:10:33 +00:00
|
|
|
use std::unstable::dynamic_lib::DynamicLibrary;
|
2014-01-18 05:35:18 +00:00
|
|
|
use std::os;
|
2013-05-25 02:35:29 +00:00
|
|
|
|
2013-12-28 03:34:51 +00:00
|
|
|
pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr {
|
2013-08-29 19:10:02 +00:00
|
|
|
match e.node {
|
2013-02-18 06:20:36 +00:00
|
|
|
// expr_mac should really be expr_ext or something; it's the
|
|
|
|
// entry-point for all syntax extensions.
|
2013-09-02 01:45:37 +00:00
|
|
|
ExprMac(ref mac) => {
|
2012-12-04 18:50:00 +00:00
|
|
|
match (*mac).node {
|
2013-07-08 22:55:14 +00:00
|
|
|
// it would almost certainly be cleaner to pass the whole
|
|
|
|
// macro invocation in, rather than pulling it apart and
|
|
|
|
// marking the tts and the ctxt separately. This also goes
|
|
|
|
// for the other three macro invocation chunks of code
|
|
|
|
// in this file.
|
2013-03-04 21:58:31 +00:00
|
|
|
// Token-tree macros:
|
2014-01-25 21:34:26 +00:00
|
|
|
MacInvocTT(ref pth, ref tts, _) => {
|
2014-01-19 08:21:14 +00:00
|
|
|
if pth.segments.len() > 1u {
|
2014-01-17 14:53:10 +00:00
|
|
|
fld.cx.span_err(
|
2013-03-04 21:58:31 +00:00
|
|
|
pth.span,
|
2013-09-28 04:01:58 +00:00
|
|
|
format!("expected macro name without module \
|
2013-03-05 00:50:52 +00:00
|
|
|
separators"));
|
2014-01-17 14:53:10 +00:00
|
|
|
// let compilation continue
|
2014-02-18 16:14:12 +00:00
|
|
|
return MacResult::raw_dummy_expr(e.span);
|
2013-03-04 21:58:31 +00:00
|
|
|
}
|
2014-02-28 20:54:01 +00:00
|
|
|
let extname = pth.segments.get(0).identifier;
|
2014-02-14 05:07:09 +00:00
|
|
|
let extnamestr = token::get_ident(extname);
|
2013-02-27 19:03:21 +00:00
|
|
|
// leaving explicit deref here to highlight unbox op:
|
2014-01-06 02:02:57 +00:00
|
|
|
let marked_after = match fld.extsbox.find(&extname.name) {
|
2013-02-18 06:20:36 +00:00
|
|
|
None => {
|
2014-01-17 14:53:10 +00:00
|
|
|
fld.cx.span_err(
|
2013-02-18 06:20:36 +00:00
|
|
|
pth.span,
|
2014-01-30 23:53:09 +00:00
|
|
|
format!("macro undefined: '{}'",
|
|
|
|
extnamestr.get()));
|
2014-01-17 14:53:10 +00:00
|
|
|
|
|
|
|
// let compilation continue
|
2014-02-18 16:14:12 +00:00
|
|
|
return MacResult::raw_dummy_expr(e.span);
|
2013-02-21 08:16:31 +00:00
|
|
|
}
|
2014-01-06 02:02:57 +00:00
|
|
|
Some(&NormalTT(ref expandfun, exp_span)) => {
|
2013-12-28 00:43:01 +00:00
|
|
|
fld.cx.bt_push(ExpnInfo {
|
2013-08-29 19:10:02 +00:00
|
|
|
call_site: e.span,
|
2013-02-18 06:20:36 +00:00
|
|
|
callee: NameAndSpan {
|
2014-01-30 23:53:09 +00:00
|
|
|
name: extnamestr.get().to_str(),
|
2013-12-07 02:41:11 +00:00
|
|
|
format: MacroBang,
|
2013-05-28 21:53:38 +00:00
|
|
|
span: exp_span,
|
2013-02-18 06:20:36 +00:00
|
|
|
},
|
2013-07-02 09:31:00 +00:00
|
|
|
});
|
2013-06-25 18:43:52 +00:00
|
|
|
let fm = fresh_mark();
|
2013-06-29 12:59:08 +00:00
|
|
|
// mark before:
|
2014-02-28 20:54:01 +00:00
|
|
|
let marked_before = mark_tts(tts.as_slice(), fm);
|
2013-09-14 03:54:52 +00:00
|
|
|
|
|
|
|
// The span that we pass to the expanders we want to
|
|
|
|
// be the root of the call stack. That's the most
|
|
|
|
// relevant span and it's the actual invocation of
|
|
|
|
// the macro.
|
2013-12-28 00:43:01 +00:00
|
|
|
let mac_span = original_span(fld.cx);
|
2013-09-14 03:54:52 +00:00
|
|
|
|
2014-01-06 02:02:57 +00:00
|
|
|
let expanded = match expandfun.expand(fld.cx,
|
|
|
|
mac_span.call_site,
|
2014-02-28 20:54:01 +00:00
|
|
|
marked_before.as_slice()) {
|
2014-01-06 02:02:57 +00:00
|
|
|
MRExpr(e) => e,
|
|
|
|
MRAny(any_macro) => any_macro.make_expr(),
|
|
|
|
_ => {
|
2014-01-17 14:53:10 +00:00
|
|
|
fld.cx.span_err(
|
2014-01-06 02:02:57 +00:00
|
|
|
pth.span,
|
|
|
|
format!(
|
|
|
|
"non-expr macro in expr pos: {}",
|
2014-01-30 23:53:09 +00:00
|
|
|
extnamestr.get()
|
2013-02-18 06:20:36 +00:00
|
|
|
)
|
2014-01-17 14:53:10 +00:00
|
|
|
);
|
2014-02-18 16:14:12 +00:00
|
|
|
return MacResult::raw_dummy_expr(e.span);
|
2014-01-06 02:02:57 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-06-29 12:59:08 +00:00
|
|
|
// mark after:
|
2014-01-06 02:02:57 +00:00
|
|
|
mark_expr(expanded,fm)
|
2013-02-18 06:20:36 +00:00
|
|
|
}
|
|
|
|
_ => {
|
2014-01-17 14:53:10 +00:00
|
|
|
fld.cx.span_err(
|
2013-02-18 06:20:36 +00:00
|
|
|
pth.span,
|
2014-01-30 23:53:09 +00:00
|
|
|
format!("'{}' is not a tt-style macro",
|
|
|
|
extnamestr.get())
|
2014-01-17 14:53:10 +00:00
|
|
|
);
|
2014-02-18 16:14:12 +00:00
|
|
|
return MacResult::raw_dummy_expr(e.span);
|
2013-02-18 06:20:36 +00:00
|
|
|
}
|
2014-01-06 02:02:57 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Keep going, outside-in.
|
|
|
|
//
|
2014-01-26 08:43:42 +00:00
|
|
|
// FIXME(pcwalton): Is it necessary to clone the
|
2014-01-06 02:02:57 +00:00
|
|
|
// node here?
|
|
|
|
let fully_expanded =
|
|
|
|
fld.fold_expr(marked_after).node.clone();
|
|
|
|
fld.cx.bt_pop();
|
|
|
|
|
|
|
|
@ast::Expr {
|
|
|
|
id: ast::DUMMY_NODE_ID,
|
|
|
|
node: fully_expanded,
|
|
|
|
span: e.span,
|
2013-02-18 06:20:36 +00:00
|
|
|
}
|
2011-07-06 22:22:23 +00:00
|
|
|
}
|
|
|
|
}
|
2013-02-18 06:20:36 +00:00
|
|
|
}
|
2013-07-30 00:25:00 +00:00
|
|
|
|
|
|
|
// Desugar expr_for_loop
|
2013-09-08 12:46:20 +00:00
|
|
|
// From: `['<ident>:] for <src_pat> in <src_expr> <src_loop_block>`
|
2014-02-06 22:38:33 +00:00
|
|
|
// FIXME #6993: change type of opt_ident to Option<Name>
|
2013-11-30 22:00:39 +00:00
|
|
|
ast::ExprForLoop(src_pat, src_expr, src_loop_block, opt_ident) => {
|
2013-07-30 00:25:00 +00:00
|
|
|
// Expand any interior macros etc.
|
|
|
|
// NB: we don't fold pats yet. Curious.
|
2013-08-29 19:10:02 +00:00
|
|
|
let src_expr = fld.fold_expr(src_expr).clone();
|
2014-02-24 20:47:19 +00:00
|
|
|
let (src_loop_block, opt_ident) = expand_loop_block(src_loop_block, opt_ident, fld);
|
2013-08-29 19:10:02 +00:00
|
|
|
|
|
|
|
let span = e.span;
|
|
|
|
|
2013-07-30 00:25:00 +00:00
|
|
|
// to:
|
|
|
|
//
|
2014-01-17 13:30:06 +00:00
|
|
|
// match &mut <src_expr> {
|
|
|
|
// i => {
|
|
|
|
// ['<ident>:] loop {
|
|
|
|
// match i.next() {
|
2013-07-30 00:25:00 +00:00
|
|
|
// None => break,
|
|
|
|
// Some(<src_pat>) => <src_loop_block>
|
2014-01-17 13:30:06 +00:00
|
|
|
// }
|
2013-07-30 00:25:00 +00:00
|
|
|
// }
|
2014-01-17 13:30:06 +00:00
|
|
|
// }
|
2013-07-30 00:25:00 +00:00
|
|
|
// }
|
|
|
|
|
|
|
|
let local_ident = token::gensym_ident("i");
|
2013-12-28 00:43:01 +00:00
|
|
|
let next_ident = fld.cx.ident_of("next");
|
|
|
|
let none_ident = fld.cx.ident_of("None");
|
2013-07-30 00:25:00 +00:00
|
|
|
|
2013-12-28 00:43:01 +00:00
|
|
|
let local_path = fld.cx.path_ident(span, local_ident);
|
|
|
|
let some_path = fld.cx.path_ident(span, fld.cx.ident_of("Some"));
|
2013-07-30 00:25:00 +00:00
|
|
|
|
2013-09-08 12:46:20 +00:00
|
|
|
// `None => break ['<ident>];`
|
2013-07-30 00:25:00 +00:00
|
|
|
let none_arm = {
|
2014-02-15 08:54:32 +00:00
|
|
|
let break_expr = fld.cx.expr(span, ast::ExprBreak(opt_ident));
|
2013-12-28 00:43:01 +00:00
|
|
|
let none_pat = fld.cx.pat_ident(span, none_ident);
|
2014-02-28 21:09:09 +00:00
|
|
|
fld.cx.arm(span, vec!(none_pat), break_expr)
|
2013-07-30 00:25:00 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// `Some(<src_pat>) => <src_loop_block>`
|
2013-09-08 12:46:20 +00:00
|
|
|
let some_arm =
|
2013-12-28 00:43:01 +00:00
|
|
|
fld.cx.arm(span,
|
2014-02-28 21:09:09 +00:00
|
|
|
vec!(fld.cx.pat_enum(span, some_path, vec!(src_pat))),
|
2013-12-28 00:43:01 +00:00
|
|
|
fld.cx.expr_block(src_loop_block));
|
2013-07-30 00:25:00 +00:00
|
|
|
|
|
|
|
// `match i.next() { ... }`
|
2013-09-08 12:46:20 +00:00
|
|
|
let match_expr = {
|
|
|
|
let next_call_expr =
|
2014-02-28 20:54:01 +00:00
|
|
|
fld.cx.expr_method_call(span,
|
|
|
|
fld.cx.expr_path(local_path),
|
|
|
|
next_ident,
|
|
|
|
Vec::new());
|
2013-07-30 00:25:00 +00:00
|
|
|
|
2014-02-28 21:09:09 +00:00
|
|
|
fld.cx.expr_match(span, next_call_expr, vec!(none_arm, some_arm))
|
2013-07-30 00:25:00 +00:00
|
|
|
};
|
|
|
|
|
2013-09-08 12:46:20 +00:00
|
|
|
// ['ident:] loop { ... }
|
2013-12-28 00:43:01 +00:00
|
|
|
let loop_expr = fld.cx.expr(span,
|
|
|
|
ast::ExprLoop(fld.cx.block_expr(match_expr),
|
|
|
|
opt_ident));
|
2013-09-08 12:46:20 +00:00
|
|
|
|
2014-01-17 13:30:06 +00:00
|
|
|
// `i => loop { ... }`
|
2013-09-08 12:46:20 +00:00
|
|
|
|
2014-01-17 13:30:06 +00:00
|
|
|
// `match &mut <src_expr> { i => loop { ... } }`
|
|
|
|
let discrim = fld.cx.expr_mut_addr_of(span, src_expr);
|
|
|
|
let i_pattern = fld.cx.pat_ident(span, local_ident);
|
2014-02-28 21:09:09 +00:00
|
|
|
let arm = fld.cx.arm(span, vec!(i_pattern), loop_expr);
|
|
|
|
fld.cx.expr_match(span, discrim, vec!(arm))
|
2013-07-30 00:25:00 +00:00
|
|
|
}
|
|
|
|
|
2014-02-15 08:54:32 +00:00
|
|
|
ast::ExprLoop(loop_block, opt_ident) => {
|
2014-02-24 20:47:19 +00:00
|
|
|
let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld);
|
2014-02-15 08:54:32 +00:00
|
|
|
fld.cx.expr(e.span, ast::ExprLoop(loop_block, opt_ident))
|
|
|
|
}
|
|
|
|
|
2013-08-29 19:10:02 +00:00
|
|
|
_ => noop_fold_expr(e, fld)
|
2013-02-18 06:20:36 +00:00
|
|
|
}
|
2011-07-06 22:22:23 +00:00
|
|
|
}
|
|
|
|
|
2014-02-24 20:47:19 +00:00
|
|
|
// Rename loop label and expand its loop body
|
|
|
|
//
|
|
|
|
// The renaming procedure for loop is different in the sense that the loop
|
|
|
|
// body is in a block enclosed by loop head so the renaming of loop label
|
|
|
|
// must be propagated to the enclosed context.
|
|
|
|
fn expand_loop_block(loop_block: P<Block>,
|
|
|
|
opt_ident: Option<Ident>,
|
|
|
|
fld: &mut MacroExpander) -> (P<Block>, Option<Ident>) {
|
2014-02-15 08:54:32 +00:00
|
|
|
match opt_ident {
|
|
|
|
Some(label) => {
|
|
|
|
let new_label = fresh_name(&label);
|
|
|
|
let rename = (label, new_label);
|
2014-02-24 20:47:19 +00:00
|
|
|
|
|
|
|
// The rename *must not* be added to the pending list of current
|
|
|
|
// syntax context otherwise an unrelated `break` or `continue` in
|
|
|
|
// the same context will pick that up in the deferred renaming pass
|
|
|
|
// and be renamed incorrectly.
|
|
|
|
let mut rename_list = vec!(rename);
|
|
|
|
let mut rename_fld = renames_to_fold(&mut rename_list);
|
|
|
|
let renamed_ident = rename_fld.fold_ident(label);
|
|
|
|
|
|
|
|
// The rename *must* be added to the enclosed syntax context for
|
|
|
|
// `break` or `continue` to pick up because by definition they are
|
|
|
|
// in a block enclosed by loop head.
|
|
|
|
fld.extsbox.push_frame();
|
2014-02-15 08:54:32 +00:00
|
|
|
fld.extsbox.info().pending_renames.push(rename);
|
2014-02-24 20:47:19 +00:00
|
|
|
let expanded_block = expand_block_elts(loop_block, fld);
|
|
|
|
fld.extsbox.pop_frame();
|
|
|
|
|
|
|
|
(expanded_block, Some(renamed_ident))
|
2014-02-15 08:54:32 +00:00
|
|
|
}
|
2014-02-24 20:47:19 +00:00
|
|
|
None => (fld.fold_block(loop_block), opt_ident)
|
2014-02-15 08:54:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-26 18:15:29 +00:00
|
|
|
// eval $e with a new exts frame:
|
|
|
|
macro_rules! with_exts_frame (
|
2013-05-08 22:27:29 +00:00
|
|
|
($extsboxexpr:expr,$macros_escape:expr,$e:expr) =>
|
2013-12-31 01:45:39 +00:00
|
|
|
({$extsboxexpr.push_frame();
|
|
|
|
$extsboxexpr.info().macros_escape = $macros_escape;
|
2013-02-26 18:15:29 +00:00
|
|
|
let result = $e;
|
2013-12-31 01:45:39 +00:00
|
|
|
$extsboxexpr.pop_frame();
|
2013-02-26 18:15:29 +00:00
|
|
|
result
|
|
|
|
})
|
|
|
|
)
|
|
|
|
|
2012-07-27 18:19:21 +00:00
|
|
|
// When we enter a module, record it, for the sake of `module!`
|
2014-01-09 13:05:33 +00:00
|
|
|
pub fn expand_item(it: @ast::Item, fld: &mut MacroExpander)
|
|
|
|
-> SmallVector<@ast::Item> {
|
2014-02-28 07:49:25 +00:00
|
|
|
let it = expand_item_modifiers(it, fld);
|
|
|
|
|
|
|
|
let mut decorator_items = SmallVector::zero();
|
2014-03-16 23:04:29 +00:00
|
|
|
for attr in it.attrs.iter().rev() {
|
2014-02-14 06:57:43 +00:00
|
|
|
let mname = attr.name();
|
|
|
|
|
|
|
|
match fld.extsbox.find(&intern(mname.get())) {
|
|
|
|
Some(&ItemDecorator(dec_fn)) => {
|
|
|
|
fld.cx.bt_push(ExpnInfo {
|
|
|
|
call_site: attr.span,
|
|
|
|
callee: NameAndSpan {
|
|
|
|
name: mname.get().to_str(),
|
|
|
|
format: MacroAttribute,
|
|
|
|
span: None
|
|
|
|
}
|
|
|
|
});
|
2014-03-03 18:12:40 +00:00
|
|
|
|
2014-02-14 06:57:43 +00:00
|
|
|
// we'd ideally decorator_items.push_all(expand_item(item, fld)),
|
|
|
|
// but that double-mut-borrows fld
|
2014-03-03 18:12:40 +00:00
|
|
|
let mut items: SmallVector<@ast::Item> = SmallVector::zero();
|
2014-02-14 06:57:43 +00:00
|
|
|
dec_fn(fld.cx, attr.span, attr.node.value, it,
|
2014-03-03 18:12:40 +00:00
|
|
|
|item| items.push(item));
|
2014-03-20 13:12:56 +00:00
|
|
|
decorator_items.extend(items.move_iter()
|
2014-03-03 18:12:40 +00:00
|
|
|
.flat_map(|item| expand_item(item, fld).move_iter()));
|
|
|
|
|
2014-02-14 06:57:43 +00:00
|
|
|
fld.cx.bt_pop();
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut new_items = match it.node {
|
2014-01-09 13:05:33 +00:00
|
|
|
ast::ItemMac(..) => expand_item_mac(it, fld),
|
|
|
|
ast::ItemMod(_) | ast::ItemForeignMod(_) => {
|
2013-12-28 00:43:01 +00:00
|
|
|
fld.cx.mod_push(it.ident);
|
2014-02-28 20:54:01 +00:00
|
|
|
let macro_escape = contains_macro_escape(it.attrs.as_slice());
|
2013-12-28 00:43:01 +00:00
|
|
|
let result = with_exts_frame!(fld.extsbox,
|
2013-08-29 19:10:02 +00:00
|
|
|
macro_escape,
|
|
|
|
noop_fold_item(it, fld));
|
2013-12-28 00:43:01 +00:00
|
|
|
fld.cx.mod_pop();
|
2013-07-26 16:57:30 +00:00
|
|
|
result
|
|
|
|
},
|
2013-08-29 19:10:02 +00:00
|
|
|
_ => noop_fold_item(it, fld)
|
2014-02-14 06:57:43 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
new_items.push_all(decorator_items);
|
|
|
|
new_items
|
2012-05-18 17:00:49 +00:00
|
|
|
}
|
|
|
|
|
2014-02-28 07:49:25 +00:00
|
|
|
fn expand_item_modifiers(mut it: @ast::Item, fld: &mut MacroExpander)
|
|
|
|
-> @ast::Item {
|
|
|
|
let (modifiers, attrs) = it.attrs.partitioned(|attr| {
|
|
|
|
match fld.extsbox.find(&intern(attr.name().get())) {
|
|
|
|
Some(&ItemModifier(_)) => true,
|
|
|
|
_ => false
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
it = @ast::Item {
|
|
|
|
attrs: attrs,
|
|
|
|
..(*it).clone()
|
|
|
|
};
|
|
|
|
|
|
|
|
if modifiers.is_empty() {
|
|
|
|
return it;
|
|
|
|
}
|
|
|
|
|
|
|
|
for attr in modifiers.iter() {
|
|
|
|
let mname = attr.name();
|
|
|
|
|
|
|
|
match fld.extsbox.find(&intern(mname.get())) {
|
|
|
|
Some(&ItemModifier(dec_fn)) => {
|
|
|
|
fld.cx.bt_push(ExpnInfo {
|
|
|
|
call_site: attr.span,
|
|
|
|
callee: NameAndSpan {
|
|
|
|
name: mname.get().to_str(),
|
|
|
|
format: MacroAttribute,
|
|
|
|
span: None,
|
|
|
|
}
|
|
|
|
});
|
|
|
|
it = dec_fn(fld.cx, attr.span, attr.node.value, it);
|
|
|
|
fld.cx.bt_pop();
|
|
|
|
}
|
|
|
|
_ => unreachable!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// expansion may have added new ItemModifiers
|
|
|
|
expand_item_modifiers(it, fld)
|
|
|
|
}
|
|
|
|
|
2013-02-26 18:15:29 +00:00
|
|
|
// does this attribute list contain "macro_escape" ?
|
2013-07-19 11:51:37 +00:00
|
|
|
pub fn contains_macro_escape(attrs: &[ast::Attribute]) -> bool {
|
|
|
|
attr::contains_name(attrs, "macro_escape")
|
2013-02-26 18:15:29 +00:00
|
|
|
}
|
|
|
|
|
2012-07-27 18:19:21 +00:00
|
|
|
// Support for item-position macro invocations, exactly the same
|
|
|
|
// logic as for expression-position macro invocations.
|
2014-01-09 13:05:33 +00:00
|
|
|
pub fn expand_item_mac(it: @ast::Item, fld: &mut MacroExpander)
|
|
|
|
-> SmallVector<@ast::Item> {
|
2014-01-25 21:34:26 +00:00
|
|
|
let (pth, tts) = match it.node {
|
2014-01-09 13:05:33 +00:00
|
|
|
ItemMac(codemap::Spanned {
|
2014-01-25 21:34:26 +00:00
|
|
|
node: MacInvocTT(ref pth, ref tts, _),
|
2013-11-28 20:22:53 +00:00
|
|
|
..
|
2013-08-29 19:10:02 +00:00
|
|
|
}) => {
|
2014-01-25 21:34:26 +00:00
|
|
|
(pth, (*tts).clone())
|
2012-12-27 19:36:00 +00:00
|
|
|
}
|
2013-12-28 00:43:01 +00:00
|
|
|
_ => fld.cx.span_bug(it.span, "invalid item macro invocation")
|
2012-12-12 20:25:40 +00:00
|
|
|
};
|
2012-11-17 12:41:36 +00:00
|
|
|
|
2014-02-28 20:54:01 +00:00
|
|
|
let extname = pth.segments.get(0).identifier;
|
2014-02-14 05:07:09 +00:00
|
|
|
let extnamestr = token::get_ident(extname);
|
2013-06-29 12:59:08 +00:00
|
|
|
let fm = fresh_mark();
|
2013-12-31 01:45:39 +00:00
|
|
|
let expanded = match fld.extsbox.find(&extname.name) {
|
2014-01-17 14:53:10 +00:00
|
|
|
None => {
|
|
|
|
fld.cx.span_err(pth.span,
|
2014-01-30 23:53:09 +00:00
|
|
|
format!("macro undefined: '{}!'",
|
2014-02-14 05:07:09 +00:00
|
|
|
extnamestr));
|
2014-01-17 14:53:10 +00:00
|
|
|
// let compilation continue
|
|
|
|
return SmallVector::zero();
|
|
|
|
}
|
2012-11-17 12:41:36 +00:00
|
|
|
|
2014-01-06 02:02:57 +00:00
|
|
|
Some(&NormalTT(ref expander, span)) => {
|
2013-07-03 18:34:01 +00:00
|
|
|
if it.ident.name != parse::token::special_idents::invalid.name {
|
2014-01-17 14:53:10 +00:00
|
|
|
fld.cx.span_err(pth.span,
|
|
|
|
format!("macro {}! expects no ident argument, \
|
2014-01-30 23:53:09 +00:00
|
|
|
given '{}'",
|
2014-02-14 05:07:09 +00:00
|
|
|
extnamestr,
|
|
|
|
token::get_ident(it.ident)));
|
2014-01-17 14:53:10 +00:00
|
|
|
return SmallVector::zero();
|
2012-11-09 04:12:45 +00:00
|
|
|
}
|
2013-12-28 00:43:01 +00:00
|
|
|
fld.cx.bt_push(ExpnInfo {
|
2013-02-21 08:16:31 +00:00
|
|
|
call_site: it.span,
|
|
|
|
callee: NameAndSpan {
|
2014-01-30 23:53:09 +00:00
|
|
|
name: extnamestr.get().to_str(),
|
2013-12-07 02:41:11 +00:00
|
|
|
format: MacroBang,
|
2013-05-28 21:53:38 +00:00
|
|
|
span: span
|
2013-02-21 08:16:31 +00:00
|
|
|
}
|
2013-07-02 09:31:00 +00:00
|
|
|
});
|
2013-06-29 12:59:08 +00:00
|
|
|
// mark before expansion:
|
2014-02-28 20:54:01 +00:00
|
|
|
let marked_before = mark_tts(tts.as_slice(), fm);
|
|
|
|
expander.expand(fld.cx, it.span, marked_before.as_slice())
|
2012-11-17 12:41:36 +00:00
|
|
|
}
|
2014-01-06 02:02:57 +00:00
|
|
|
Some(&IdentTT(ref expander, span)) => {
|
2013-07-03 18:34:01 +00:00
|
|
|
if it.ident.name == parse::token::special_idents::invalid.name {
|
2014-01-17 14:53:10 +00:00
|
|
|
fld.cx.span_err(pth.span,
|
2014-01-30 23:53:09 +00:00
|
|
|
format!("macro {}! expects an ident argument",
|
|
|
|
extnamestr.get()));
|
2014-01-17 14:53:10 +00:00
|
|
|
return SmallVector::zero();
|
2012-11-09 04:12:45 +00:00
|
|
|
}
|
2013-12-28 00:43:01 +00:00
|
|
|
fld.cx.bt_push(ExpnInfo {
|
2013-02-21 08:16:31 +00:00
|
|
|
call_site: it.span,
|
|
|
|
callee: NameAndSpan {
|
2014-01-30 23:53:09 +00:00
|
|
|
name: extnamestr.get().to_str(),
|
2013-12-07 02:41:11 +00:00
|
|
|
format: MacroBang,
|
2013-05-28 21:53:38 +00:00
|
|
|
span: span
|
2013-02-21 08:16:31 +00:00
|
|
|
}
|
2013-07-02 09:31:00 +00:00
|
|
|
});
|
2013-06-29 12:59:08 +00:00
|
|
|
// mark before expansion:
|
2014-02-28 20:54:01 +00:00
|
|
|
let marked_tts = mark_tts(tts.as_slice(), fm);
|
2014-01-25 21:34:26 +00:00
|
|
|
expander.expand(fld.cx, it.span, it.ident, marked_tts)
|
2012-11-17 12:41:36 +00:00
|
|
|
}
|
2014-01-17 14:53:10 +00:00
|
|
|
_ => {
|
2014-01-30 23:53:09 +00:00
|
|
|
fld.cx.span_err(it.span,
|
|
|
|
format!("{}! is not legal in item position",
|
|
|
|
extnamestr.get()));
|
2014-01-17 14:53:10 +00:00
|
|
|
return SmallVector::zero();
|
|
|
|
}
|
2012-11-17 12:41:36 +00:00
|
|
|
};
|
2012-11-09 04:12:45 +00:00
|
|
|
|
2013-11-25 07:08:53 +00:00
|
|
|
let items = match expanded {
|
2013-09-11 19:52:17 +00:00
|
|
|
MRItem(it) => {
|
2013-11-25 07:08:53 +00:00
|
|
|
mark_item(it,fm).move_iter()
|
|
|
|
.flat_map(|i| fld.fold_item(i).move_iter())
|
|
|
|
.collect()
|
2013-09-11 19:52:17 +00:00
|
|
|
}
|
|
|
|
MRExpr(_) => {
|
2014-01-30 23:53:09 +00:00
|
|
|
fld.cx.span_err(pth.span,
|
|
|
|
format!("expr macro in item position: {}",
|
|
|
|
extnamestr.get()));
|
2014-01-17 14:53:10 +00:00
|
|
|
return SmallVector::zero();
|
2013-09-11 19:52:17 +00:00
|
|
|
}
|
2013-08-30 21:40:05 +00:00
|
|
|
MRAny(any_macro) => {
|
2013-11-25 07:08:53 +00:00
|
|
|
any_macro.make_items().move_iter()
|
|
|
|
.flat_map(|i| mark_item(i, fm).move_iter())
|
|
|
|
.flat_map(|i| fld.fold_item(i).move_iter())
|
|
|
|
.collect()
|
2013-09-11 19:52:17 +00:00
|
|
|
}
|
2014-01-06 02:02:57 +00:00
|
|
|
MRDef(MacroDef { name, ext }) => {
|
2013-06-29 12:59:08 +00:00
|
|
|
// yikes... no idea how to apply the mark to this. I'm afraid
|
|
|
|
// we're going to have to wait-and-see on this one.
|
2014-01-06 02:02:57 +00:00
|
|
|
fld.extsbox.insert(intern(name), ext);
|
2014-02-28 20:54:01 +00:00
|
|
|
if attr::contains_name(it.attrs.as_slice(), "macro_export") {
|
2013-12-25 18:10:33 +00:00
|
|
|
SmallVector::one(it)
|
|
|
|
} else {
|
|
|
|
SmallVector::zero()
|
|
|
|
}
|
2012-11-17 12:41:36 +00:00
|
|
|
}
|
|
|
|
};
|
2013-12-28 00:43:01 +00:00
|
|
|
fld.cx.bt_pop();
|
2013-11-25 07:08:53 +00:00
|
|
|
return items;
|
2012-07-05 19:10:33 +00:00
|
|
|
}
|
|
|
|
|
2013-12-25 18:10:33 +00:00
|
|
|
// load macros from syntax-phase crates
|
|
|
|
pub fn expand_view_item(vi: &ast::ViewItem,
|
|
|
|
fld: &mut MacroExpander)
|
|
|
|
-> ast::ViewItem {
|
2014-02-11 03:45:28 +00:00
|
|
|
match vi.node {
|
2014-03-07 07:57:45 +00:00
|
|
|
ast::ViewItemExternCrate(..) => {
|
2014-02-11 03:45:28 +00:00
|
|
|
let should_load = vi.attrs.iter().any(|attr| {
|
|
|
|
attr.name().get() == "phase" &&
|
|
|
|
attr.meta_item_list().map_or(false, |phases| {
|
|
|
|
attr::contains_name(phases, "syntax")
|
|
|
|
})
|
|
|
|
});
|
2013-12-25 18:10:33 +00:00
|
|
|
|
2014-02-11 03:45:28 +00:00
|
|
|
if should_load {
|
|
|
|
load_extern_macros(vi, fld);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ast::ViewItemUse(_) => {}
|
2013-12-25 18:10:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
noop_fold_view_item(vi, fld)
|
|
|
|
}
|
|
|
|
|
2014-02-05 21:15:24 +00:00
|
|
|
fn load_extern_macros(krate: &ast::ViewItem, fld: &mut MacroExpander) {
|
2014-04-07 19:16:43 +00:00
|
|
|
let MacroCrate { lib, macros, registrar_symbol } =
|
|
|
|
fld.cx.ecfg.loader.load_crate(krate);
|
2013-12-25 18:10:33 +00:00
|
|
|
|
2014-02-05 21:15:24 +00:00
|
|
|
let crate_name = match krate.node {
|
2014-03-07 07:57:45 +00:00
|
|
|
ast::ViewItemExternCrate(name, _, _) => name,
|
2014-02-14 05:07:09 +00:00
|
|
|
_ => unreachable!()
|
2014-01-22 07:09:53 +00:00
|
|
|
};
|
2014-02-14 05:07:09 +00:00
|
|
|
let name = format!("<{} macros>", token::get_ident(crate_name));
|
2014-01-22 07:09:53 +00:00
|
|
|
|
2014-04-07 19:16:43 +00:00
|
|
|
for source in macros.iter() {
|
2014-01-21 18:08:10 +00:00
|
|
|
let item = parse::parse_item_from_source_str(name.clone(),
|
|
|
|
(*source).clone(),
|
2014-01-22 07:09:53 +00:00
|
|
|
fld.cx.cfg(),
|
|
|
|
fld.cx.parse_sess())
|
|
|
|
.expect("expected a serialized item");
|
|
|
|
expand_item_mac(item, fld);
|
2013-12-25 18:10:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let path = match lib {
|
|
|
|
Some(path) => path,
|
|
|
|
None => return
|
|
|
|
};
|
|
|
|
// Make sure the path contains a / or the linker will search for it.
|
2014-01-18 05:35:18 +00:00
|
|
|
let path = os::make_absolute(&path);
|
2013-12-25 18:10:33 +00:00
|
|
|
|
2014-04-07 19:16:43 +00:00
|
|
|
let registrar = match registrar_symbol {
|
2013-12-25 18:10:33 +00:00
|
|
|
Some(registrar) => registrar,
|
|
|
|
None => return
|
|
|
|
};
|
|
|
|
|
|
|
|
let lib = match DynamicLibrary::open(Some(&path)) {
|
|
|
|
Ok(lib) => lib,
|
2014-01-17 14:53:10 +00:00
|
|
|
// this is fatal: there are almost certainly macros we need
|
|
|
|
// inside this crate, so continue would spew "macro undefined"
|
|
|
|
// errors
|
2014-02-05 21:15:24 +00:00
|
|
|
Err(err) => fld.cx.span_fatal(krate.span, err)
|
2013-12-25 18:10:33 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
unsafe {
|
|
|
|
let registrar: MacroCrateRegistrationFun = match lib.symbol(registrar) {
|
|
|
|
Ok(registrar) => registrar,
|
2014-01-17 14:53:10 +00:00
|
|
|
// again fatal if we can't register macros
|
2014-02-05 21:15:24 +00:00
|
|
|
Err(err) => fld.cx.span_fatal(krate.span, err)
|
2013-12-25 18:10:33 +00:00
|
|
|
};
|
|
|
|
registrar(|name, extension| {
|
|
|
|
let extension = match extension {
|
2014-02-05 21:15:24 +00:00
|
|
|
NormalTT(ext, _) => NormalTT(ext, Some(krate.span)),
|
|
|
|
IdentTT(ext, _) => IdentTT(ext, Some(krate.span)),
|
2013-12-25 18:10:33 +00:00
|
|
|
ItemDecorator(ext) => ItemDecorator(ext),
|
2014-02-28 07:49:25 +00:00
|
|
|
ItemModifier(ext) => ItemModifier(ext),
|
2013-12-25 18:10:33 +00:00
|
|
|
};
|
|
|
|
fld.extsbox.insert(name, extension);
|
|
|
|
});
|
|
|
|
|
2014-02-12 06:43:23 +00:00
|
|
|
// Intentionally leak the dynamic library. We can't ever unload it
|
|
|
|
// since the library can do things that will outlive the expansion
|
|
|
|
// phase (e.g. make an @-box cycle or launch a task).
|
|
|
|
cast::forget(lib);
|
|
|
|
}
|
2013-12-25 18:10:33 +00:00
|
|
|
}
|
|
|
|
|
2013-02-26 18:15:29 +00:00
|
|
|
// expand a stmt
|
2013-12-28 03:34:51 +00:00
|
|
|
pub fn expand_stmt(s: &Stmt, fld: &mut MacroExpander) -> SmallVector<@Stmt> {
|
2013-06-29 12:59:08 +00:00
|
|
|
// why the copying here and not in expand_expr?
|
|
|
|
// looks like classic changed-in-only-one-place
|
2014-01-25 21:34:26 +00:00
|
|
|
let (pth, tts, semi) = match s.node {
|
2013-09-02 01:45:37 +00:00
|
|
|
StmtMac(ref mac, semi) => {
|
2013-02-18 06:20:36 +00:00
|
|
|
match mac.node {
|
2014-01-25 21:34:26 +00:00
|
|
|
MacInvocTT(ref pth, ref tts, _) => {
|
|
|
|
(pth, (*tts).clone(), semi)
|
2013-02-25 05:27:51 +00:00
|
|
|
}
|
2012-12-12 20:25:40 +00:00
|
|
|
}
|
|
|
|
}
|
2013-12-28 00:43:01 +00:00
|
|
|
_ => return expand_non_macro_stmt(s, fld)
|
2012-12-12 20:25:40 +00:00
|
|
|
};
|
2014-01-19 08:21:14 +00:00
|
|
|
if pth.segments.len() > 1u {
|
2014-01-17 14:53:10 +00:00
|
|
|
fld.cx.span_err(pth.span, "expected macro name without module separators");
|
|
|
|
return SmallVector::zero();
|
2013-03-04 21:58:31 +00:00
|
|
|
}
|
2014-02-28 20:54:01 +00:00
|
|
|
let extname = pth.segments.get(0).identifier;
|
2014-02-14 05:07:09 +00:00
|
|
|
let extnamestr = token::get_ident(extname);
|
2014-01-06 02:02:57 +00:00
|
|
|
let marked_after = match fld.extsbox.find(&extname.name) {
|
2013-08-29 19:10:02 +00:00
|
|
|
None => {
|
2014-02-14 05:07:09 +00:00
|
|
|
fld.cx.span_err(pth.span, format!("macro undefined: '{}'", extnamestr));
|
2014-01-17 14:53:10 +00:00
|
|
|
return SmallVector::zero();
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
2012-11-17 12:41:36 +00:00
|
|
|
|
2014-01-06 02:02:57 +00:00
|
|
|
Some(&NormalTT(ref expandfun, exp_span)) => {
|
2013-12-28 00:43:01 +00:00
|
|
|
fld.cx.bt_push(ExpnInfo {
|
2013-08-29 19:10:02 +00:00
|
|
|
call_site: s.span,
|
|
|
|
callee: NameAndSpan {
|
2014-01-30 23:53:09 +00:00
|
|
|
name: extnamestr.get().to_str(),
|
2013-12-07 02:41:11 +00:00
|
|
|
format: MacroBang,
|
2013-08-29 19:10:02 +00:00
|
|
|
span: exp_span,
|
|
|
|
}
|
2013-07-02 09:31:00 +00:00
|
|
|
});
|
2013-06-29 12:59:08 +00:00
|
|
|
let fm = fresh_mark();
|
|
|
|
// mark before expansion:
|
2014-02-28 20:54:01 +00:00
|
|
|
let marked_tts = mark_tts(tts.as_slice(), fm);
|
2013-09-15 08:27:38 +00:00
|
|
|
|
|
|
|
// See the comment in expand_expr for why we want the original span,
|
|
|
|
// not the current mac.span.
|
2013-12-28 00:43:01 +00:00
|
|
|
let mac_span = original_span(fld.cx);
|
2013-09-15 08:27:38 +00:00
|
|
|
|
2013-12-28 00:43:01 +00:00
|
|
|
let expanded = match expandfun.expand(fld.cx,
|
2013-08-30 21:40:05 +00:00
|
|
|
mac_span.call_site,
|
2014-02-28 20:54:01 +00:00
|
|
|
marked_tts.as_slice()) {
|
2013-08-29 19:10:02 +00:00
|
|
|
MRExpr(e) => {
|
|
|
|
@codemap::Spanned {
|
|
|
|
node: StmtExpr(e, ast::DUMMY_NODE_ID),
|
|
|
|
span: e.span,
|
|
|
|
}
|
|
|
|
}
|
2013-08-30 21:40:05 +00:00
|
|
|
MRAny(any_macro) => any_macro.make_stmt(),
|
2014-01-17 14:53:10 +00:00
|
|
|
_ => {
|
|
|
|
fld.cx.span_err(pth.span,
|
2014-01-30 23:53:09 +00:00
|
|
|
format!("non-stmt macro in stmt pos: {}",
|
2014-02-14 05:07:09 +00:00
|
|
|
extnamestr));
|
2014-01-17 14:53:10 +00:00
|
|
|
return SmallVector::zero();
|
|
|
|
}
|
2012-11-17 12:41:36 +00:00
|
|
|
};
|
|
|
|
|
2014-01-06 02:02:57 +00:00
|
|
|
mark_stmt(expanded,fm)
|
2012-11-20 01:31:22 +00:00
|
|
|
}
|
2012-11-19 05:30:48 +00:00
|
|
|
|
2012-11-17 12:41:36 +00:00
|
|
|
_ => {
|
2014-01-30 23:53:09 +00:00
|
|
|
fld.cx.span_err(pth.span, format!("'{}' is not a tt-style macro",
|
2014-02-14 05:07:09 +00:00
|
|
|
extnamestr));
|
2014-01-17 14:53:10 +00:00
|
|
|
return SmallVector::zero();
|
2012-11-17 12:41:36 +00:00
|
|
|
}
|
2012-11-21 23:49:42 +00:00
|
|
|
};
|
|
|
|
|
2014-01-06 02:02:57 +00:00
|
|
|
// Keep going, outside-in.
|
|
|
|
let fully_expanded = fld.fold_stmt(marked_after);
|
|
|
|
if fully_expanded.is_empty() {
|
2014-01-17 14:53:10 +00:00
|
|
|
fld.cx.span_err(pth.span, "macro didn't expand to a statement");
|
|
|
|
return SmallVector::zero();
|
2014-01-06 02:02:57 +00:00
|
|
|
}
|
|
|
|
fld.cx.bt_pop();
|
|
|
|
let fully_expanded: SmallVector<@Stmt> = fully_expanded.move_iter()
|
|
|
|
.map(|s| @Spanned { span: s.span, node: s.node.clone() })
|
|
|
|
.collect();
|
|
|
|
|
2013-11-25 07:08:53 +00:00
|
|
|
fully_expanded.move_iter().map(|s| {
|
|
|
|
match s.node {
|
|
|
|
StmtExpr(e, stmt_id) if semi => {
|
|
|
|
@Spanned {
|
|
|
|
span: s.span,
|
|
|
|
node: StmtSemi(e, stmt_id)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => s /* might already have a semi */
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
2013-11-25 07:08:53 +00:00
|
|
|
}).collect()
|
2012-11-20 01:31:22 +00:00
|
|
|
}
|
|
|
|
|
2013-06-25 18:40:51 +00:00
|
|
|
// expand a non-macro stmt. this is essentially the fallthrough for
|
|
|
|
// expand_stmt, above.
|
2013-12-28 03:34:51 +00:00
|
|
|
fn expand_non_macro_stmt(s: &Stmt, fld: &mut MacroExpander)
|
2013-11-25 07:08:53 +00:00
|
|
|
-> SmallVector<@Stmt> {
|
2013-06-25 18:40:51 +00:00
|
|
|
// is it a let?
|
2013-08-29 19:10:02 +00:00
|
|
|
match s.node {
|
2014-01-03 23:08:48 +00:00
|
|
|
StmtDecl(decl, node_id) => {
|
|
|
|
match *decl {
|
|
|
|
Spanned {
|
|
|
|
node: DeclLocal(ref local),
|
|
|
|
span: stmt_span
|
|
|
|
} => {
|
|
|
|
// take it apart:
|
|
|
|
let Local {
|
|
|
|
ty: _,
|
|
|
|
pat: pat,
|
|
|
|
init: init,
|
|
|
|
id: id,
|
|
|
|
span: span
|
|
|
|
} = **local;
|
|
|
|
// expand the pat (it might contain exprs... #:(o)>
|
|
|
|
let expanded_pat = fld.fold_pat(pat);
|
|
|
|
// find the pat_idents in the pattern:
|
|
|
|
// oh dear heaven... this is going to include the enum
|
|
|
|
// names, as well... but that should be okay, as long as
|
|
|
|
// the new names are gensyms for the old ones.
|
2014-02-28 21:09:09 +00:00
|
|
|
let mut name_finder = new_name_finder(Vec::new());
|
2014-01-03 23:08:48 +00:00
|
|
|
name_finder.visit_pat(expanded_pat,());
|
|
|
|
// generate fresh names, push them to a new pending list
|
2014-02-28 21:09:09 +00:00
|
|
|
let mut new_pending_renames = Vec::new();
|
2014-01-03 23:08:48 +00:00
|
|
|
for ident in name_finder.ident_accumulator.iter() {
|
|
|
|
let new_name = fresh_name(ident);
|
|
|
|
new_pending_renames.push((*ident,new_name));
|
|
|
|
}
|
|
|
|
let rewritten_pat = {
|
|
|
|
let mut rename_fld =
|
|
|
|
renames_to_fold(&mut new_pending_renames);
|
|
|
|
// rewrite the pattern using the new names (the old
|
|
|
|
// ones have already been applied):
|
|
|
|
rename_fld.fold_pat(expanded_pat)
|
|
|
|
};
|
|
|
|
// add them to the existing pending renames:
|
2014-02-24 20:47:19 +00:00
|
|
|
fld.extsbox.info().pending_renames.push_all_move(new_pending_renames);
|
2014-01-03 23:08:48 +00:00
|
|
|
// also, don't forget to expand the init:
|
|
|
|
let new_init_opt = init.map(|e| fld.fold_expr(e));
|
|
|
|
let rewritten_local =
|
|
|
|
@Local {
|
|
|
|
ty: local.ty,
|
|
|
|
pat: rewritten_pat,
|
|
|
|
init: new_init_opt,
|
|
|
|
id: id,
|
|
|
|
span: span,
|
|
|
|
};
|
|
|
|
SmallVector::one(@Spanned {
|
|
|
|
node: StmtDecl(@Spanned {
|
|
|
|
node: DeclLocal(rewritten_local),
|
|
|
|
span: stmt_span
|
|
|
|
},
|
|
|
|
node_id),
|
|
|
|
span: span
|
|
|
|
})
|
|
|
|
}
|
|
|
|
_ => noop_fold_stmt(s, fld),
|
2013-12-31 01:45:39 +00:00
|
|
|
}
|
2013-06-25 18:40:51 +00:00
|
|
|
},
|
2013-08-29 19:10:02 +00:00
|
|
|
_ => noop_fold_stmt(s, fld),
|
2013-06-25 18:40:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// a visitor that extracts the pat_ident paths
|
2013-07-05 20:57:53 +00:00
|
|
|
// from a given thingy and puts them in a mutable
|
2013-06-25 18:40:51 +00:00
|
|
|
// array (passed in to the traversal)
|
2013-07-20 01:42:11 +00:00
|
|
|
#[deriving(Clone)]
|
2014-03-02 23:26:39 +00:00
|
|
|
pub struct NewNameFinderContext {
|
2014-02-28 21:09:09 +00:00
|
|
|
ident_accumulator: Vec<ast::Ident> ,
|
2013-07-20 01:42:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Visitor<()> for NewNameFinderContext {
|
2013-11-25 18:22:21 +00:00
|
|
|
fn visit_pat(&mut self, pattern: &ast::Pat, _: ()) {
|
2013-07-20 01:42:11 +00:00
|
|
|
match *pattern {
|
|
|
|
// we found a pat_ident!
|
2013-09-02 01:45:37 +00:00
|
|
|
ast::Pat {
|
2013-07-20 01:42:11 +00:00
|
|
|
id: _,
|
2013-09-02 01:45:37 +00:00
|
|
|
node: ast::PatIdent(_, ref path, ref inner),
|
2013-07-20 01:42:11 +00:00
|
|
|
span: _
|
|
|
|
} => {
|
|
|
|
match path {
|
|
|
|
// a path of length one:
|
|
|
|
&ast::Path {
|
|
|
|
global: false,
|
|
|
|
span: _,
|
2014-02-13 17:46:46 +00:00
|
|
|
segments: ref segments
|
|
|
|
} if segments.len() == 1 => {
|
2014-02-28 20:54:01 +00:00
|
|
|
self.ident_accumulator.push(segments.get(0)
|
|
|
|
.identifier)
|
2014-02-13 17:46:46 +00:00
|
|
|
}
|
2013-07-20 01:42:11 +00:00
|
|
|
// I believe these must be enums...
|
|
|
|
_ => ()
|
|
|
|
}
|
|
|
|
// visit optional subpattern of pat_ident:
|
2013-08-03 16:45:23 +00:00
|
|
|
for subpat in inner.iter() {
|
2013-07-20 01:42:11 +00:00
|
|
|
self.visit_pat(*subpat, ())
|
2013-06-04 21:56:33 +00:00
|
|
|
}
|
|
|
|
}
|
2013-07-20 01:42:11 +00:00
|
|
|
// use the default traversal for non-pat_idents
|
2013-08-08 12:23:25 +00:00
|
|
|
_ => visit::walk_pat(self, pattern, ())
|
2013-07-20 01:42:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-08-08 12:23:25 +00:00
|
|
|
fn visit_ty(&mut self, typ: &ast::Ty, _: ()) {
|
|
|
|
visit::walk_ty(self, typ, ())
|
2013-07-20 01:42:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// return a visitor that extracts the pat_ident paths
|
2013-07-05 20:57:53 +00:00
|
|
|
// from a given thingy and puts them in a mutable
|
2013-07-20 01:42:11 +00:00
|
|
|
// array (passed in to the traversal)
|
2014-02-28 21:09:09 +00:00
|
|
|
pub fn new_name_finder(idents: Vec<ast::Ident> ) -> NewNameFinderContext {
|
2013-10-28 08:20:26 +00:00
|
|
|
NewNameFinderContext {
|
2013-07-20 01:42:11 +00:00
|
|
|
ident_accumulator: idents,
|
2013-10-28 08:20:26 +00:00
|
|
|
}
|
2013-06-29 12:59:08 +00:00
|
|
|
}
|
|
|
|
|
2013-07-05 20:57:53 +00:00
|
|
|
// expand a block. pushes a new exts_frame, then calls expand_block_elts
|
2013-12-28 03:34:51 +00:00
|
|
|
pub fn expand_block(blk: &Block, fld: &mut MacroExpander) -> P<Block> {
|
2013-06-25 18:40:51 +00:00
|
|
|
// see note below about treatment of exts table
|
2013-12-28 00:43:01 +00:00
|
|
|
with_exts_frame!(fld.extsbox,false,
|
|
|
|
expand_block_elts(blk, fld))
|
2013-06-25 18:40:51 +00:00
|
|
|
}
|
|
|
|
|
2013-07-05 20:57:53 +00:00
|
|
|
// expand the elements of a block.
|
2013-12-28 03:34:51 +00:00
|
|
|
pub fn expand_block_elts(b: &Block, fld: &mut MacroExpander) -> P<Block> {
|
2014-03-28 19:42:34 +00:00
|
|
|
let new_view_items = b.view_items.iter().map(|x| fld.fold_view_item(x)).collect();
|
2014-02-07 22:28:07 +00:00
|
|
|
let new_stmts =
|
|
|
|
b.stmts.iter().flat_map(|x| {
|
|
|
|
let renamed_stmt = {
|
2013-12-31 01:45:39 +00:00
|
|
|
let pending_renames = &mut fld.extsbox.info().pending_renames;
|
|
|
|
let mut rename_fld = renames_to_fold(pending_renames);
|
|
|
|
rename_fld.fold_stmt(*x).expect_one("rename_fold didn't return one value")
|
2014-02-07 22:28:07 +00:00
|
|
|
};
|
|
|
|
fld.fold_stmt(renamed_stmt).move_iter()
|
|
|
|
}).collect();
|
2013-12-31 01:45:39 +00:00
|
|
|
let new_expr = b.expr.map(|x| {
|
|
|
|
let expr = {
|
|
|
|
let pending_renames = &mut fld.extsbox.info().pending_renames;
|
|
|
|
let mut rename_fld = renames_to_fold(pending_renames);
|
|
|
|
rename_fld.fold_expr(x)
|
|
|
|
};
|
|
|
|
fld.fold_expr(expr)
|
|
|
|
});
|
2013-11-30 22:00:39 +00:00
|
|
|
P(Block {
|
2013-06-25 18:40:51 +00:00
|
|
|
view_items: new_view_items,
|
|
|
|
stmts: new_stmts,
|
|
|
|
expr: new_expr,
|
|
|
|
id: fld.new_id(b.id),
|
|
|
|
rules: b.rules,
|
|
|
|
span: b.span,
|
2013-11-30 22:00:39 +00:00
|
|
|
})
|
2013-05-17 16:41:04 +00:00
|
|
|
}
|
|
|
|
|
2014-03-02 23:26:39 +00:00
|
|
|
pub struct IdentRenamer<'a> {
|
2013-12-31 01:45:39 +00:00
|
|
|
renames: &'a mut RenameList,
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
|
|
|
|
2014-01-09 13:05:33 +00:00
|
|
|
impl<'a> Folder for IdentRenamer<'a> {
|
2014-02-24 20:47:19 +00:00
|
|
|
fn fold_ident(&mut self, id: Ident) -> Ident {
|
2013-08-29 19:10:02 +00:00
|
|
|
let new_ctxt = self.renames.iter().fold(id.ctxt, |ctxt, &(from, to)| {
|
2014-02-24 20:47:19 +00:00
|
|
|
mtwt::new_rename(from, to, ctxt)
|
2013-08-29 19:10:02 +00:00
|
|
|
});
|
2014-02-24 20:47:19 +00:00
|
|
|
Ident {
|
2013-08-29 19:10:02 +00:00
|
|
|
name: id.name,
|
|
|
|
ctxt: new_ctxt,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// given a mutable list of renames, return a tree-folder that applies those
|
|
|
|
// renames.
|
2013-12-31 01:45:39 +00:00
|
|
|
pub fn renames_to_fold<'a>(renames: &'a mut RenameList) -> IdentRenamer<'a> {
|
2013-12-28 03:34:51 +00:00
|
|
|
IdentRenamer {
|
2013-08-29 19:10:02 +00:00
|
|
|
renames: renames,
|
2013-12-28 03:34:51 +00:00
|
|
|
}
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
|
|
|
|
2013-12-28 00:17:36 +00:00
|
|
|
pub fn new_span(cx: &ExtCtxt, sp: Span) -> Span {
|
2012-02-05 01:37:24 +00:00
|
|
|
/* this discards information in the case of macro-defining macros */
|
2013-08-29 19:10:02 +00:00
|
|
|
Span {
|
|
|
|
lo: sp.lo,
|
|
|
|
hi: sp.hi,
|
|
|
|
expn_info: cx.backtrace(),
|
|
|
|
}
|
2012-02-05 01:37:24 +00:00
|
|
|
}
|
|
|
|
|
2014-03-09 14:54:34 +00:00
|
|
|
pub struct MacroExpander<'a, 'b> {
|
2014-03-27 22:39:48 +00:00
|
|
|
pub extsbox: SyntaxEnv,
|
|
|
|
pub cx: &'a mut ExtCtxt<'b>,
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
|
|
|
|
2014-03-09 14:54:34 +00:00
|
|
|
impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
|
2013-12-28 03:34:51 +00:00
|
|
|
fn fold_expr(&mut self, expr: @ast::Expr) -> @ast::Expr {
|
2013-12-28 00:43:01 +00:00
|
|
|
expand_expr(expr, self)
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
|
|
|
|
2014-01-09 13:05:33 +00:00
|
|
|
fn fold_item(&mut self, item: @ast::Item) -> SmallVector<@ast::Item> {
|
2013-12-28 00:43:01 +00:00
|
|
|
expand_item(item, self)
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
|
|
|
|
2013-12-25 18:10:33 +00:00
|
|
|
fn fold_view_item(&mut self, vi: &ast::ViewItem) -> ast::ViewItem {
|
|
|
|
expand_view_item(vi, self)
|
|
|
|
}
|
|
|
|
|
2013-12-28 03:34:51 +00:00
|
|
|
fn fold_stmt(&mut self, stmt: &ast::Stmt) -> SmallVector<@ast::Stmt> {
|
2013-12-28 00:43:01 +00:00
|
|
|
expand_stmt(stmt, self)
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
|
|
|
|
2013-12-28 03:34:51 +00:00
|
|
|
fn fold_block(&mut self, block: P<Block>) -> P<Block> {
|
2013-12-28 00:43:01 +00:00
|
|
|
expand_block(block, self)
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
|
|
|
|
2013-12-28 03:34:51 +00:00
|
|
|
fn new_span(&mut self, span: Span) -> Span {
|
2013-08-29 19:10:02 +00:00
|
|
|
new_span(self.cx, span)
|
|
|
|
}
|
2013-07-16 05:05:50 +00:00
|
|
|
}
|
|
|
|
|
2014-03-01 07:17:38 +00:00
|
|
|
pub struct ExpansionConfig<'a> {
|
2014-03-27 22:39:48 +00:00
|
|
|
pub loader: &'a mut CrateLoader,
|
|
|
|
pub deriving_hash_type_parameter: bool,
|
|
|
|
pub crate_id: CrateId,
|
2014-03-01 07:17:38 +00:00
|
|
|
}
|
|
|
|
|
2014-03-09 14:54:34 +00:00
|
|
|
pub fn expand_crate(parse_sess: &parse::ParseSess,
|
2014-03-01 07:17:38 +00:00
|
|
|
cfg: ExpansionConfig,
|
2013-09-28 02:46:09 +00:00
|
|
|
c: Crate) -> Crate {
|
2014-03-01 07:17:38 +00:00
|
|
|
let mut cx = ExtCtxt::new(parse_sess, c.config.clone(), cfg);
|
2013-12-28 03:34:51 +00:00
|
|
|
let mut expander = MacroExpander {
|
2013-12-31 01:45:39 +00:00
|
|
|
extsbox: syntax_expander_table(),
|
2013-12-29 05:06:22 +00:00
|
|
|
cx: &mut cx,
|
2013-12-28 00:17:36 +00:00
|
|
|
};
|
2013-08-29 19:10:02 +00:00
|
|
|
|
2013-09-28 02:46:09 +00:00
|
|
|
let ret = expander.fold_crate(c);
|
2013-07-29 08:12:41 +00:00
|
|
|
parse_sess.span_diagnostic.handler().abort_if_errors();
|
|
|
|
return ret;
|
2011-07-06 22:22:23 +00:00
|
|
|
}
|
2013-02-26 18:15:29 +00:00
|
|
|
|
2013-07-13 17:18:37 +00:00
|
|
|
// HYGIENIC CONTEXT EXTENSION:
|
|
|
|
// all of these functions are for walking over
|
|
|
|
// ASTs and making some change to the context of every
|
|
|
|
// element that has one. a CtxtFn is a trait-ified
|
|
|
|
// version of a closure in (SyntaxContext -> SyntaxContext).
|
|
|
|
// the ones defined here include:
|
|
|
|
// Marker - add a mark to a context
|
|
|
|
|
2014-02-06 22:38:33 +00:00
|
|
|
// A Marker adds the given mark to the syntax context
|
|
|
|
struct Marker { mark: Mrk }
|
2013-07-09 22:56:21 +00:00
|
|
|
|
2014-02-06 22:38:33 +00:00
|
|
|
impl Folder for Marker {
|
2014-02-24 20:47:19 +00:00
|
|
|
fn fold_ident(&mut self, id: Ident) -> Ident {
|
2013-08-29 19:10:02 +00:00
|
|
|
ast::Ident {
|
2014-02-06 22:38:33 +00:00
|
|
|
name: id.name,
|
2014-02-24 20:47:19 +00:00
|
|
|
ctxt: mtwt::new_mark(self.mark, id.ctxt)
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
|
|
|
}
|
2014-01-09 13:05:33 +00:00
|
|
|
fn fold_mac(&mut self, m: &ast::Mac) -> ast::Mac {
|
2013-08-29 19:10:02 +00:00
|
|
|
let macro = match m.node {
|
2014-01-09 13:05:33 +00:00
|
|
|
MacInvocTT(ref path, ref tts, ctxt) => {
|
|
|
|
MacInvocTT(self.fold_path(path),
|
2014-02-28 20:54:01 +00:00
|
|
|
fold_tts(tts.as_slice(), self),
|
2014-02-24 20:47:19 +00:00
|
|
|
mtwt::new_mark(self.mark, ctxt))
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
Spanned {
|
|
|
|
node: macro,
|
|
|
|
span: m.span,
|
|
|
|
}
|
2013-07-09 22:56:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// just a convenience:
|
2014-02-06 22:38:33 +00:00
|
|
|
fn new_mark_folder(m: Mrk) -> Marker {
|
|
|
|
Marker {mark: m}
|
2013-07-09 22:56:21 +00:00
|
|
|
}
|
|
|
|
|
2013-06-29 12:59:08 +00:00
|
|
|
// apply a given mark to the given token trees. Used prior to expansion of a macro.
|
2014-02-28 21:09:09 +00:00
|
|
|
fn mark_tts(tts: &[TokenTree], m: Mrk) -> Vec<TokenTree> {
|
2013-12-28 03:34:51 +00:00
|
|
|
fold_tts(tts, &mut new_mark_folder(m))
|
2013-06-29 12:59:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// apply a given mark to the given expr. Used following the expansion of a macro.
|
2014-02-06 22:38:33 +00:00
|
|
|
fn mark_expr(expr: @ast::Expr, m: Mrk) -> @ast::Expr {
|
2013-07-09 22:56:21 +00:00
|
|
|
new_mark_folder(m).fold_expr(expr)
|
2013-06-29 12:59:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// apply a given mark to the given stmt. Used following the expansion of a macro.
|
2014-02-06 22:38:33 +00:00
|
|
|
fn mark_stmt(expr: &ast::Stmt, m: Mrk) -> @ast::Stmt {
|
2013-11-26 04:02:15 +00:00
|
|
|
new_mark_folder(m).fold_stmt(expr)
|
|
|
|
.expect_one("marking a stmt didn't return a stmt")
|
2013-06-29 12:59:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// apply a given mark to the given item. Used following the expansion of a macro.
|
2014-02-06 22:38:33 +00:00
|
|
|
fn mark_item(expr: @ast::Item, m: Mrk) -> SmallVector<@ast::Item> {
|
2013-07-09 22:56:21 +00:00
|
|
|
new_mark_folder(m).fold_item(expr)
|
2013-06-29 12:59:08 +00:00
|
|
|
}
|
|
|
|
|
2013-12-28 00:17:36 +00:00
|
|
|
fn original_span(cx: &ExtCtxt) -> @codemap::ExpnInfo {
|
2013-09-15 08:27:38 +00:00
|
|
|
let mut relevant_info = cx.backtrace();
|
|
|
|
let mut einfo = relevant_info.unwrap();
|
|
|
|
loop {
|
|
|
|
match relevant_info {
|
|
|
|
None => { break }
|
|
|
|
Some(e) => {
|
|
|
|
einfo = e;
|
|
|
|
relevant_info = einfo.call_site.expn_info;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return einfo;
|
|
|
|
}
|
2013-07-14 19:25:04 +00:00
|
|
|
|
2013-02-26 18:15:29 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
use super::*;
|
2013-02-25 19:11:21 +00:00
|
|
|
use ast;
|
2014-02-08 05:37:34 +00:00
|
|
|
use ast::{Attribute_, AttrOuter, MetaWord};
|
2013-02-25 19:11:21 +00:00
|
|
|
use codemap;
|
2013-08-31 16:13:04 +00:00
|
|
|
use codemap::Spanned;
|
2013-12-25 18:10:33 +00:00
|
|
|
use ext::base::{CrateLoader, MacroCrate};
|
2014-02-24 20:47:19 +00:00
|
|
|
use ext::mtwt;
|
2013-02-25 19:11:21 +00:00
|
|
|
use parse;
|
2013-07-14 19:25:04 +00:00
|
|
|
use parse::token;
|
2014-03-09 14:54:34 +00:00
|
|
|
use util::parser_testing::{string_to_parser};
|
2014-01-30 22:28:44 +00:00
|
|
|
use util::parser_testing::{string_to_pat, strs_to_idents};
|
2013-06-29 12:59:08 +00:00
|
|
|
use visit;
|
2013-10-28 08:20:26 +00:00
|
|
|
use visit::Visitor;
|
|
|
|
|
|
|
|
// a visitor that extracts the paths
|
|
|
|
// from a given thingy and puts them in a mutable
|
|
|
|
// array (passed in to the traversal)
|
|
|
|
#[deriving(Clone)]
|
|
|
|
struct NewPathExprFinderContext {
|
2014-02-28 21:09:09 +00:00
|
|
|
path_accumulator: Vec<ast::Path> ,
|
2013-10-28 08:20:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Visitor<()> for NewPathExprFinderContext {
|
|
|
|
|
2014-01-06 12:00:46 +00:00
|
|
|
fn visit_expr(&mut self, expr: &ast::Expr, _: ()) {
|
2013-10-28 08:20:26 +00:00
|
|
|
match *expr {
|
|
|
|
ast::Expr{id:_,span:_,node:ast::ExprPath(ref p)} => {
|
|
|
|
self.path_accumulator.push(p.clone());
|
|
|
|
// not calling visit_path, should be fine.
|
|
|
|
}
|
|
|
|
_ => visit::walk_expr(self,expr,())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn visit_ty(&mut self, typ: &ast::Ty, _: ()) {
|
|
|
|
visit::walk_ty(self, typ, ())
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// return a visitor that extracts the paths
|
|
|
|
// from a given pattern and puts them in a mutable
|
|
|
|
// array (passed in to the traversal)
|
2014-02-28 21:09:09 +00:00
|
|
|
pub fn new_path_finder(paths: Vec<ast::Path> ) -> NewPathExprFinderContext {
|
2013-10-28 08:20:26 +00:00
|
|
|
NewPathExprFinderContext {
|
|
|
|
path_accumulator: paths
|
|
|
|
}
|
|
|
|
}
|
2013-02-26 18:15:29 +00:00
|
|
|
|
2013-12-25 18:10:33 +00:00
|
|
|
struct ErrLoader;
|
|
|
|
|
|
|
|
impl CrateLoader for ErrLoader {
|
|
|
|
fn load_crate(&mut self, _: &ast::ViewItem) -> MacroCrate {
|
|
|
|
fail!("lolwut")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-02-26 18:15:29 +00:00
|
|
|
// these following tests are quite fragile, in that they don't test what
|
|
|
|
// *kind* of failure occurs.
|
|
|
|
|
|
|
|
// make sure that macros can leave scope
|
|
|
|
#[should_fail]
|
|
|
|
#[test] fn macros_cant_escape_fns_test () {
|
2014-01-31 02:46:19 +00:00
|
|
|
let src = ~"fn bogus() {macro_rules! z (() => (3+4))}\
|
2013-02-26 18:15:29 +00:00
|
|
|
fn inty() -> int { z!() }";
|
2014-02-06 22:38:33 +00:00
|
|
|
let sess = parse::new_parse_sess();
|
2013-02-26 18:15:29 +00:00
|
|
|
let crate_ast = parse::parse_crate_from_source_str(
|
2014-01-31 02:46:19 +00:00
|
|
|
~"<test>",
|
2013-06-12 17:02:55 +00:00
|
|
|
src,
|
2014-03-17 07:55:41 +00:00
|
|
|
Vec::new(), &sess);
|
2013-02-26 18:15:29 +00:00
|
|
|
// should fail:
|
2013-12-25 18:10:33 +00:00
|
|
|
let mut loader = ErrLoader;
|
2014-03-01 07:17:38 +00:00
|
|
|
let cfg = ::syntax::ext::expand::ExpansionConfig {
|
|
|
|
loader: &mut loader,
|
|
|
|
deriving_hash_type_parameter: false,
|
log: Introduce liblog, the old std::logging
This commit moves all logging out of the standard library into an external
crate. This crate is the new crate which is responsible for all logging macros
and logging implementation. A few reasons for this change are:
* The crate map has always been a bit of a code smell among rust programs. It
has difficulty being loaded on almost all platforms, and it's used almost
exclusively for logging and only logging. Removing the crate map is one of the
end goals of this movement.
* The compiler has a fair bit of special support for logging. It has the
__log_level() expression as well as generating a global word per module
specifying the log level. This is unfairly favoring the built-in logging
system, and is much better done purely in libraries instead of the compiler
itself.
* Initialization of logging is much easier to do if there is no reliance on a
magical crate map being available to set module log levels.
* If the logging library can be written outside of the standard library, there's
no reason that it shouldn't be. It's likely that we're not going to build the
highest quality logging library of all time, so third-party libraries should
be able to provide just as high-quality logging systems as the default one
provided in the rust distribution.
With a migration such as this, the change does not come for free. There are some
subtle changes in the behavior of liblog vs the previous logging macros:
* The core change of this migration is that there is no longer a physical
log-level per module. This concept is still emulated (it is quite useful), but
there is now only a global log level, not a local one. This global log level
is a reflection of the maximum of all log levels specified. The previously
generated logging code looked like:
if specified_level <= __module_log_level() {
println!(...)
}
The newly generated code looks like:
if specified_level <= ::log::LOG_LEVEL {
if ::log::module_enabled(module_path!()) {
println!(...)
}
}
Notably, the first layer of checking is still intended to be "super fast" in
that it's just a load of a global word and a compare. The second layer of
checking is executed to determine if the current module does indeed have
logging turned on.
This means that if any module has a debug log level turned on, all modules
with debug log levels get a little bit slower (they all do more expensive
dynamic checks to determine if they're turned on or not).
Semantically, this migration brings no change in this respect, but
runtime-wise, this will have a perf impact on some code.
* A `RUST_LOG=::help` directive will no longer print out a list of all modules
that can be logged. This is because the crate map will no longer specify the
log levels of all modules, so the list of modules is not known. Additionally,
warnings can no longer be provided if a malformed logging directive was
supplied.
The new "hello world" for logging looks like:
#[phase(syntax, link)]
extern crate log;
fn main() {
debug!("Hello, world!");
}
2014-03-09 06:11:44 +00:00
|
|
|
crate_id: from_str("test").unwrap(),
|
2014-03-01 07:17:38 +00:00
|
|
|
};
|
2014-03-17 07:55:41 +00:00
|
|
|
expand_crate(&sess,cfg,crate_ast);
|
2013-02-26 18:15:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// make sure that macros can leave scope for modules
|
|
|
|
#[should_fail]
|
|
|
|
#[test] fn macros_cant_escape_mods_test () {
|
2014-01-31 02:46:19 +00:00
|
|
|
let src = ~"mod foo {macro_rules! z (() => (3+4))}\
|
2013-02-26 18:15:29 +00:00
|
|
|
fn inty() -> int { z!() }";
|
2014-02-06 22:38:33 +00:00
|
|
|
let sess = parse::new_parse_sess();
|
2013-02-26 18:15:29 +00:00
|
|
|
let crate_ast = parse::parse_crate_from_source_str(
|
2014-01-31 02:46:19 +00:00
|
|
|
~"<test>",
|
2013-06-12 17:02:55 +00:00
|
|
|
src,
|
2014-03-17 07:55:41 +00:00
|
|
|
Vec::new(), &sess);
|
2013-02-26 18:15:29 +00:00
|
|
|
// should fail:
|
2013-12-25 18:10:33 +00:00
|
|
|
let mut loader = ErrLoader;
|
2014-03-01 07:17:38 +00:00
|
|
|
let cfg = ::syntax::ext::expand::ExpansionConfig {
|
|
|
|
loader: &mut loader,
|
|
|
|
deriving_hash_type_parameter: false,
|
log: Introduce liblog, the old std::logging
This commit moves all logging out of the standard library into an external
crate. This crate is the new crate which is responsible for all logging macros
and logging implementation. A few reasons for this change are:
* The crate map has always been a bit of a code smell among rust programs. It
has difficulty being loaded on almost all platforms, and it's used almost
exclusively for logging and only logging. Removing the crate map is one of the
end goals of this movement.
* The compiler has a fair bit of special support for logging. It has the
__log_level() expression as well as generating a global word per module
specifying the log level. This is unfairly favoring the built-in logging
system, and is much better done purely in libraries instead of the compiler
itself.
* Initialization of logging is much easier to do if there is no reliance on a
magical crate map being available to set module log levels.
* If the logging library can be written outside of the standard library, there's
no reason that it shouldn't be. It's likely that we're not going to build the
highest quality logging library of all time, so third-party libraries should
be able to provide just as high-quality logging systems as the default one
provided in the rust distribution.
With a migration such as this, the change does not come for free. There are some
subtle changes in the behavior of liblog vs the previous logging macros:
* The core change of this migration is that there is no longer a physical
log-level per module. This concept is still emulated (it is quite useful), but
there is now only a global log level, not a local one. This global log level
is a reflection of the maximum of all log levels specified. The previously
generated logging code looked like:
if specified_level <= __module_log_level() {
println!(...)
}
The newly generated code looks like:
if specified_level <= ::log::LOG_LEVEL {
if ::log::module_enabled(module_path!()) {
println!(...)
}
}
Notably, the first layer of checking is still intended to be "super fast" in
that it's just a load of a global word and a compare. The second layer of
checking is executed to determine if the current module does indeed have
logging turned on.
This means that if any module has a debug log level turned on, all modules
with debug log levels get a little bit slower (they all do more expensive
dynamic checks to determine if they're turned on or not).
Semantically, this migration brings no change in this respect, but
runtime-wise, this will have a perf impact on some code.
* A `RUST_LOG=::help` directive will no longer print out a list of all modules
that can be logged. This is because the crate map will no longer specify the
log levels of all modules, so the list of modules is not known. Additionally,
warnings can no longer be provided if a malformed logging directive was
supplied.
The new "hello world" for logging looks like:
#[phase(syntax, link)]
extern crate log;
fn main() {
debug!("Hello, world!");
}
2014-03-09 06:11:44 +00:00
|
|
|
crate_id: from_str("test").unwrap(),
|
2014-03-01 07:17:38 +00:00
|
|
|
};
|
2014-03-17 07:55:41 +00:00
|
|
|
expand_crate(&sess,cfg,crate_ast);
|
2013-02-26 18:15:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// macro_escape modules shouldn't cause macros to leave scope
|
|
|
|
#[test] fn macros_can_escape_flattened_mods_test () {
|
2014-01-31 02:46:19 +00:00
|
|
|
let src = ~"#[macro_escape] mod foo {macro_rules! z (() => (3+4))}\
|
2013-02-26 18:15:29 +00:00
|
|
|
fn inty() -> int { z!() }";
|
2014-02-06 22:38:33 +00:00
|
|
|
let sess = parse::new_parse_sess();
|
2013-02-26 18:15:29 +00:00
|
|
|
let crate_ast = parse::parse_crate_from_source_str(
|
2014-01-31 02:46:19 +00:00
|
|
|
~"<test>",
|
2013-06-12 17:02:55 +00:00
|
|
|
src,
|
2014-03-17 07:55:41 +00:00
|
|
|
Vec::new(), &sess);
|
2013-02-26 18:15:29 +00:00
|
|
|
// should fail:
|
2013-12-25 18:10:33 +00:00
|
|
|
let mut loader = ErrLoader;
|
2014-03-01 07:17:38 +00:00
|
|
|
let cfg = ::syntax::ext::expand::ExpansionConfig {
|
|
|
|
loader: &mut loader,
|
|
|
|
deriving_hash_type_parameter: false,
|
log: Introduce liblog, the old std::logging
This commit moves all logging out of the standard library into an external
crate. This crate is the new crate which is responsible for all logging macros
and logging implementation. A few reasons for this change are:
* The crate map has always been a bit of a code smell among rust programs. It
has difficulty being loaded on almost all platforms, and it's used almost
exclusively for logging and only logging. Removing the crate map is one of the
end goals of this movement.
* The compiler has a fair bit of special support for logging. It has the
__log_level() expression as well as generating a global word per module
specifying the log level. This is unfairly favoring the built-in logging
system, and is much better done purely in libraries instead of the compiler
itself.
* Initialization of logging is much easier to do if there is no reliance on a
magical crate map being available to set module log levels.
* If the logging library can be written outside of the standard library, there's
no reason that it shouldn't be. It's likely that we're not going to build the
highest quality logging library of all time, so third-party libraries should
be able to provide just as high-quality logging systems as the default one
provided in the rust distribution.
With a migration such as this, the change does not come for free. There are some
subtle changes in the behavior of liblog vs the previous logging macros:
* The core change of this migration is that there is no longer a physical
log-level per module. This concept is still emulated (it is quite useful), but
there is now only a global log level, not a local one. This global log level
is a reflection of the maximum of all log levels specified. The previously
generated logging code looked like:
if specified_level <= __module_log_level() {
println!(...)
}
The newly generated code looks like:
if specified_level <= ::log::LOG_LEVEL {
if ::log::module_enabled(module_path!()) {
println!(...)
}
}
Notably, the first layer of checking is still intended to be "super fast" in
that it's just a load of a global word and a compare. The second layer of
checking is executed to determine if the current module does indeed have
logging turned on.
This means that if any module has a debug log level turned on, all modules
with debug log levels get a little bit slower (they all do more expensive
dynamic checks to determine if they're turned on or not).
Semantically, this migration brings no change in this respect, but
runtime-wise, this will have a perf impact on some code.
* A `RUST_LOG=::help` directive will no longer print out a list of all modules
that can be logged. This is because the crate map will no longer specify the
log levels of all modules, so the list of modules is not known. Additionally,
warnings can no longer be provided if a malformed logging directive was
supplied.
The new "hello world" for logging looks like:
#[phase(syntax, link)]
extern crate log;
fn main() {
debug!("Hello, world!");
}
2014-03-09 06:11:44 +00:00
|
|
|
crate_id: from_str("test").unwrap(),
|
2014-03-01 07:17:38 +00:00
|
|
|
};
|
2014-03-17 07:55:41 +00:00
|
|
|
expand_crate(&sess, cfg, crate_ast);
|
2013-02-26 18:15:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test] fn test_contains_flatten (){
|
2014-01-31 02:46:19 +00:00
|
|
|
let attr1 = make_dummy_attr ("foo");
|
|
|
|
let attr2 = make_dummy_attr ("bar");
|
|
|
|
let escape_attr = make_dummy_attr ("macro_escape");
|
2014-02-28 21:09:09 +00:00
|
|
|
let attrs1 = vec!(attr1, escape_attr, attr2);
|
2014-02-28 20:54:01 +00:00
|
|
|
assert_eq!(contains_macro_escape(attrs1.as_slice()),true);
|
2014-02-28 21:09:09 +00:00
|
|
|
let attrs2 = vec!(attr1,attr2);
|
2014-02-28 20:54:01 +00:00
|
|
|
assert_eq!(contains_macro_escape(attrs2.as_slice()),false);
|
2013-02-26 18:15:29 +00:00
|
|
|
}
|
|
|
|
|
2013-07-19 11:51:37 +00:00
|
|
|
// make a MetaWord outer attribute with the given name
|
2014-01-31 02:46:19 +00:00
|
|
|
fn make_dummy_attr(s: &str) -> ast::Attribute {
|
2013-08-31 16:13:04 +00:00
|
|
|
Spanned {
|
2014-01-01 06:53:22 +00:00
|
|
|
span:codemap::DUMMY_SP,
|
2013-07-19 11:51:37 +00:00
|
|
|
node: Attribute_ {
|
|
|
|
style: AttrOuter,
|
2013-08-31 16:13:04 +00:00
|
|
|
value: @Spanned {
|
2014-01-31 02:46:19 +00:00
|
|
|
node: MetaWord(token::intern_and_get_ident(s)),
|
2014-01-01 06:53:22 +00:00
|
|
|
span: codemap::DUMMY_SP,
|
2013-02-28 15:25:31 +00:00
|
|
|
},
|
|
|
|
is_sugared_doc: false,
|
|
|
|
}
|
|
|
|
}
|
2013-02-26 18:15:29 +00:00
|
|
|
}
|
|
|
|
|
2014-02-05 21:15:24 +00:00
|
|
|
//fn fake_print_crate(krate: &ast::Crate) {
|
2014-01-04 03:24:33 +00:00
|
|
|
// let mut out = ~std::io::stderr() as ~std::io::Writer;
|
|
|
|
// let mut s = pprust::rust_printer(out, get_ident_interner());
|
2014-02-05 21:15:24 +00:00
|
|
|
// pprust::print_crate_(&mut s, krate);
|
2014-01-04 03:24:33 +00:00
|
|
|
//}
|
2013-05-17 00:42:08 +00:00
|
|
|
|
2014-01-31 02:46:19 +00:00
|
|
|
fn expand_crate_str(crate_str: ~str) -> ast::Crate {
|
2014-03-09 14:54:34 +00:00
|
|
|
let ps = parse::new_parse_sess();
|
2014-03-17 07:55:41 +00:00
|
|
|
let crate_ast = string_to_parser(&ps, crate_str).parse_crate_mod();
|
2013-06-25 18:40:51 +00:00
|
|
|
// the cfg argument actually does matter, here...
|
2013-12-25 18:10:33 +00:00
|
|
|
let mut loader = ErrLoader;
|
2014-03-01 07:17:38 +00:00
|
|
|
let cfg = ::syntax::ext::expand::ExpansionConfig {
|
|
|
|
loader: &mut loader,
|
|
|
|
deriving_hash_type_parameter: false,
|
log: Introduce liblog, the old std::logging
This commit moves all logging out of the standard library into an external
crate. This crate is the new crate which is responsible for all logging macros
and logging implementation. A few reasons for this change are:
* The crate map has always been a bit of a code smell among rust programs. It
has difficulty being loaded on almost all platforms, and it's used almost
exclusively for logging and only logging. Removing the crate map is one of the
end goals of this movement.
* The compiler has a fair bit of special support for logging. It has the
__log_level() expression as well as generating a global word per module
specifying the log level. This is unfairly favoring the built-in logging
system, and is much better done purely in libraries instead of the compiler
itself.
* Initialization of logging is much easier to do if there is no reliance on a
magical crate map being available to set module log levels.
* If the logging library can be written outside of the standard library, there's
no reason that it shouldn't be. It's likely that we're not going to build the
highest quality logging library of all time, so third-party libraries should
be able to provide just as high-quality logging systems as the default one
provided in the rust distribution.
With a migration such as this, the change does not come for free. There are some
subtle changes in the behavior of liblog vs the previous logging macros:
* The core change of this migration is that there is no longer a physical
log-level per module. This concept is still emulated (it is quite useful), but
there is now only a global log level, not a local one. This global log level
is a reflection of the maximum of all log levels specified. The previously
generated logging code looked like:
if specified_level <= __module_log_level() {
println!(...)
}
The newly generated code looks like:
if specified_level <= ::log::LOG_LEVEL {
if ::log::module_enabled(module_path!()) {
println!(...)
}
}
Notably, the first layer of checking is still intended to be "super fast" in
that it's just a load of a global word and a compare. The second layer of
checking is executed to determine if the current module does indeed have
logging turned on.
This means that if any module has a debug log level turned on, all modules
with debug log levels get a little bit slower (they all do more expensive
dynamic checks to determine if they're turned on or not).
Semantically, this migration brings no change in this respect, but
runtime-wise, this will have a perf impact on some code.
* A `RUST_LOG=::help` directive will no longer print out a list of all modules
that can be logged. This is because the crate map will no longer specify the
log levels of all modules, so the list of modules is not known. Additionally,
warnings can no longer be provided if a malformed logging directive was
supplied.
The new "hello world" for logging looks like:
#[phase(syntax, link)]
extern crate log;
fn main() {
debug!("Hello, world!");
}
2014-03-09 06:11:44 +00:00
|
|
|
crate_id: from_str("test").unwrap(),
|
2014-03-01 07:17:38 +00:00
|
|
|
};
|
2014-03-09 14:54:34 +00:00
|
|
|
expand_crate(&ps,cfg,crate_ast)
|
2013-07-09 23:00:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//fn expand_and_resolve(crate_str: @str) -> ast::crate {
|
|
|
|
//let expanded_ast = expand_crate_str(crate_str);
|
2014-01-09 10:06:55 +00:00
|
|
|
// println!("expanded: {:?}\n",expanded_ast);
|
2013-06-07 19:28:03 +00:00
|
|
|
//mtwt_resolve_crate(expanded_ast)
|
|
|
|
//}
|
2014-02-06 22:38:33 +00:00
|
|
|
//fn expand_and_resolve_and_pretty_print (crate_str: @str) -> ~str {
|
2013-06-07 19:28:03 +00:00
|
|
|
//let resolved_ast = expand_and_resolve(crate_str);
|
2013-06-07 01:09:31 +00:00
|
|
|
//pprust::to_str(&resolved_ast,fake_print_crate,get_ident_interner())
|
|
|
|
//}
|
2013-06-25 18:40:51 +00:00
|
|
|
|
2013-07-09 22:10:16 +00:00
|
|
|
#[test] fn macro_tokens_should_match(){
|
2014-01-31 02:46:19 +00:00
|
|
|
expand_crate_str(~"macro_rules! m((a)=>(13)) fn main(){m!(a);}");
|
2013-07-09 22:10:16 +00:00
|
|
|
}
|
|
|
|
|
2013-06-29 12:59:08 +00:00
|
|
|
// renaming tests expand a crate and then check that the bindings match
|
|
|
|
// the right varrefs. The specification of the test case includes the
|
|
|
|
// text of the crate, and also an array of arrays. Each element in the
|
|
|
|
// outer array corresponds to a binding in the traversal of the AST
|
|
|
|
// induced by visit. Each of these arrays contains a list of indexes,
|
|
|
|
// interpreted as the varrefs in the varref traversal that this binding
|
|
|
|
// should match. So, for instance, in a program with two bindings and
|
|
|
|
// three varrefs, the array ~[~[1,2],~[0]] would indicate that the first
|
|
|
|
// binding should match the second two varrefs, and the second binding
|
|
|
|
// should match the first varref.
|
|
|
|
//
|
|
|
|
// The comparisons are done post-mtwt-resolve, so we're comparing renamed
|
|
|
|
// names; differences in marks don't matter any more.
|
2013-07-13 01:35:47 +00:00
|
|
|
//
|
|
|
|
// oog... I also want tests that check "binding-identifier-=?". That is,
|
|
|
|
// not just "do these have the same name", but "do they have the same
|
|
|
|
// name *and* the same marks"? Understanding this is really pretty painful.
|
|
|
|
// in principle, you might want to control this boolean on a per-varref basis,
|
|
|
|
// but that would make things even harder to understand, and might not be
|
|
|
|
// necessary for thorough testing.
|
2014-02-28 20:54:01 +00:00
|
|
|
type RenamingTest = (&'static str, Vec<Vec<uint>>, bool);
|
2013-06-29 12:59:08 +00:00
|
|
|
|
2013-06-25 18:40:51 +00:00
|
|
|
#[test]
|
|
|
|
fn automatic_renaming () {
|
2014-02-28 21:09:09 +00:00
|
|
|
let tests: Vec<RenamingTest> =
|
|
|
|
vec!(// b & c should get new names throughout, in the expr too:
|
2013-06-29 12:59:08 +00:00
|
|
|
("fn a() -> int { let b = 13; let c = b; b+c }",
|
2014-02-28 21:09:09 +00:00
|
|
|
vec!(vec!(0,1),vec!(2)), false),
|
2013-06-07 22:01:28 +00:00
|
|
|
// both x's should be renamed (how is this causing a bug?)
|
2014-02-06 22:38:33 +00:00
|
|
|
("fn main () {let x: int = 13;x;}",
|
2014-02-28 21:09:09 +00:00
|
|
|
vec!(vec!(0)), false),
|
2013-06-29 12:59:08 +00:00
|
|
|
// 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)}",
|
2014-02-28 21:09:09 +00:00
|
|
|
vec!(vec!(1)), false),
|
2013-06-06 18:21:02 +00:00
|
|
|
// the b before the plus should not be renamed (requires marks)
|
2013-06-29 12:59:08 +00:00
|
|
|
("macro_rules! f (($x:ident) => ({let b=9; ($x + b)})) fn a() -> int { f!(b)}",
|
2014-02-28 21:09:09 +00:00
|
|
|
vec!(vec!(1)), false),
|
2013-06-29 12:59:08 +00:00
|
|
|
// 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}))
|
|
|
|
fn main() -> int {user!(z)}",
|
2014-02-28 21:09:09 +00:00
|
|
|
vec!(vec!(0)), false));
|
2013-09-05 20:28:57 +00:00
|
|
|
for (idx,s) in tests.iter().enumerate() {
|
|
|
|
run_renaming_test(s,idx);
|
2013-06-29 12:59:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-13 01:35:47 +00:00
|
|
|
// run one of the renaming tests
|
2014-01-09 13:05:33 +00:00
|
|
|
fn run_renaming_test(t: &RenamingTest, test_idx: uint) {
|
2013-09-05 16:29:31 +00:00
|
|
|
let invalid_name = token::special_idents::invalid.name;
|
2013-07-13 01:35:47 +00:00
|
|
|
let (teststr, bound_connections, bound_ident_check) = match *t {
|
2014-02-01 02:25:08 +00:00
|
|
|
(ref str,ref conns, bic) => (str.to_owned(), conns.clone(), bic)
|
2013-06-29 12:59:08 +00:00
|
|
|
};
|
2014-01-31 02:46:19 +00:00
|
|
|
let cr = expand_crate_str(teststr.to_owned());
|
2013-06-29 12:59:08 +00:00
|
|
|
// find the bindings:
|
2014-02-28 21:09:09 +00:00
|
|
|
let mut name_finder = new_name_finder(Vec::new());
|
2013-10-28 08:20:26 +00:00
|
|
|
visit::walk_crate(&mut name_finder,&cr,());
|
|
|
|
let bindings = name_finder.ident_accumulator;
|
|
|
|
|
2013-06-29 12:59:08 +00:00
|
|
|
// find the varrefs:
|
2014-02-28 21:09:09 +00:00
|
|
|
let mut path_finder = new_path_finder(Vec::new());
|
2013-10-28 08:20:26 +00:00
|
|
|
visit::walk_crate(&mut path_finder,&cr,());
|
|
|
|
let varrefs = path_finder.path_accumulator;
|
|
|
|
|
2013-06-29 12:59:08 +00:00
|
|
|
// must be one check clause for each binding:
|
|
|
|
assert_eq!(bindings.len(),bound_connections.len());
|
|
|
|
for (binding_idx,shouldmatch) in bound_connections.iter().enumerate() {
|
2014-02-24 20:47:19 +00:00
|
|
|
let binding_name = mtwt::resolve(*bindings.get(binding_idx));
|
|
|
|
let binding_marks = mtwt::marksof(bindings.get(binding_idx).ctxt, invalid_name);
|
2013-06-29 12:59:08 +00:00
|
|
|
// shouldmatch can't name varrefs that don't exist:
|
|
|
|
assert!((shouldmatch.len() == 0) ||
|
|
|
|
(varrefs.len() > *shouldmatch.iter().max().unwrap()));
|
|
|
|
for (idx,varref) in varrefs.iter().enumerate() {
|
|
|
|
if shouldmatch.contains(&idx) {
|
|
|
|
// it should be a path of length 1, and it should
|
2013-07-13 01:35:47 +00:00
|
|
|
// be free-identifier=? or bound-identifier=? to the given binding
|
2013-06-29 12:59:08 +00:00
|
|
|
assert_eq!(varref.segments.len(),1);
|
2014-02-24 20:47:19 +00:00
|
|
|
let varref_name = mtwt::resolve(varref.segments
|
2014-02-28 20:54:01 +00:00
|
|
|
.get(0)
|
2014-02-24 20:47:19 +00:00
|
|
|
.identifier);
|
|
|
|
let varref_marks = mtwt::marksof(varref.segments
|
|
|
|
.get(0)
|
|
|
|
.identifier
|
|
|
|
.ctxt,
|
|
|
|
invalid_name);
|
2014-01-30 22:28:44 +00:00
|
|
|
if !(varref_name==binding_name) {
|
2014-01-09 10:06:55 +00:00
|
|
|
println!("uh oh, should match but doesn't:");
|
2013-10-14 01:48:47 +00:00
|
|
|
println!("varref: {:?}",varref);
|
2014-02-28 20:54:01 +00:00
|
|
|
println!("binding: {:?}", *bindings.get(binding_idx));
|
2014-02-24 20:47:19 +00:00
|
|
|
mtwt::with_sctable(|x| mtwt::display_sctable(x));
|
2013-06-29 12:59:08 +00:00
|
|
|
}
|
|
|
|
assert_eq!(varref_name,binding_name);
|
2014-01-30 22:28:44 +00:00
|
|
|
if bound_ident_check {
|
2013-09-05 16:29:31 +00:00
|
|
|
// we're checking bound-identifier=?, and the marks
|
|
|
|
// should be the same, too:
|
2013-07-13 01:35:47 +00:00
|
|
|
assert_eq!(varref_marks,binding_marks.clone());
|
|
|
|
}
|
2013-06-29 12:59:08 +00:00
|
|
|
} else {
|
|
|
|
let fail = (varref.segments.len() == 1)
|
2014-02-24 20:47:19 +00:00
|
|
|
&& (mtwt::resolve(varref.segments.get(0).identifier)
|
|
|
|
== binding_name);
|
2013-06-29 12:59:08 +00:00
|
|
|
// temp debugging:
|
2014-01-30 22:28:44 +00:00
|
|
|
if fail {
|
2013-09-05 20:28:57 +00:00
|
|
|
println!("failure on test {}",test_idx);
|
|
|
|
println!("text of test case: \"{}\"", teststr);
|
|
|
|
println!("");
|
|
|
|
println!("uh oh, matches but shouldn't:");
|
2013-10-14 01:48:47 +00:00
|
|
|
println!("varref: {:?}",varref);
|
2013-09-05 20:28:57 +00:00
|
|
|
// good lord, you can't make a path with 0 segments, can you?
|
2014-02-28 20:54:01 +00:00
|
|
|
let string = token::get_ident(varref.segments
|
|
|
|
.get(0)
|
|
|
|
.identifier);
|
2013-09-05 20:28:57 +00:00
|
|
|
println!("varref's first segment's uint: {}, and string: \"{}\"",
|
2014-02-28 20:54:01 +00:00
|
|
|
varref.segments.get(0).identifier.name,
|
2014-01-31 23:26:59 +00:00
|
|
|
string.get());
|
2014-02-28 20:54:01 +00:00
|
|
|
println!("binding: {:?}", *bindings.get(binding_idx));
|
2014-02-24 20:47:19 +00:00
|
|
|
mtwt::with_sctable(|x| mtwt::display_sctable(x));
|
2013-06-29 12:59:08 +00:00
|
|
|
}
|
|
|
|
assert!(!fail);
|
|
|
|
}
|
|
|
|
}
|
2013-06-25 18:40:51 +00:00
|
|
|
}
|
|
|
|
}
|
2013-05-21 23:57:21 +00:00
|
|
|
|
2013-07-13 01:35:47 +00:00
|
|
|
#[test] fn fmt_in_macro_used_inside_module_macro() {
|
2014-01-31 02:46:19 +00:00
|
|
|
let crate_str = ~"macro_rules! fmt_wrap(($b:expr)=>($b.to_str()))
|
2013-07-13 01:35:47 +00:00
|
|
|
macro_rules! foo_module (() => (mod generated { fn a() { let xx = 147; fmt_wrap!(xx);}}))
|
|
|
|
foo_module!()
|
|
|
|
";
|
|
|
|
let cr = expand_crate_str(crate_str);
|
|
|
|
// find the xx binding
|
2014-02-28 21:09:09 +00:00
|
|
|
let mut name_finder = new_name_finder(Vec::new());
|
2013-10-28 08:20:26 +00:00
|
|
|
visit::walk_crate(&mut name_finder, &cr, ());
|
|
|
|
let bindings = name_finder.ident_accumulator;
|
|
|
|
|
2014-02-28 21:09:09 +00:00
|
|
|
let cxbinds: Vec<&ast::Ident> =
|
2014-02-13 17:46:46 +00:00
|
|
|
bindings.iter().filter(|b| {
|
|
|
|
let ident = token::get_ident(**b);
|
|
|
|
let string = ident.get();
|
|
|
|
"xx" == string
|
|
|
|
}).collect();
|
2014-02-28 20:54:01 +00:00
|
|
|
let cxbinds: &[&ast::Ident] = cxbinds.as_slice();
|
2013-07-13 01:35:47 +00:00
|
|
|
let cxbind = match cxbinds {
|
|
|
|
[b] => b,
|
2013-10-21 20:08:31 +00:00
|
|
|
_ => fail!("expected just one binding for ext_cx")
|
2013-07-13 01:35:47 +00:00
|
|
|
};
|
2014-02-24 20:47:19 +00:00
|
|
|
let resolved_binding = mtwt::resolve(*cxbind);
|
2013-07-13 01:35:47 +00:00
|
|
|
// find all the xx varrefs:
|
2014-02-28 21:09:09 +00:00
|
|
|
let mut path_finder = new_path_finder(Vec::new());
|
2013-10-28 08:20:26 +00:00
|
|
|
visit::walk_crate(&mut path_finder, &cr, ());
|
|
|
|
let varrefs = path_finder.path_accumulator;
|
|
|
|
|
2013-07-13 01:35:47 +00:00
|
|
|
// the xx binding should bind all of the xx varrefs:
|
2014-02-14 05:07:09 +00:00
|
|
|
for (idx,v) in varrefs.iter().filter(|p| {
|
2014-01-31 23:26:59 +00:00
|
|
|
p.segments.len() == 1
|
2014-02-28 20:54:01 +00:00
|
|
|
&& "xx" == token::get_ident(p.segments.get(0).identifier).get()
|
2014-01-31 23:26:59 +00:00
|
|
|
}).enumerate() {
|
2014-02-24 20:47:19 +00:00
|
|
|
if mtwt::resolve(v.segments.get(0).identifier) != resolved_binding {
|
2014-01-09 10:06:55 +00:00
|
|
|
println!("uh oh, xx binding didn't match xx varref:");
|
2013-10-14 01:48:47 +00:00
|
|
|
println!("this is xx varref \\# {:?}",idx);
|
|
|
|
println!("binding: {:?}",cxbind);
|
|
|
|
println!("resolves to: {:?}",resolved_binding);
|
2014-02-28 20:54:01 +00:00
|
|
|
println!("varref: {:?}",v.segments.get(0).identifier);
|
2013-10-14 01:48:47 +00:00
|
|
|
println!("resolves to: {:?}",
|
2014-02-24 20:47:19 +00:00
|
|
|
mtwt::resolve(v.segments.get(0).identifier));
|
|
|
|
mtwt::with_sctable(|x| mtwt::display_sctable(x));
|
2013-07-13 01:35:47 +00:00
|
|
|
}
|
2014-02-24 20:47:19 +00:00
|
|
|
assert_eq!(mtwt::resolve(v.segments.get(0).identifier),
|
2014-02-28 20:54:01 +00:00
|
|
|
resolved_binding);
|
2013-07-13 01:35:47 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2013-06-04 21:56:33 +00:00
|
|
|
#[test]
|
|
|
|
fn pat_idents(){
|
2014-01-31 02:46:19 +00:00
|
|
|
let pat = string_to_pat(~"(a,Foo{x:c @ (b,9),y:Bar(4,d)})");
|
2014-02-28 21:09:09 +00:00
|
|
|
let mut pat_idents = new_name_finder(Vec::new());
|
2013-07-20 01:42:11 +00:00
|
|
|
pat_idents.visit_pat(pat, ());
|
2013-10-28 08:20:26 +00:00
|
|
|
assert_eq!(pat_idents.ident_accumulator,
|
2014-02-28 21:09:09 +00:00
|
|
|
strs_to_idents(vec!("a","c","b","d")));
|
2013-06-04 21:56:33 +00:00
|
|
|
}
|
2013-06-07 19:28:03 +00:00
|
|
|
|
2013-02-26 18:15:29 +00:00
|
|
|
}
|