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.
|
|
|
|
|
2016-06-22 02:50:05 +00:00
|
|
|
use ast::{Block, Crate, Ident, Mac_, Name, PatKind};
|
2016-02-09 10:36:51 +00:00
|
|
|
use ast::{MacStmtStyle, Mrk, Stmt, StmtKind, ItemKind};
|
2012-12-23 22:41:37 +00:00
|
|
|
use ast;
|
2016-06-12 13:38:39 +00:00
|
|
|
use attr::HasAttrs;
|
2014-02-24 20:47:19 +00:00
|
|
|
use ext::mtwt;
|
2013-02-11 16:36:42 +00:00
|
|
|
use attr;
|
2016-06-26 02:18:04 +00:00
|
|
|
use attr::AttrMetaMethods;
|
2016-06-21 22:08:13 +00:00
|
|
|
use codemap::{Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
|
|
|
|
use syntax_pos::{self, Span, ExpnId};
|
2016-05-16 10:09:23 +00:00
|
|
|
use config::StripUnconfigured;
|
2012-09-04 18:37:29 +00:00
|
|
|
use ext::base::*;
|
2015-12-10 14:23:14 +00:00
|
|
|
use feature_gate::{self, Features};
|
2014-07-03 06:16:01 +00:00
|
|
|
use fold;
|
2012-12-23 22:41:37 +00:00
|
|
|
use fold::*;
|
2016-06-22 02:50:05 +00:00
|
|
|
use parse::token::{fresh_mark, intern, keywords};
|
2014-09-13 16:06:01 +00:00
|
|
|
use ptr::P;
|
2016-06-20 15:49:33 +00:00
|
|
|
use tokenstream::TokenTree;
|
2014-09-13 16:06:01 +00:00
|
|
|
use util::small_vector::SmallVector;
|
2013-06-04 21:56:33 +00:00
|
|
|
use visit;
|
2013-06-08 16:21:11 +00:00
|
|
|
use visit::Visitor;
|
2014-09-07 21:57:26 +00:00
|
|
|
use std_inject;
|
2011-08-05 20:06:11 +00:00
|
|
|
|
2015-11-21 09:37:50 +00:00
|
|
|
use std::collections::HashSet;
|
2015-01-27 00:22:12 +00:00
|
|
|
|
2016-05-24 06:12:54 +00:00
|
|
|
// A trait for AST nodes and AST node lists into which macro invocations may expand.
|
2016-05-19 09:45:37 +00:00
|
|
|
trait MacroGenerable: Sized {
|
2016-05-24 06:12:54 +00:00
|
|
|
// Expand the given MacResult using its appropriate `make_*` method.
|
2016-05-19 09:45:37 +00:00
|
|
|
fn make_with<'a>(result: Box<MacResult + 'a>) -> Option<Self>;
|
2016-05-24 06:12:54 +00:00
|
|
|
|
|
|
|
// Fold this node or list of nodes using the given folder.
|
2016-05-19 09:45:37 +00:00
|
|
|
fn fold_with<F: Folder>(self, folder: &mut F) -> Self;
|
2016-06-26 02:14:27 +00:00
|
|
|
fn visit_with<V: Visitor>(&self, visitor: &mut V);
|
2016-05-24 06:12:54 +00:00
|
|
|
|
|
|
|
// The user-friendly name of the node type (e.g. "expression", "item", etc.) for diagnostics.
|
2016-05-19 09:45:37 +00:00
|
|
|
fn kind_name() -> &'static str;
|
2016-06-17 11:02:42 +00:00
|
|
|
|
|
|
|
// Return a placeholder expansion to allow compilation to continue after an erroring expansion.
|
|
|
|
fn dummy(span: Span) -> Self {
|
|
|
|
Self::make_with(DummyResult::any(span)).unwrap()
|
|
|
|
}
|
2016-05-19 09:45:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! impl_macro_generable {
|
2016-06-11 22:59:33 +00:00
|
|
|
($($ty:ty: $kind_name:expr, .$make:ident,
|
|
|
|
$(.$fold:ident)* $(lift .$fold_elt:ident)*,
|
2016-06-17 11:02:42 +00:00
|
|
|
$(.$visit:ident)* $(lift .$visit_elt:ident)*;)*) => { $(
|
2016-05-19 09:45:37 +00:00
|
|
|
impl MacroGenerable for $ty {
|
|
|
|
fn kind_name() -> &'static str { $kind_name }
|
|
|
|
fn make_with<'a>(result: Box<MacResult + 'a>) -> Option<Self> { result.$make() }
|
|
|
|
fn fold_with<F: Folder>(self, folder: &mut F) -> Self {
|
|
|
|
$( folder.$fold(self) )*
|
|
|
|
$( self.into_iter().flat_map(|item| folder. $fold_elt (item)).collect() )*
|
|
|
|
}
|
2016-06-26 02:14:27 +00:00
|
|
|
fn visit_with<V: Visitor>(&self, visitor: &mut V) {
|
2016-06-11 22:59:33 +00:00
|
|
|
$( visitor.$visit(self) )*
|
|
|
|
$( for item in self.as_slice() { visitor. $visit_elt (item) } )*
|
|
|
|
}
|
2016-05-19 09:45:37 +00:00
|
|
|
}
|
|
|
|
)* }
|
|
|
|
}
|
|
|
|
|
|
|
|
impl_macro_generable! {
|
2016-06-17 11:02:42 +00:00
|
|
|
P<ast::Expr>: "expression", .make_expr, .fold_expr, .visit_expr;
|
|
|
|
P<ast::Pat>: "pattern", .make_pat, .fold_pat, .visit_pat;
|
|
|
|
P<ast::Ty>: "type", .make_ty, .fold_ty, .visit_ty;
|
|
|
|
SmallVector<ast::Stmt>: "statement", .make_stmts, lift .fold_stmt, lift .visit_stmt;
|
|
|
|
SmallVector<P<ast::Item>>: "item", .make_items, lift .fold_item, lift .visit_item;
|
2016-06-25 22:05:08 +00:00
|
|
|
SmallVector<ast::TraitItem>:
|
2016-06-17 11:02:42 +00:00
|
|
|
"trait item", .make_trait_items, lift .fold_trait_item, lift .visit_trait_item;
|
2016-06-11 22:59:33 +00:00
|
|
|
SmallVector<ast::ImplItem>:
|
2016-06-17 11:02:42 +00:00
|
|
|
"impl item", .make_impl_items, lift .fold_impl_item, lift .visit_impl_item;
|
2016-05-19 09:45:37 +00:00
|
|
|
}
|
|
|
|
|
2016-05-16 10:09:23 +00:00
|
|
|
impl MacroGenerable for Option<P<ast::Expr>> {
|
|
|
|
fn kind_name() -> &'static str { "expression" }
|
|
|
|
fn make_with<'a>(result: Box<MacResult + 'a>) -> Option<Self> {
|
|
|
|
result.make_expr().map(Some)
|
|
|
|
}
|
|
|
|
fn fold_with<F: Folder>(self, folder: &mut F) -> Self {
|
|
|
|
self.and_then(|expr| folder.fold_opt_expr(expr))
|
|
|
|
}
|
2016-06-26 02:14:27 +00:00
|
|
|
fn visit_with<V: Visitor>(&self, visitor: &mut V) {
|
2016-06-11 22:59:33 +00:00
|
|
|
self.as_ref().map(|expr| visitor.visit_expr(expr));
|
|
|
|
}
|
2016-05-16 10:09:23 +00:00
|
|
|
}
|
2015-01-27 00:22:12 +00:00
|
|
|
|
2016-06-22 02:50:05 +00:00
|
|
|
pub fn expand_expr(expr: ast::Expr, fld: &mut MacroExpander) -> P<ast::Expr> {
|
2016-05-26 23:48:45 +00:00
|
|
|
match expr.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.
|
2016-02-08 15:05:05 +00:00
|
|
|
ast::ExprKind::Mac(mac) => {
|
2016-06-26 02:18:04 +00:00
|
|
|
return expand_mac_invoc(mac, None, expr.attrs.into(), expr.span, fld);
|
2013-02-18 06:20:36 +00:00
|
|
|
}
|
2016-06-22 02:50:05 +00:00
|
|
|
_ => P(noop_fold_expr(expr, fld)),
|
|
|
|
}
|
2011-07-06 22:22:23 +00:00
|
|
|
}
|
|
|
|
|
2016-05-21 00:19:00 +00:00
|
|
|
/// Expand a macro invocation. Returns the result of expansion.
|
|
|
|
fn expand_mac_invoc<T>(mac: ast::Mac, ident: Option<Ident>, attrs: Vec<ast::Attribute>, span: Span,
|
2016-05-20 23:55:39 +00:00
|
|
|
fld: &mut MacroExpander) -> T
|
|
|
|
where T: MacroGenerable,
|
|
|
|
{
|
2016-05-21 00:19:00 +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.
|
2016-05-21 00:15:00 +00:00
|
|
|
let Mac_ { path, tts, .. } = mac.node;
|
|
|
|
let mark = fresh_mark();
|
|
|
|
|
2016-05-21 00:19:00 +00:00
|
|
|
fn mac_result<'a>(path: &ast::Path, ident: Option<Ident>, tts: Vec<TokenTree>, mark: Mrk,
|
2016-05-21 00:15:00 +00:00
|
|
|
attrs: Vec<ast::Attribute>, call_site: Span, fld: &'a mut MacroExpander)
|
|
|
|
-> Option<Box<MacResult + 'a>> {
|
2016-05-21 01:26:30 +00:00
|
|
|
// Detect use of feature-gated or invalid attributes on macro invoations
|
|
|
|
// since they will not be detected after macro expansion.
|
|
|
|
for attr in attrs.iter() {
|
|
|
|
feature_gate::check_attribute(&attr, &fld.cx.parse_sess.span_diagnostic,
|
|
|
|
&fld.cx.parse_sess.codemap(),
|
|
|
|
&fld.cx.ecfg.features.unwrap());
|
|
|
|
}
|
2016-05-21 00:15:00 +00:00
|
|
|
|
2016-06-27 03:15:06 +00:00
|
|
|
if path.segments.len() > 1 || path.global || !path.segments[0].parameters.is_empty() {
|
2016-05-21 00:15:00 +00:00
|
|
|
fld.cx.span_err(path.span, "expected macro name without module separators");
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
let extname = path.segments[0].identifier.name;
|
|
|
|
let extension = if let Some(extension) = fld.cx.syntax_env.find(extname) {
|
|
|
|
extension
|
|
|
|
} else {
|
|
|
|
let mut err = fld.cx.struct_span_err(path.span,
|
|
|
|
&format!("macro undefined: '{}!'", &extname));
|
2016-04-20 18:44:07 +00:00
|
|
|
fld.cx.suggest_macro_name(&extname.as_str(), &mut err);
|
2015-12-20 21:00:43 +00:00
|
|
|
err.emit();
|
2016-05-21 00:15:00 +00:00
|
|
|
return None;
|
|
|
|
};
|
2014-07-12 22:00:23 +00:00
|
|
|
|
2016-05-24 06:12:54 +00:00
|
|
|
let ident = ident.unwrap_or(keywords::Invalid.ident());
|
2016-05-21 00:15:00 +00:00
|
|
|
match *extension {
|
2015-09-20 20:15:37 +00:00
|
|
|
NormalTT(ref expandfun, exp_span, allow_internal_unstable) => {
|
2016-05-21 00:19:00 +00:00
|
|
|
if ident.name != keywords::Invalid.name() {
|
|
|
|
let msg =
|
|
|
|
format!("macro {}! expects no ident argument, given '{}'", extname, ident);
|
|
|
|
fld.cx.span_err(path.span, &msg);
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
2015-09-20 20:15:37 +00:00
|
|
|
fld.cx.bt_push(ExpnInfo {
|
2016-05-21 00:15:00 +00:00
|
|
|
call_site: call_site,
|
|
|
|
callee: NameAndSpan {
|
|
|
|
format: MacroBang(extname),
|
|
|
|
span: exp_span,
|
|
|
|
allow_internal_unstable: allow_internal_unstable,
|
|
|
|
},
|
|
|
|
});
|
2015-09-20 20:15:37 +00:00
|
|
|
|
2016-07-04 10:25:50 +00:00
|
|
|
let marked_tts = mark_tts(&tts, mark);
|
2016-05-19 11:31:41 +00:00
|
|
|
Some(expandfun.expand(fld.cx, call_site, &marked_tts))
|
2015-09-20 20:15:37 +00:00
|
|
|
}
|
2016-05-21 00:19:00 +00:00
|
|
|
|
|
|
|
IdentTT(ref expander, tt_span, allow_internal_unstable) => {
|
|
|
|
if ident.name == keywords::Invalid.name() {
|
|
|
|
fld.cx.span_err(path.span,
|
|
|
|
&format!("macro {}! expects an ident argument", extname));
|
|
|
|
return None;
|
|
|
|
};
|
|
|
|
|
|
|
|
fld.cx.bt_push(ExpnInfo {
|
|
|
|
call_site: call_site,
|
|
|
|
callee: NameAndSpan {
|
|
|
|
format: MacroBang(extname),
|
|
|
|
span: tt_span,
|
|
|
|
allow_internal_unstable: allow_internal_unstable,
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2016-07-04 10:25:50 +00:00
|
|
|
let marked_tts = mark_tts(&tts, mark);
|
2016-05-21 00:19:00 +00:00
|
|
|
Some(expander.expand(fld.cx, call_site, ident, marked_tts))
|
|
|
|
}
|
|
|
|
|
|
|
|
MacroRulesTT => {
|
|
|
|
if ident.name == keywords::Invalid.name() {
|
|
|
|
fld.cx.span_err(path.span,
|
|
|
|
&format!("macro {}! expects an ident argument", extname));
|
|
|
|
return None;
|
|
|
|
};
|
|
|
|
|
|
|
|
fld.cx.bt_push(ExpnInfo {
|
|
|
|
call_site: call_site,
|
|
|
|
callee: NameAndSpan {
|
|
|
|
format: MacroBang(extname),
|
|
|
|
span: None,
|
|
|
|
// `macro_rules!` doesn't directly allow unstable
|
|
|
|
// (this is orthogonal to whether the macro it creates allows it)
|
|
|
|
allow_internal_unstable: false,
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// DON'T mark before expansion.
|
|
|
|
fld.cx.insert_macro(ast::MacroDef {
|
|
|
|
ident: ident,
|
|
|
|
id: ast::DUMMY_NODE_ID,
|
|
|
|
span: call_site,
|
|
|
|
imported_from: None,
|
|
|
|
use_locally: true,
|
|
|
|
body: tts,
|
|
|
|
export: attr::contains_name(&attrs, "macro_export"),
|
|
|
|
allow_internal_unstable: attr::contains_name(&attrs, "allow_internal_unstable"),
|
|
|
|
attrs: attrs,
|
|
|
|
});
|
|
|
|
|
|
|
|
// macro_rules! has a side effect but expands to nothing.
|
|
|
|
fld.cx.bt_pop();
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
MultiDecorator(..) | MultiModifier(..) => {
|
2016-05-21 00:15:00 +00:00
|
|
|
fld.cx.span_err(path.span,
|
2016-05-21 00:19:00 +00:00
|
|
|
&format!("`{}` can only be used in attributes", extname));
|
2016-05-21 00:15:00 +00:00
|
|
|
None
|
2014-07-12 22:00:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-05-21 00:15:00 +00:00
|
|
|
|
2016-05-21 00:19:00 +00:00
|
|
|
let opt_expanded = T::make_with(match mac_result(&path, ident, tts, mark, attrs, span, fld) {
|
2016-05-21 00:15:00 +00:00
|
|
|
Some(result) => result,
|
|
|
|
None => return T::dummy(span),
|
|
|
|
});
|
|
|
|
|
|
|
|
let expanded = if let Some(expanded) = opt_expanded {
|
|
|
|
expanded
|
|
|
|
} else {
|
|
|
|
let msg = format!("non-{kind} macro in {kind} position: {name}",
|
|
|
|
name = path.segments[0].identifier.name, kind = T::kind_name());
|
|
|
|
fld.cx.span_err(path.span, &msg);
|
|
|
|
return T::dummy(span);
|
|
|
|
};
|
|
|
|
|
2016-05-17 21:20:09 +00:00
|
|
|
let marked = expanded.fold_with(&mut Marker { mark: mark, expn_id: Some(fld.cx.backtrace()) });
|
2016-05-16 10:09:23 +00:00
|
|
|
let configured = marked.fold_with(&mut fld.strip_unconfigured());
|
2016-06-12 01:50:52 +00:00
|
|
|
fld.load_macros(&configured);
|
2016-05-16 10:09:23 +00:00
|
|
|
let fully_expanded = configured.fold_with(fld);
|
2016-05-21 00:15:00 +00:00
|
|
|
fld.cx.bt_pop();
|
|
|
|
fully_expanded
|
2014-07-12 22:00:23 +00:00
|
|
|
}
|
|
|
|
|
2014-07-04 19:05:43 +00:00
|
|
|
// eval $e with a new exts frame.
|
|
|
|
// must be a macro so that $e isn't evaluated too early.
|
2014-11-14 17:18:10 +00:00
|
|
|
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
|
|
|
|
})
|
2014-11-14 17:18:10 +00:00
|
|
|
}
|
2013-02-26 18:15:29 +00:00
|
|
|
|
2012-07-27 18:19:21 +00:00
|
|
|
// When we enter a module, record it, for the sake of `module!`
|
2014-09-13 16:06:01 +00:00
|
|
|
pub fn expand_item(it: P<ast::Item>, fld: &mut MacroExpander)
|
|
|
|
-> SmallVector<P<ast::Item>> {
|
2016-04-09 05:37:31 +00:00
|
|
|
expand_annotatable(Annotatable::Item(it), fld)
|
2014-12-02 18:07:41 +00:00
|
|
|
.into_iter().map(|i| i.expect_item()).collect()
|
2012-05-18 17:00:49 +00:00
|
|
|
}
|
|
|
|
|
2014-12-19 04:48:26 +00:00
|
|
|
// does this attribute list contain "macro_use" ?
|
|
|
|
fn contains_macro_use(fld: &mut MacroExpander, attrs: &[ast::Attribute]) -> bool {
|
2015-01-31 17:20:46 +00:00
|
|
|
for attr in attrs {
|
2014-12-19 04:48:26 +00:00
|
|
|
let mut is_use = attr.check_name("macro_use");
|
|
|
|
if attr.check_name("macro_escape") {
|
2015-12-20 21:00:43 +00:00
|
|
|
let mut err =
|
|
|
|
fld.cx.struct_span_warn(attr.span,
|
|
|
|
"macro_escape is a deprecated synonym for macro_use");
|
2014-12-19 04:48:26 +00:00
|
|
|
is_use = true;
|
2015-10-01 16:03:34 +00:00
|
|
|
if let ast::AttrStyle::Inner = attr.node.style {
|
2016-04-20 18:44:07 +00:00
|
|
|
err.help("consider an outer attribute, \
|
|
|
|
#[macro_use] mod ...").emit();
|
2015-12-20 21:00:43 +00:00
|
|
|
} else {
|
|
|
|
err.emit();
|
2014-12-19 04:48:26 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if is_use {
|
|
|
|
match attr.node.value.node {
|
2016-02-09 11:05:20 +00:00
|
|
|
ast::MetaItemKind::Word(..) => (),
|
2014-12-19 04:48:26 +00:00
|
|
|
_ => fld.cx.span_err(attr.span, "arguments to macro_use are not allowed here"),
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
false
|
2013-02-26 18:15:29 +00:00
|
|
|
}
|
|
|
|
|
2014-07-12 22:00:23 +00:00
|
|
|
/// Expand a stmt
|
2016-02-11 20:33:09 +00:00
|
|
|
fn expand_stmt(stmt: Stmt, fld: &mut MacroExpander) -> SmallVector<Stmt> {
|
2015-11-03 16:39:51 +00:00
|
|
|
let (mac, style, attrs) = match stmt.node {
|
2016-06-17 02:30:01 +00:00
|
|
|
StmtKind::Mac(mac) => mac.unwrap(),
|
2016-06-22 02:50:05 +00:00
|
|
|
_ => return noop_fold_stmt(stmt, fld)
|
2012-12-12 20:25:40 +00:00
|
|
|
};
|
2015-04-07 13:21:18 +00:00
|
|
|
|
2016-05-20 23:35:56 +00:00
|
|
|
let mut fully_expanded: SmallVector<ast::Stmt> =
|
2016-06-26 02:18:04 +00:00
|
|
|
expand_mac_invoc(mac, None, attrs.into(), stmt.span, fld);
|
2015-04-07 13:21:18 +00:00
|
|
|
|
|
|
|
// If this is a macro invocation with a semicolon, then apply that
|
|
|
|
// semicolon to the final statement produced by expansion.
|
2016-02-09 10:56:59 +00:00
|
|
|
if style == MacStmtStyle::Semicolon {
|
2015-04-11 04:06:34 +00:00
|
|
|
if let Some(stmt) = fully_expanded.pop() {
|
2016-06-17 02:30:01 +00:00
|
|
|
fully_expanded.push(Stmt {
|
|
|
|
id: stmt.id,
|
2016-02-11 20:33:09 +00:00
|
|
|
node: match stmt.node {
|
2016-06-17 02:30:01 +00:00
|
|
|
StmtKind::Expr(expr) => StmtKind::Semi(expr),
|
2016-02-11 20:33:09 +00:00
|
|
|
_ => stmt.node /* might already have a semi */
|
|
|
|
},
|
2016-06-17 02:30:01 +00:00
|
|
|
span: stmt.span,
|
|
|
|
});
|
2015-04-11 02:50:23 +00:00
|
|
|
}
|
2014-09-13 16:06:01 +00:00
|
|
|
}
|
2015-04-11 02:50:23 +00:00
|
|
|
|
|
|
|
fully_expanded
|
2012-11-20 01:31:22 +00:00
|
|
|
}
|
|
|
|
|
2014-09-13 16:06:01 +00:00
|
|
|
fn expand_pat(p: P<ast::Pat>, fld: &mut MacroExpander) -> P<ast::Pat> {
|
|
|
|
match p.node {
|
2016-02-11 18:16:33 +00:00
|
|
|
PatKind::Mac(_) => {}
|
2014-09-13 16:06:01 +00:00
|
|
|
_ => return noop_fold_pat(p, fld)
|
2014-05-19 20:59:35 +00:00
|
|
|
}
|
2016-05-19 10:18:54 +00:00
|
|
|
p.and_then(|ast::Pat {node, span, ..}| {
|
|
|
|
match node {
|
2016-05-21 00:19:00 +00:00
|
|
|
PatKind::Mac(mac) => expand_mac_invoc(mac, None, Vec::new(), span, fld),
|
2014-09-13 16:06:01 +00:00
|
|
|
_ => unreachable!()
|
|
|
|
}
|
|
|
|
})
|
2014-05-19 20:59:35 +00:00
|
|
|
}
|
|
|
|
|
2016-06-12 13:38:39 +00:00
|
|
|
fn expand_multi_modified(a: Annotatable, fld: &mut MacroExpander) -> SmallVector<Annotatable> {
|
2016-06-24 03:25:37 +00:00
|
|
|
match a {
|
2014-12-02 18:07:41 +00:00
|
|
|
Annotatable::Item(it) => match it.node {
|
2016-02-09 10:36:51 +00:00
|
|
|
ast::ItemKind::Mac(..) => {
|
2016-06-09 00:47:52 +00:00
|
|
|
it.and_then(|it| match it.node {
|
2016-05-21 00:19:00 +00:00
|
|
|
ItemKind::Mac(mac) =>
|
|
|
|
expand_mac_invoc(mac, Some(it.ident), it.attrs, it.span, fld),
|
|
|
|
_ => unreachable!(),
|
2016-06-09 00:47:52 +00:00
|
|
|
})
|
2014-12-02 18:07:41 +00:00
|
|
|
}
|
2016-02-09 10:36:51 +00:00
|
|
|
ast::ItemKind::Mod(_) | ast::ItemKind::ForeignMod(_) => {
|
2014-12-02 18:07:41 +00:00
|
|
|
let valid_ident =
|
2016-04-18 19:53:50 +00:00
|
|
|
it.ident.name != keywords::Invalid.name();
|
2014-12-02 18:07:41 +00:00
|
|
|
|
|
|
|
if valid_ident {
|
|
|
|
fld.cx.mod_push(it.ident);
|
|
|
|
}
|
2016-06-01 10:25:19 +00:00
|
|
|
let macro_use = contains_macro_use(fld, &it.attrs);
|
2014-12-02 18:07:41 +00:00
|
|
|
let result = with_exts_frame!(fld.cx.syntax_env,
|
|
|
|
macro_use,
|
|
|
|
noop_fold_item(it, fld));
|
|
|
|
if valid_ident {
|
|
|
|
fld.cx.mod_pop();
|
|
|
|
}
|
2016-06-09 00:47:52 +00:00
|
|
|
result
|
2014-12-02 18:07:41 +00:00
|
|
|
},
|
2016-06-09 00:47:52 +00:00
|
|
|
_ => noop_fold_item(it, fld),
|
|
|
|
}.into_iter().map(|i| Annotatable::Item(i)).collect(),
|
2015-03-11 21:38:58 +00:00
|
|
|
|
2016-06-11 01:00:07 +00:00
|
|
|
Annotatable::TraitItem(it) => {
|
|
|
|
expand_trait_item(it.unwrap(), fld).into_iter().
|
|
|
|
map(|it| Annotatable::TraitItem(P(it))).collect()
|
|
|
|
}
|
2015-03-11 21:38:58 +00:00
|
|
|
|
2015-03-10 10:28:44 +00:00
|
|
|
Annotatable::ImplItem(ii) => {
|
2016-02-11 20:33:09 +00:00
|
|
|
expand_impl_item(ii.unwrap(), fld).into_iter().
|
|
|
|
map(|ii| Annotatable::ImplItem(P(ii))).collect()
|
2014-12-02 18:07:41 +00:00
|
|
|
}
|
2015-04-28 05:34:39 +00:00
|
|
|
}
|
2014-12-02 18:07:41 +00:00
|
|
|
}
|
|
|
|
|
2016-06-12 13:38:39 +00:00
|
|
|
fn expand_annotatable(mut item: Annotatable, fld: &mut MacroExpander) -> SmallVector<Annotatable> {
|
|
|
|
let mut multi_modifier = None;
|
|
|
|
item = item.map_attrs(|mut attrs| {
|
|
|
|
for i in 0..attrs.len() {
|
|
|
|
if let Some(extension) = fld.cx.syntax_env.find(intern(&attrs[i].name())) {
|
2016-06-24 03:25:37 +00:00
|
|
|
match *extension {
|
|
|
|
MultiModifier(..) | MultiDecorator(..) => {
|
|
|
|
multi_modifier = Some((attrs.remove(i), extension));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
_ => {}
|
2014-12-02 18:07:41 +00:00
|
|
|
}
|
2016-06-12 13:38:39 +00:00
|
|
|
}
|
2014-12-02 18:07:41 +00:00
|
|
|
}
|
2016-06-12 13:38:39 +00:00
|
|
|
attrs
|
|
|
|
});
|
2014-12-02 18:07:41 +00:00
|
|
|
|
2016-06-12 13:38:39 +00:00
|
|
|
match multi_modifier {
|
|
|
|
None => expand_multi_modified(item, fld),
|
2016-06-24 03:25:37 +00:00
|
|
|
Some((attr, extension)) => {
|
|
|
|
attr::mark_used(&attr);
|
|
|
|
fld.cx.bt_push(ExpnInfo {
|
|
|
|
call_site: attr.span,
|
|
|
|
callee: NameAndSpan {
|
|
|
|
format: MacroAttribute(intern(&attr.name())),
|
|
|
|
span: Some(attr.span),
|
|
|
|
// attributes can do whatever they like, for now
|
|
|
|
allow_internal_unstable: true,
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
let modified = match *extension {
|
|
|
|
MultiModifier(ref mac) => mac.expand(fld.cx, attr.span, &attr.node.value, item),
|
|
|
|
MultiDecorator(ref mac) => {
|
|
|
|
let mut items = Vec::new();
|
|
|
|
mac.expand(fld.cx, attr.span, &attr.node.value, &item,
|
|
|
|
&mut |item| items.push(item));
|
|
|
|
items.push(item);
|
|
|
|
items
|
|
|
|
}
|
|
|
|
_ => unreachable!(),
|
|
|
|
};
|
|
|
|
|
|
|
|
fld.cx.bt_pop();
|
2016-07-04 03:10:04 +00:00
|
|
|
let configured = modified.into_iter().flat_map(|it| {
|
|
|
|
it.fold_with(&mut fld.strip_unconfigured())
|
|
|
|
}).collect::<SmallVector<_>>();
|
|
|
|
|
|
|
|
configured.into_iter().flat_map(|it| expand_annotatable(it, fld)).collect()
|
2016-06-12 13:38:39 +00:00
|
|
|
}
|
|
|
|
}
|
2014-12-02 18:07:41 +00:00
|
|
|
}
|
|
|
|
|
2016-02-11 20:33:09 +00:00
|
|
|
fn expand_impl_item(ii: ast::ImplItem, fld: &mut MacroExpander)
|
|
|
|
-> SmallVector<ast::ImplItem> {
|
2015-03-10 10:28:44 +00:00
|
|
|
match ii.node {
|
2016-04-06 22:43:03 +00:00
|
|
|
ast::ImplItemKind::Macro(mac) => {
|
2016-05-21 00:19:00 +00:00
|
|
|
expand_mac_invoc(mac, None, ii.attrs, ii.span, fld)
|
2014-07-12 04:22:11 +00:00
|
|
|
}
|
2015-03-11 21:38:58 +00:00
|
|
|
_ => fold::noop_fold_impl_item(ii, fld)
|
2015-03-05 02:48:54 +00:00
|
|
|
}
|
2014-07-04 18:24:28 +00:00
|
|
|
}
|
|
|
|
|
2016-06-11 01:00:07 +00:00
|
|
|
fn expand_trait_item(ti: ast::TraitItem, fld: &mut MacroExpander)
|
|
|
|
-> SmallVector<ast::TraitItem> {
|
|
|
|
match ti.node {
|
|
|
|
ast::TraitItemKind::Macro(mac) => {
|
|
|
|
expand_mac_invoc(mac, None, ti.attrs, ti.span, fld)
|
|
|
|
}
|
|
|
|
_ => fold::noop_fold_trait_item(ti, fld)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-26 04:54:19 +00:00
|
|
|
pub fn expand_type(t: P<ast::Ty>, fld: &mut MacroExpander) -> P<ast::Ty> {
|
|
|
|
let t = match t.node.clone() {
|
2016-02-08 15:53:21 +00:00
|
|
|
ast::TyKind::Mac(mac) => {
|
2015-07-28 16:00:32 +00:00
|
|
|
if fld.cx.ecfg.features.unwrap().type_macros {
|
2016-05-21 00:19:00 +00:00
|
|
|
expand_mac_invoc(mac, None, Vec::new(), t.span, fld)
|
2015-07-28 16:00:32 +00:00
|
|
|
} else {
|
|
|
|
feature_gate::emit_feature_err(
|
|
|
|
&fld.cx.parse_sess.span_diagnostic,
|
|
|
|
"type_macros",
|
|
|
|
t.span,
|
2015-09-04 23:37:22 +00:00
|
|
|
feature_gate::GateIssue::Language,
|
|
|
|
"type macros are experimental");
|
2015-08-04 23:04:26 +00:00
|
|
|
|
|
|
|
DummyResult::raw_ty(t.span)
|
2015-07-28 16:00:32 +00:00
|
|
|
}
|
2015-07-26 04:54:19 +00:00
|
|
|
}
|
|
|
|
_ => t
|
|
|
|
};
|
2015-07-28 16:00:32 +00:00
|
|
|
|
2015-07-26 04:54:19 +00:00
|
|
|
fold::noop_fold_ty(t, fld)
|
|
|
|
}
|
|
|
|
|
2014-07-03 06:16:01 +00:00
|
|
|
/// A tree-folder that performs macro expansion
|
2014-08-28 01:46:52 +00:00
|
|
|
pub struct MacroExpander<'a, 'b:'a> {
|
2014-03-27 22:39:48 +00:00
|
|
|
pub cx: &'a mut ExtCtxt<'b>,
|
2014-12-14 02:42:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, 'b> MacroExpander<'a, 'b> {
|
|
|
|
pub fn new(cx: &'a mut ExtCtxt<'b>) -> MacroExpander<'a, 'b> {
|
2015-04-03 04:13:52 +00:00
|
|
|
MacroExpander { cx: cx }
|
2014-12-14 02:42:41 +00:00
|
|
|
}
|
2016-05-16 10:09:23 +00:00
|
|
|
|
|
|
|
fn strip_unconfigured(&mut self) -> StripUnconfigured {
|
2016-06-11 01:37:24 +00:00
|
|
|
StripUnconfigured {
|
|
|
|
config: &self.cx.cfg,
|
|
|
|
should_test: self.cx.ecfg.should_test,
|
|
|
|
sess: self.cx.parse_sess,
|
|
|
|
features: self.cx.ecfg.features,
|
|
|
|
}
|
2016-05-16 10:09:23 +00:00
|
|
|
}
|
2016-06-12 01:50:52 +00:00
|
|
|
|
|
|
|
fn load_macros<T: MacroGenerable>(&mut self, node: &T) {
|
|
|
|
struct MacroLoadingVisitor<'a, 'b: 'a>{
|
|
|
|
cx: &'a mut ExtCtxt<'b>,
|
|
|
|
at_crate_root: bool,
|
|
|
|
}
|
|
|
|
|
2016-06-26 02:14:27 +00:00
|
|
|
impl<'a, 'b> Visitor for MacroLoadingVisitor<'a, 'b> {
|
|
|
|
fn visit_mac(&mut self, _: &ast::Mac) {}
|
|
|
|
fn visit_item(&mut self, item: &ast::Item) {
|
2016-06-12 01:50:52 +00:00
|
|
|
if let ast::ItemKind::ExternCrate(..) = item.node {
|
|
|
|
// We need to error on `#[macro_use] extern crate` when it isn't at the
|
|
|
|
// crate root, because `$crate` won't work properly.
|
|
|
|
for def in self.cx.loader.load_crate(item, self.at_crate_root) {
|
|
|
|
self.cx.insert_macro(def);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
let at_crate_root = ::std::mem::replace(&mut self.at_crate_root, false);
|
|
|
|
visit::walk_item(self, item);
|
|
|
|
self.at_crate_root = at_crate_root;
|
|
|
|
}
|
|
|
|
}
|
2016-06-26 02:14:27 +00:00
|
|
|
fn visit_block(&mut self, block: &ast::Block) {
|
2016-06-12 01:50:52 +00:00
|
|
|
let at_crate_root = ::std::mem::replace(&mut self.at_crate_root, false);
|
|
|
|
visit::walk_block(self, block);
|
|
|
|
self.at_crate_root = at_crate_root;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
node.visit_with(&mut MacroLoadingVisitor {
|
|
|
|
at_crate_root: self.cx.syntax_env.is_crate_root(),
|
|
|
|
cx: self.cx,
|
|
|
|
});
|
|
|
|
}
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
|
|
|
|
2014-03-09 14:54:34 +00:00
|
|
|
impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
|
2016-03-01 09:28:42 +00:00
|
|
|
fn fold_crate(&mut self, c: Crate) -> Crate {
|
|
|
|
self.cx.filename = Some(self.cx.parse_sess.codemap().span_to_filename(c.span));
|
|
|
|
noop_fold_crate(c, self)
|
|
|
|
}
|
|
|
|
|
2014-09-13 16:06:01 +00:00
|
|
|
fn fold_expr(&mut self, expr: P<ast::Expr>) -> P<ast::Expr> {
|
2016-05-26 23:48:45 +00:00
|
|
|
expr.and_then(|expr| expand_expr(expr, self))
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
|
|
|
|
2016-05-16 10:09:23 +00:00
|
|
|
fn fold_opt_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
|
2016-05-26 23:48:45 +00:00
|
|
|
expr.and_then(|expr| match expr.node {
|
2016-05-16 10:09:23 +00:00
|
|
|
ast::ExprKind::Mac(mac) =>
|
2016-06-18 04:01:57 +00:00
|
|
|
expand_mac_invoc(mac, None, expr.attrs.into(), expr.span, self),
|
2016-05-26 23:48:45 +00:00
|
|
|
_ => Some(expand_expr(expr, self)),
|
2016-05-16 10:09:23 +00:00
|
|
|
})
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
|
|
|
|
2014-09-13 16:06:01 +00:00
|
|
|
fn fold_pat(&mut self, pat: P<ast::Pat>) -> P<ast::Pat> {
|
2014-05-19 20:59:35 +00:00
|
|
|
expand_pat(pat, self)
|
|
|
|
}
|
|
|
|
|
2014-09-13 16:06:01 +00:00
|
|
|
fn fold_item(&mut self, item: P<ast::Item>) -> SmallVector<P<ast::Item>> {
|
2016-03-01 09:28:42 +00:00
|
|
|
use std::mem::replace;
|
|
|
|
let result;
|
|
|
|
if let ast::ItemKind::Mod(ast::Mod { inner, .. }) = item.node {
|
|
|
|
if item.span.contains(inner) {
|
|
|
|
self.push_mod_path(item.ident, &item.attrs);
|
|
|
|
result = expand_item(item, self);
|
|
|
|
self.pop_mod_path();
|
|
|
|
} else {
|
2016-06-21 22:08:13 +00:00
|
|
|
let filename = if inner != syntax_pos::DUMMY_SP {
|
2016-03-01 09:28:42 +00:00
|
|
|
Some(self.cx.parse_sess.codemap().span_to_filename(inner))
|
|
|
|
} else { None };
|
|
|
|
let orig_filename = replace(&mut self.cx.filename, filename);
|
|
|
|
let orig_mod_path_stack = replace(&mut self.cx.mod_path_stack, Vec::new());
|
|
|
|
result = expand_item(item, self);
|
|
|
|
self.cx.filename = orig_filename;
|
|
|
|
self.cx.mod_path_stack = orig_mod_path_stack;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
result = expand_item(item, self);
|
|
|
|
}
|
|
|
|
result
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
|
|
|
|
2016-02-11 20:33:09 +00:00
|
|
|
fn fold_stmt(&mut self, stmt: ast::Stmt) -> SmallVector<ast::Stmt> {
|
2015-04-07 13:21:18 +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> {
|
2016-03-01 09:28:42 +00:00
|
|
|
let was_in_block = ::std::mem::replace(&mut self.cx.in_block, true);
|
2016-06-22 02:50:05 +00:00
|
|
|
let result = with_exts_frame!(self.cx.syntax_env, false, noop_fold_block(block, self));
|
2016-03-01 09:28:42 +00:00
|
|
|
self.cx.in_block = was_in_block;
|
|
|
|
result
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
|
|
|
|
2016-02-11 20:33:09 +00:00
|
|
|
fn fold_trait_item(&mut self, i: ast::TraitItem) -> SmallVector<ast::TraitItem> {
|
|
|
|
expand_annotatable(Annotatable::TraitItem(P(i)), self)
|
2015-03-10 10:28:44 +00:00
|
|
|
.into_iter().map(|i| i.expect_trait_item()).collect()
|
2014-12-02 18:07:41 +00:00
|
|
|
}
|
|
|
|
|
2016-02-11 20:33:09 +00:00
|
|
|
fn fold_impl_item(&mut self, i: ast::ImplItem) -> SmallVector<ast::ImplItem> {
|
|
|
|
expand_annotatable(Annotatable::ImplItem(P(i)), self)
|
2015-03-10 10:28:44 +00:00
|
|
|
.into_iter().map(|i| i.expect_impl_item()).collect()
|
2014-07-04 18:24:28 +00:00
|
|
|
}
|
|
|
|
|
2015-07-26 04:54:19 +00:00
|
|
|
fn fold_ty(&mut self, ty: P<ast::Ty>) -> P<ast::Ty> {
|
|
|
|
expand_type(ty, self)
|
|
|
|
}
|
2013-07-16 05:05:50 +00:00
|
|
|
}
|
|
|
|
|
2016-03-01 09:28:42 +00:00
|
|
|
impl<'a, 'b> MacroExpander<'a, 'b> {
|
|
|
|
fn push_mod_path(&mut self, id: Ident, attrs: &[ast::Attribute]) {
|
|
|
|
let default_path = id.name.as_str();
|
|
|
|
let file_path = match ::attr::first_attr_value_str_by_name(attrs, "path") {
|
|
|
|
Some(d) => d,
|
|
|
|
None => default_path,
|
|
|
|
};
|
|
|
|
self.cx.mod_path_stack.push(file_path)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn pop_mod_path(&mut self) {
|
|
|
|
self.cx.mod_path_stack.pop().unwrap();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-15 20:30:45 +00:00
|
|
|
pub struct ExpansionConfig<'feat> {
|
2014-06-06 20:21:18 +00:00
|
|
|
pub crate_name: String,
|
2015-02-15 20:30:45 +00:00
|
|
|
pub features: Option<&'feat Features>,
|
2015-01-17 23:33:05 +00:00
|
|
|
pub recursion_limit: usize,
|
2015-04-14 13:36:38 +00:00
|
|
|
pub trace_mac: bool,
|
2016-06-01 01:27:12 +00:00
|
|
|
pub should_test: bool, // If false, strip `#[test]` nodes
|
2014-09-27 00:14:23 +00:00
|
|
|
}
|
|
|
|
|
2015-03-06 20:56:28 +00:00
|
|
|
macro_rules! feature_tests {
|
|
|
|
($( fn $getter:ident = $field:ident, )*) => {
|
|
|
|
$(
|
|
|
|
pub fn $getter(&self) -> bool {
|
|
|
|
match self.features {
|
|
|
|
Some(&Features { $field: true, .. }) => true,
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)*
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-15 20:30:45 +00:00
|
|
|
impl<'feat> ExpansionConfig<'feat> {
|
|
|
|
pub fn default(crate_name: String) -> ExpansionConfig<'static> {
|
2014-09-27 00:14:23 +00:00
|
|
|
ExpansionConfig {
|
|
|
|
crate_name: crate_name,
|
2015-02-15 20:30:45 +00:00
|
|
|
features: None,
|
2014-10-01 09:38:54 +00:00
|
|
|
recursion_limit: 64,
|
2015-04-14 13:36:38 +00:00
|
|
|
trace_mac: false,
|
2016-06-01 01:27:12 +00:00
|
|
|
should_test: false,
|
2014-09-27 00:14:23 +00:00
|
|
|
}
|
|
|
|
}
|
2015-02-15 20:30:45 +00:00
|
|
|
|
2015-03-06 20:56:28 +00:00
|
|
|
feature_tests! {
|
2016-04-04 15:08:41 +00:00
|
|
|
fn enable_quotes = quote,
|
|
|
|
fn enable_asm = asm,
|
|
|
|
fn enable_log_syntax = log_syntax,
|
|
|
|
fn enable_concat_idents = concat_idents,
|
|
|
|
fn enable_trace_macros = trace_macros,
|
2015-03-06 20:56:28 +00:00
|
|
|
fn enable_allow_internal_unstable = allow_internal_unstable,
|
2016-04-04 15:08:41 +00:00
|
|
|
fn enable_custom_derive = custom_derive,
|
|
|
|
fn enable_pushpop_unsafe = pushpop_unsafe,
|
Add #[allow_internal_unstable] to track stability for macros better.
Unstable items used in a macro expansion will now always trigger
stability warnings, *unless* the unstable items are directly inside a
macro marked with `#[allow_internal_unstable]`. IOW, the compiler warns
unless the span of the unstable item is a subspan of the definition of a
macro marked with that attribute.
E.g.
#[allow_internal_unstable]
macro_rules! foo {
($e: expr) => {{
$e;
unstable(); // no warning
only_called_by_foo!();
}}
}
macro_rules! only_called_by_foo {
() => { unstable() } // warning
}
foo!(unstable()) // warning
The unstable inside `foo` is fine, due to the attribute. But the
`unstable` inside `only_called_by_foo` is not, since that macro doesn't
have the attribute, and the `unstable` passed into `foo` is also not
fine since it isn't contained in the macro itself (that is, even though
it is only used directly in the macro).
In the process this makes the stability tracking much more precise,
e.g. previously `println!("{}", unstable())` got no warning, but now it
does. As such, this is a bug fix that may cause [breaking-change]s.
The attribute is definitely feature gated, since it explicitly allows
side-stepping the feature gating system.
2015-03-01 03:09:28 +00:00
|
|
|
}
|
2014-03-01 07:17:38 +00:00
|
|
|
}
|
|
|
|
|
2015-12-10 14:23:14 +00:00
|
|
|
pub fn expand_crate(mut cx: ExtCtxt,
|
|
|
|
user_exts: Vec<NamedSyntaxExtension>,
|
2016-06-12 01:50:52 +00:00
|
|
|
mut c: Crate) -> (Crate, HashSet<Name>) {
|
2015-07-30 00:01:14 +00:00
|
|
|
if std_inject::no_core(&c) {
|
|
|
|
cx.crate_root = None;
|
|
|
|
} else if std_inject::no_std(&c) {
|
|
|
|
cx.crate_root = Some("core");
|
|
|
|
} else {
|
|
|
|
cx.crate_root = Some("std");
|
|
|
|
}
|
2015-11-21 09:37:50 +00:00
|
|
|
let ret = {
|
|
|
|
let mut expander = MacroExpander::new(&mut cx);
|
2014-09-07 21:57:26 +00:00
|
|
|
|
2015-11-21 09:37:50 +00:00
|
|
|
for (name, extension) in user_exts {
|
|
|
|
expander.cx.syntax_env.insert(name, extension);
|
|
|
|
}
|
2014-05-24 23:16:10 +00:00
|
|
|
|
2016-06-12 01:50:52 +00:00
|
|
|
let items = SmallVector::many(c.module.items);
|
|
|
|
expander.load_macros(&items);
|
|
|
|
c.module.items = items.into();
|
|
|
|
|
2016-01-20 09:07:33 +00:00
|
|
|
let err_count = cx.parse_sess.span_diagnostic.err_count();
|
2015-11-21 09:37:50 +00:00
|
|
|
let mut ret = expander.fold_crate(c);
|
|
|
|
ret.exported_macros = expander.cx.exported_macros.clone();
|
2016-01-20 09:07:33 +00:00
|
|
|
|
|
|
|
if cx.parse_sess.span_diagnostic.err_count() > err_count {
|
|
|
|
cx.parse_sess.span_diagnostic.abort_if_errors();
|
|
|
|
}
|
|
|
|
|
2015-11-21 09:37:50 +00:00
|
|
|
ret
|
|
|
|
};
|
|
|
|
return (ret, cx.syntax_env.names);
|
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
|
|
|
|
|
2016-05-17 21:20:09 +00:00
|
|
|
// A Marker adds the given mark to the syntax context and
|
|
|
|
// sets spans' `expn_id` to the given expn_id (unless it is `None`).
|
|
|
|
struct Marker { mark: Mrk, expn_id: Option<ExpnId> }
|
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 {
|
2015-09-24 20:05:02 +00:00
|
|
|
ast::Ident::new(id.name, mtwt::apply_mark(self.mark, id.ctxt))
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
2014-09-13 16:06:01 +00:00
|
|
|
fn fold_mac(&mut self, Spanned {node, span}: ast::Mac) -> ast::Mac {
|
2013-08-29 19:10:02 +00:00
|
|
|
Spanned {
|
2015-09-20 20:15:37 +00:00
|
|
|
node: Mac_ {
|
|
|
|
path: self.fold_path(node.path),
|
2016-07-04 10:25:50 +00:00
|
|
|
tts: self.fold_tts(&node.tts),
|
2014-09-13 16:06:01 +00:00
|
|
|
},
|
2016-05-17 21:20:09 +00:00
|
|
|
span: self.new_span(span),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn new_span(&mut self, mut span: Span) -> Span {
|
|
|
|
if let Some(expn_id) = self.expn_id {
|
|
|
|
span.expn_id = expn_id;
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
2016-05-17 21:20:09 +00:00
|
|
|
span
|
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.
|
2016-07-04 10:25:50 +00:00
|
|
|
fn mark_tts(tts: &[TokenTree], m: Mrk) -> Vec<TokenTree> {
|
2016-05-17 21:20:09 +00:00
|
|
|
noop_fold_tts(tts, &mut Marker{mark:m, expn_id: None})
|
2013-06-29 12:59:08 +00:00
|
|
|
}
|
|
|
|
|
2014-07-09 23:41:13 +00:00
|
|
|
|
2013-02-26 18:15:29 +00:00
|
|
|
#[cfg(test)]
|
2015-04-24 15:30:41 +00:00
|
|
|
mod tests {
|
2015-01-08 02:53:58 +00:00
|
|
|
use super::{pattern_bindings, expand_crate};
|
2014-09-27 00:14:23 +00:00
|
|
|
use super::{PatIdentFinder, IdentRenamer, PatIdentRenamer, ExpansionConfig};
|
2013-02-25 19:11:21 +00:00
|
|
|
use ast;
|
2015-01-27 03:56:50 +00:00
|
|
|
use ast::Name;
|
2016-06-22 16:50:19 +00:00
|
|
|
use syntax_pos;
|
2016-06-02 01:14:33 +00:00
|
|
|
use ext::base::{ExtCtxt, DummyMacroLoader};
|
2014-02-24 20:47:19 +00:00
|
|
|
use ext::mtwt;
|
2014-07-04 19:18:37 +00:00
|
|
|
use fold::Folder;
|
2013-02-25 19:11:21 +00:00
|
|
|
use parse;
|
2016-06-20 10:51:48 +00:00
|
|
|
use parse::token;
|
2014-03-09 14:54:34 +00:00
|
|
|
use util::parser_testing::{string_to_parser};
|
2013-11-12 01:35:25 +00:00
|
|
|
use util::parser_testing::{string_to_pat, string_to_crate, 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)
|
2015-01-04 03:54:18 +00:00
|
|
|
#[derive(Clone)]
|
2014-06-25 00:03:49 +00:00
|
|
|
struct PathExprFinderContext {
|
2014-02-28 21:09:09 +00:00
|
|
|
path_accumulator: Vec<ast::Path> ,
|
2013-10-28 08:20:26 +00:00
|
|
|
}
|
|
|
|
|
2016-06-12 07:51:31 +00:00
|
|
|
impl Visitor for PathExprFinderContext {
|
2014-09-12 10:10:30 +00:00
|
|
|
fn visit_expr(&mut self, expr: &ast::Expr) {
|
2016-02-08 15:05:05 +00:00
|
|
|
if let ast::ExprKind::Path(None, ref p) = expr.node {
|
2015-02-17 17:29:13 +00:00
|
|
|
self.path_accumulator.push(p.clone());
|
2013-10-28 08:20:26 +00:00
|
|
|
}
|
2015-02-17 17:29:13 +00:00
|
|
|
visit::walk_expr(self, expr);
|
2013-10-28 08:20:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-26 21:39:54 +00:00
|
|
|
// find the variable references in a crate
|
|
|
|
fn crate_varrefs(the_crate : &ast::Crate) -> Vec<ast::Path> {
|
|
|
|
let mut path_finder = PathExprFinderContext{path_accumulator:Vec::new()};
|
2014-09-12 10:10:30 +00:00
|
|
|
visit::walk_crate(&mut path_finder, the_crate);
|
2014-06-26 21:39:54 +00:00
|
|
|
path_finder.path_accumulator
|
2013-10-28 08:20:26 +00:00
|
|
|
}
|
2013-02-26 18:15:29 +00:00
|
|
|
|
2014-07-04 19:18:37 +00:00
|
|
|
/// A Visitor that extracts the identifiers from a thingy.
|
|
|
|
// as a side note, I'm starting to want to abstract over these....
|
2014-09-12 10:10:30 +00:00
|
|
|
struct IdentFinder {
|
2014-07-04 19:18:37 +00:00
|
|
|
ident_accumulator: Vec<ast::Ident>
|
|
|
|
}
|
2014-06-26 21:39:54 +00:00
|
|
|
|
2016-06-12 07:51:31 +00:00
|
|
|
impl Visitor for IdentFinder {
|
2016-06-21 22:08:13 +00:00
|
|
|
fn visit_ident(&mut self, _: syntax_pos::Span, id: ast::Ident){
|
2014-07-04 19:18:37 +00:00
|
|
|
self.ident_accumulator.push(id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Find the idents in a crate
|
|
|
|
fn crate_idents(the_crate: &ast::Crate) -> Vec<ast::Ident> {
|
|
|
|
let mut ident_finder = IdentFinder{ident_accumulator: Vec::new()};
|
2014-09-12 10:10:30 +00:00
|
|
|
visit::walk_crate(&mut ident_finder, the_crate);
|
2014-07-04 19:18:37 +00:00
|
|
|
ident_finder.ident_accumulator
|
|
|
|
}
|
2014-06-26 21:39:54 +00:00
|
|
|
|
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.
|
|
|
|
|
2015-02-15 20:30:45 +00:00
|
|
|
fn test_ecfg() -> ExpansionConfig<'static> {
|
2014-09-27 00:14:23 +00:00
|
|
|
ExpansionConfig::default("test".to_string())
|
|
|
|
}
|
|
|
|
|
2014-06-24 01:45:51 +00:00
|
|
|
// make sure that macros can't escape fns
|
2015-01-31 23:08:25 +00:00
|
|
|
#[should_panic]
|
2013-02-26 18:15:29 +00:00
|
|
|
#[test] fn macros_cant_escape_fns_test () {
|
2014-11-14 17:18:10 +00:00
|
|
|
let src = "fn bogus() {macro_rules! z (() => (3+4));}\
|
2015-01-17 23:55:21 +00:00
|
|
|
fn inty() -> i32 { z!() }".to_string();
|
2015-05-13 20:00:17 +00:00
|
|
|
let sess = parse::ParseSess::new();
|
2013-02-26 18:15:29 +00:00
|
|
|
let crate_ast = parse::parse_crate_from_source_str(
|
2014-05-25 10:17:19 +00:00
|
|
|
"<test>".to_string(),
|
2013-06-12 17:02:55 +00:00
|
|
|
src,
|
2016-02-13 17:05:16 +00:00
|
|
|
Vec::new(), &sess).unwrap();
|
2013-02-26 18:15:29 +00:00
|
|
|
// should fail:
|
2016-06-11 01:37:24 +00:00
|
|
|
let mut loader = DummyMacroLoader;
|
|
|
|
let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader);
|
2016-06-02 01:14:33 +00:00
|
|
|
expand_crate(ecx, vec![], crate_ast);
|
2013-02-26 18:15:29 +00:00
|
|
|
}
|
|
|
|
|
2014-06-24 01:45:51 +00:00
|
|
|
// make sure that macros can't escape modules
|
2015-01-31 23:08:25 +00:00
|
|
|
#[should_panic]
|
2013-02-26 18:15:29 +00:00
|
|
|
#[test] fn macros_cant_escape_mods_test () {
|
2014-11-14 17:18:10 +00:00
|
|
|
let src = "mod foo {macro_rules! z (() => (3+4));}\
|
2015-01-17 23:55:21 +00:00
|
|
|
fn inty() -> i32 { z!() }".to_string();
|
2015-05-13 20:00:17 +00:00
|
|
|
let sess = parse::ParseSess::new();
|
2013-02-26 18:15:29 +00:00
|
|
|
let crate_ast = parse::parse_crate_from_source_str(
|
2014-05-25 10:17:19 +00:00
|
|
|
"<test>".to_string(),
|
2013-06-12 17:02:55 +00:00
|
|
|
src,
|
2016-02-13 17:05:16 +00:00
|
|
|
Vec::new(), &sess).unwrap();
|
2016-06-11 01:37:24 +00:00
|
|
|
let mut loader = DummyMacroLoader;
|
|
|
|
let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader);
|
2016-06-02 01:14:33 +00:00
|
|
|
expand_crate(ecx, vec![], crate_ast);
|
2013-02-26 18:15:29 +00:00
|
|
|
}
|
|
|
|
|
2014-12-19 04:48:26 +00:00
|
|
|
// macro_use modules should allow macros to escape
|
2013-02-26 18:15:29 +00:00
|
|
|
#[test] fn macros_can_escape_flattened_mods_test () {
|
2014-12-19 04:48:26 +00:00
|
|
|
let src = "#[macro_use] mod foo {macro_rules! z (() => (3+4));}\
|
2015-01-17 23:55:21 +00:00
|
|
|
fn inty() -> i32 { z!() }".to_string();
|
2015-05-13 20:00:17 +00:00
|
|
|
let sess = parse::ParseSess::new();
|
2013-02-26 18:15:29 +00:00
|
|
|
let crate_ast = parse::parse_crate_from_source_str(
|
2014-05-25 10:17:19 +00:00
|
|
|
"<test>".to_string(),
|
2013-06-12 17:02:55 +00:00
|
|
|
src,
|
2016-02-13 17:05:16 +00:00
|
|
|
Vec::new(), &sess).unwrap();
|
2016-06-11 01:37:24 +00:00
|
|
|
let mut loader = DummyMacroLoader;
|
|
|
|
let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader);
|
2016-06-02 01:14:33 +00:00
|
|
|
expand_crate(ecx, vec![], crate_ast);
|
2013-02-26 18:15:29 +00:00
|
|
|
}
|
|
|
|
|
2014-05-22 23:57:53 +00:00
|
|
|
fn expand_crate_str(crate_str: String) -> ast::Crate {
|
2015-05-13 20:00:17 +00:00
|
|
|
let ps = parse::ParseSess::new();
|
2015-03-28 21:58:51 +00:00
|
|
|
let crate_ast = panictry!(string_to_parser(&ps, crate_str).parse_crate_mod());
|
2013-06-25 18:40:51 +00:00
|
|
|
// the cfg argument actually does matter, here...
|
2016-06-11 01:37:24 +00:00
|
|
|
let mut loader = DummyMacroLoader;
|
|
|
|
let ecx = ExtCtxt::new(&ps, vec![], test_ecfg(), &mut loader);
|
2016-06-02 01:14:33 +00:00
|
|
|
expand_crate(ecx, vec![], crate_ast).0
|
2013-07-09 23:00:41 +00:00
|
|
|
}
|
|
|
|
|
2014-06-26 21:39:54 +00:00
|
|
|
// find the pat_ident paths in a crate
|
|
|
|
fn crate_bindings(the_crate : &ast::Crate) -> Vec<ast::Ident> {
|
2014-07-04 19:05:43 +00:00
|
|
|
let mut name_finder = PatIdentFinder{ident_accumulator:Vec::new()};
|
2014-09-12 10:10:30 +00:00
|
|
|
visit::walk_crate(&mut name_finder, the_crate);
|
2014-06-26 21:39:54 +00:00
|
|
|
name_finder.ident_accumulator
|
|
|
|
}
|
|
|
|
|
2013-07-09 22:10:16 +00:00
|
|
|
#[test] fn macro_tokens_should_match(){
|
2014-05-07 23:33:43 +00:00
|
|
|
expand_crate_str(
|
2014-11-14 17:18:10 +00:00
|
|
|
"macro_rules! m((a)=>(13)) ;fn main(){m!(a);}".to_string());
|
2013-07-09 22:10:16 +00:00
|
|
|
}
|
|
|
|
|
2014-07-08 00:43:09 +00:00
|
|
|
// should be able to use a bound identifier as a literal in a macro definition:
|
|
|
|
#[test] fn self_macro_parsing(){
|
|
|
|
expand_crate_str(
|
2015-01-17 23:55:21 +00:00
|
|
|
"macro_rules! foo ((zz) => (287;));
|
|
|
|
fn f(zz: i32) {foo!(zz);}".to_string()
|
2014-07-08 00:43:09 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2013-11-12 01:35:25 +00:00
|
|
|
// create a really evil test case where a $x appears inside a binding of $x
|
2014-07-03 01:27:07 +00:00
|
|
|
// but *shouldn't* bind because it was inserted by a different macro....
|
2013-11-12 01:35:25 +00:00
|
|
|
// can't write this test case until we have macro-generating macros.
|
|
|
|
|
2015-03-29 03:16:11 +00:00
|
|
|
#[test]
|
|
|
|
fn fmt_in_macro_used_inside_module_macro() {
|
2014-11-14 17:18:10 +00:00
|
|
|
let crate_str = "macro_rules! fmt_wrap(($b:expr)=>($b.to_string()));
|
|
|
|
macro_rules! foo_module (() => (mod generated { fn a() { let xx = 147; fmt_wrap!(xx);}}));
|
|
|
|
foo_module!();
|
2014-05-25 10:17:19 +00:00
|
|
|
".to_string();
|
2013-07-13 01:35:47 +00:00
|
|
|
let cr = expand_crate_str(crate_str);
|
|
|
|
// find the xx binding
|
2014-06-26 21:39:54 +00:00
|
|
|
let bindings = crate_bindings(&cr);
|
2014-02-28 21:09:09 +00:00
|
|
|
let cxbinds: Vec<&ast::Ident> =
|
2015-09-24 20:05:02 +00:00
|
|
|
bindings.iter().filter(|b| b.name.as_str() == "xx").collect();
|
2015-02-18 19:48:57 +00:00
|
|
|
let cxbinds: &[&ast::Ident] = &cxbinds[..];
|
2015-04-16 05:12:12 +00:00
|
|
|
let cxbind = match (cxbinds.len(), cxbinds.get(0)) {
|
|
|
|
(1, Some(b)) => *b,
|
2014-10-09 19:17:22 +00:00
|
|
|
_ => panic!("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);
|
2014-06-26 21:39:54 +00:00
|
|
|
let varrefs = crate_varrefs(&cr);
|
2013-10-28 08:20:26 +00:00
|
|
|
|
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
|
2015-09-24 20:05:02 +00:00
|
|
|
&& p.segments[0].identifier.name.as_str() == "xx"
|
2014-01-31 23:26:59 +00:00
|
|
|
}).enumerate() {
|
2014-10-15 06:05:01 +00:00
|
|
|
if mtwt::resolve(v.segments[0].identifier) != resolved_binding {
|
2014-01-09 10:06:55 +00:00
|
|
|
println!("uh oh, xx binding didn't match xx varref:");
|
2014-10-15 01:59:41 +00:00
|
|
|
println!("this is xx varref \\# {}", idx);
|
|
|
|
println!("binding: {}", cxbind);
|
|
|
|
println!("resolves to: {}", resolved_binding);
|
2014-10-15 06:05:01 +00:00
|
|
|
println!("varref: {}", v.segments[0].identifier);
|
2014-10-15 01:59:41 +00:00
|
|
|
println!("resolves to: {}",
|
2014-10-15 06:05:01 +00:00
|
|
|
mtwt::resolve(v.segments[0].identifier));
|
2014-02-24 20:47:19 +00:00
|
|
|
mtwt::with_sctable(|x| mtwt::display_sctable(x));
|
2013-07-13 01:35:47 +00:00
|
|
|
}
|
2014-10-15 06:05:01 +00:00
|
|
|
assert_eq!(mtwt::resolve(v.segments[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-05-07 23:33:43 +00:00
|
|
|
let pat = string_to_pat(
|
2014-05-25 10:17:19 +00:00
|
|
|
"(a,Foo{x:c @ (b,9),y:Bar(4,d)})".to_string());
|
2015-11-10 11:16:28 +00:00
|
|
|
let idents = pattern_bindings(&pat);
|
2014-06-26 21:39:54 +00:00
|
|
|
assert_eq!(idents, strs_to_idents(vec!("a","c","b","d")));
|
2013-06-04 21:56:33 +00:00
|
|
|
}
|
2013-06-07 19:28:03 +00:00
|
|
|
|
2014-06-24 23:33:44 +00:00
|
|
|
// test the list of identifier patterns gathered by the visitor. Note that
|
|
|
|
// 'None' is listed as an identifier pattern because we don't yet know that
|
|
|
|
// it's the name of a 0-ary variant, and that 'i' appears twice in succession.
|
2013-11-12 01:35:25 +00:00
|
|
|
#[test]
|
2014-07-04 19:18:37 +00:00
|
|
|
fn crate_bindings_test(){
|
2015-01-17 23:55:21 +00:00
|
|
|
let the_crate = string_to_crate("fn main (a: i32) -> i32 {|b| {
|
2014-06-24 23:33:44 +00:00
|
|
|
match 34 {None => 3, Some(i) | i => j, Foo{k:z,l:y} => \"banana\"}} }".to_string());
|
2014-06-26 21:39:54 +00:00
|
|
|
let idents = crate_bindings(&the_crate);
|
|
|
|
assert_eq!(idents, strs_to_idents(vec!("a","b","None","i","i","z","y")));
|
2013-11-12 01:35:25 +00:00
|
|
|
}
|
|
|
|
|
2014-07-04 19:18:37 +00:00
|
|
|
// test the IdentRenamer directly
|
|
|
|
#[test]
|
|
|
|
fn ident_renamer_test () {
|
2015-01-17 23:55:21 +00:00
|
|
|
let the_crate = string_to_crate("fn f(x: i32){let x = x; x}".to_string());
|
2014-07-04 19:18:37 +00:00
|
|
|
let f_ident = token::str_to_ident("f");
|
|
|
|
let x_ident = token::str_to_ident("x");
|
2015-01-17 23:55:21 +00:00
|
|
|
let int_ident = token::str_to_ident("i32");
|
2014-07-09 05:28:52 +00:00
|
|
|
let renames = vec!((x_ident,Name(16)));
|
2014-07-04 19:18:37 +00:00
|
|
|
let mut renamer = IdentRenamer{renames: &renames};
|
|
|
|
let renamed_crate = renamer.fold_crate(the_crate);
|
|
|
|
let idents = crate_idents(&renamed_crate);
|
|
|
|
let resolved : Vec<ast::Name> = idents.iter().map(|id| mtwt::resolve(*id)).collect();
|
2015-02-24 18:15:45 +00:00
|
|
|
assert_eq!(resolved, [f_ident.name,Name(16),int_ident.name,Name(16),Name(16),Name(16)]);
|
2014-07-04 19:18:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// test the PatIdentRenamer; only PatIdents get renamed
|
|
|
|
#[test]
|
|
|
|
fn pat_ident_renamer_test () {
|
2015-01-17 23:55:21 +00:00
|
|
|
let the_crate = string_to_crate("fn f(x: i32){let x = x; x}".to_string());
|
2014-07-04 19:18:37 +00:00
|
|
|
let f_ident = token::str_to_ident("f");
|
|
|
|
let x_ident = token::str_to_ident("x");
|
2015-01-17 23:55:21 +00:00
|
|
|
let int_ident = token::str_to_ident("i32");
|
2014-07-09 05:28:52 +00:00
|
|
|
let renames = vec!((x_ident,Name(16)));
|
2014-07-04 19:18:37 +00:00
|
|
|
let mut renamer = PatIdentRenamer{renames: &renames};
|
|
|
|
let renamed_crate = renamer.fold_crate(the_crate);
|
|
|
|
let idents = crate_idents(&renamed_crate);
|
|
|
|
let resolved : Vec<ast::Name> = idents.iter().map(|id| mtwt::resolve(*id)).collect();
|
|
|
|
let x_name = x_ident.name;
|
2015-02-24 18:15:45 +00:00
|
|
|
assert_eq!(resolved, [f_ident.name,Name(16),int_ident.name,Name(16),x_name,x_name]);
|
2014-07-04 19:18:37 +00:00
|
|
|
}
|
2013-02-26 18:15:29 +00:00
|
|
|
}
|