Move built-in syntax extensions to a separate crate
This commit is contained in:
@@ -1,245 +0,0 @@
|
||||
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
/*
|
||||
* Inline assembly support.
|
||||
*/
|
||||
use self::State::*;
|
||||
|
||||
use ast;
|
||||
use codemap;
|
||||
use codemap::Span;
|
||||
use ext::base;
|
||||
use ext::base::*;
|
||||
use feature_gate;
|
||||
use parse::token::{intern, InternedString};
|
||||
use parse::token;
|
||||
use ptr::P;
|
||||
use syntax::ast::AsmDialect;
|
||||
|
||||
enum State {
|
||||
Asm,
|
||||
Outputs,
|
||||
Inputs,
|
||||
Clobbers,
|
||||
Options,
|
||||
StateNone
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn next(&self) -> State {
|
||||
match *self {
|
||||
Asm => Outputs,
|
||||
Outputs => Inputs,
|
||||
Inputs => Clobbers,
|
||||
Clobbers => Options,
|
||||
Options => StateNone,
|
||||
StateNone => StateNone
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const OPTIONS: &'static [&'static str] = &["volatile", "alignstack", "intel"];
|
||||
|
||||
pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
|
||||
-> Box<base::MacResult+'cx> {
|
||||
if !cx.ecfg.enable_asm() {
|
||||
feature_gate::emit_feature_err(
|
||||
&cx.parse_sess.span_diagnostic, "asm", sp,
|
||||
feature_gate::GateIssue::Language,
|
||||
feature_gate::EXPLAIN_ASM);
|
||||
return DummyResult::expr(sp);
|
||||
}
|
||||
|
||||
let mut p = cx.new_parser_from_tts(tts);
|
||||
let mut asm = InternedString::new("");
|
||||
let mut asm_str_style = None;
|
||||
let mut outputs = Vec::new();
|
||||
let mut inputs = Vec::new();
|
||||
let mut clobs = Vec::new();
|
||||
let mut volatile = false;
|
||||
let mut alignstack = false;
|
||||
let mut dialect = AsmDialect::Att;
|
||||
|
||||
let mut state = Asm;
|
||||
|
||||
'statement: loop {
|
||||
match state {
|
||||
Asm => {
|
||||
if asm_str_style.is_some() {
|
||||
// If we already have a string with instructions,
|
||||
// ending up in Asm state again is an error.
|
||||
cx.span_err(sp, "malformed inline assembly");
|
||||
return DummyResult::expr(sp);
|
||||
}
|
||||
let (s, style) = match expr_to_string(cx, panictry!(p.parse_expr()),
|
||||
"inline assembly must be a string literal") {
|
||||
Some((s, st)) => (s, st),
|
||||
// let compilation continue
|
||||
None => return DummyResult::expr(sp),
|
||||
};
|
||||
asm = s;
|
||||
asm_str_style = Some(style);
|
||||
}
|
||||
Outputs => {
|
||||
while p.token != token::Eof &&
|
||||
p.token != token::Colon &&
|
||||
p.token != token::ModSep {
|
||||
|
||||
if !outputs.is_empty() {
|
||||
panictry!(p.eat(&token::Comma));
|
||||
}
|
||||
|
||||
let (constraint, _str_style) = panictry!(p.parse_str());
|
||||
|
||||
let span = p.last_span;
|
||||
|
||||
panictry!(p.expect(&token::OpenDelim(token::Paren)));
|
||||
let out = panictry!(p.parse_expr());
|
||||
panictry!(p.expect(&token::CloseDelim(token::Paren)));
|
||||
|
||||
// Expands a read+write operand into two operands.
|
||||
//
|
||||
// Use '+' modifier when you want the same expression
|
||||
// to be both an input and an output at the same time.
|
||||
// It's the opposite of '=&' which means that the memory
|
||||
// cannot be shared with any other operand (usually when
|
||||
// a register is clobbered early.)
|
||||
let output = match constraint.slice_shift_char() {
|
||||
Some(('=', _)) => None,
|
||||
Some(('+', operand)) => {
|
||||
Some(token::intern_and_get_ident(&format!(
|
||||
"={}", operand)))
|
||||
}
|
||||
_ => {
|
||||
cx.span_err(span, "output operand constraint lacks '=' or '+'");
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let is_rw = output.is_some();
|
||||
let is_indirect = constraint.contains("*");
|
||||
outputs.push(ast::InlineAsmOutput {
|
||||
constraint: output.unwrap_or(constraint),
|
||||
expr: out,
|
||||
is_rw: is_rw,
|
||||
is_indirect: is_indirect,
|
||||
});
|
||||
}
|
||||
}
|
||||
Inputs => {
|
||||
while p.token != token::Eof &&
|
||||
p.token != token::Colon &&
|
||||
p.token != token::ModSep {
|
||||
|
||||
if !inputs.is_empty() {
|
||||
panictry!(p.eat(&token::Comma));
|
||||
}
|
||||
|
||||
let (constraint, _str_style) = panictry!(p.parse_str());
|
||||
|
||||
if constraint.starts_with("=") {
|
||||
cx.span_err(p.last_span, "input operand constraint contains '='");
|
||||
} else if constraint.starts_with("+") {
|
||||
cx.span_err(p.last_span, "input operand constraint contains '+'");
|
||||
}
|
||||
|
||||
panictry!(p.expect(&token::OpenDelim(token::Paren)));
|
||||
let input = panictry!(p.parse_expr());
|
||||
panictry!(p.expect(&token::CloseDelim(token::Paren)));
|
||||
|
||||
inputs.push((constraint, input));
|
||||
}
|
||||
}
|
||||
Clobbers => {
|
||||
while p.token != token::Eof &&
|
||||
p.token != token::Colon &&
|
||||
p.token != token::ModSep {
|
||||
|
||||
if !clobs.is_empty() {
|
||||
panictry!(p.eat(&token::Comma));
|
||||
}
|
||||
|
||||
let (s, _str_style) = panictry!(p.parse_str());
|
||||
|
||||
if OPTIONS.iter().any(|&opt| s == opt) {
|
||||
cx.span_warn(p.last_span, "expected a clobber, found an option");
|
||||
}
|
||||
clobs.push(s);
|
||||
}
|
||||
}
|
||||
Options => {
|
||||
let (option, _str_style) = panictry!(p.parse_str());
|
||||
|
||||
if option == "volatile" {
|
||||
// Indicates that the inline assembly has side effects
|
||||
// and must not be optimized out along with its outputs.
|
||||
volatile = true;
|
||||
} else if option == "alignstack" {
|
||||
alignstack = true;
|
||||
} else if option == "intel" {
|
||||
dialect = AsmDialect::Intel;
|
||||
} else {
|
||||
cx.span_warn(p.last_span, "unrecognized option");
|
||||
}
|
||||
|
||||
if p.token == token::Comma {
|
||||
panictry!(p.eat(&token::Comma));
|
||||
}
|
||||
}
|
||||
StateNone => ()
|
||||
}
|
||||
|
||||
loop {
|
||||
// MOD_SEP is a double colon '::' without space in between.
|
||||
// When encountered, the state must be advanced twice.
|
||||
match (&p.token, state.next(), state.next().next()) {
|
||||
(&token::Colon, StateNone, _) |
|
||||
(&token::ModSep, _, StateNone) => {
|
||||
panictry!(p.bump());
|
||||
break 'statement;
|
||||
}
|
||||
(&token::Colon, st, _) |
|
||||
(&token::ModSep, _, st) => {
|
||||
panictry!(p.bump());
|
||||
state = st;
|
||||
}
|
||||
(&token::Eof, _, _) => break 'statement,
|
||||
_ => break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let expn_id = cx.codemap().record_expansion(codemap::ExpnInfo {
|
||||
call_site: sp,
|
||||
callee: codemap::NameAndSpan {
|
||||
format: codemap::MacroBang(intern("asm")),
|
||||
span: None,
|
||||
allow_internal_unstable: false,
|
||||
},
|
||||
});
|
||||
|
||||
MacEager::expr(P(ast::Expr {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
node: ast::ExprInlineAsm(ast::InlineAsm {
|
||||
asm: token::intern_and_get_ident(&asm),
|
||||
asm_str_style: asm_str_style.unwrap(),
|
||||
outputs: outputs,
|
||||
inputs: inputs,
|
||||
clobbers: clobs,
|
||||
volatile: volatile,
|
||||
alignstack: alignstack,
|
||||
dialect: dialect,
|
||||
expn_id: expn_id,
|
||||
}),
|
||||
span: sp,
|
||||
attrs: None,
|
||||
}))
|
||||
}
|
||||
@@ -464,26 +464,6 @@ fn initial_syntax_expander_table<'feat>(ecfg: &expand::ExpansionConfig<'feat>)
|
||||
|
||||
let mut syntax_expanders = SyntaxEnv::new();
|
||||
syntax_expanders.insert(intern("macro_rules"), MacroRulesTT);
|
||||
syntax_expanders.insert(intern("format_args"),
|
||||
// format_args uses `unstable` things internally.
|
||||
NormalTT(Box::new(ext::format::expand_format_args), None, true));
|
||||
syntax_expanders.insert(intern("env"),
|
||||
builtin_normal_expander(
|
||||
ext::env::expand_env));
|
||||
syntax_expanders.insert(intern("option_env"),
|
||||
builtin_normal_expander(
|
||||
ext::env::expand_option_env));
|
||||
syntax_expanders.insert(intern("concat_idents"),
|
||||
builtin_normal_expander(
|
||||
ext::concat_idents::expand_syntax_ext));
|
||||
syntax_expanders.insert(intern("concat"),
|
||||
builtin_normal_expander(
|
||||
ext::concat::expand_syntax_ext));
|
||||
syntax_expanders.insert(intern("log_syntax"),
|
||||
builtin_normal_expander(
|
||||
ext::log_syntax::expand_syntax_ext));
|
||||
|
||||
ext::deriving::register_all(&mut syntax_expanders);
|
||||
|
||||
if ecfg.enable_quotes() {
|
||||
// Quasi-quoting expanders
|
||||
@@ -552,15 +532,6 @@ fn initial_syntax_expander_table<'feat>(ecfg: &expand::ExpansionConfig<'feat>)
|
||||
syntax_expanders.insert(intern("module_path"),
|
||||
builtin_normal_expander(
|
||||
ext::source_util::expand_mod));
|
||||
syntax_expanders.insert(intern("asm"),
|
||||
builtin_normal_expander(
|
||||
ext::asm::expand_asm));
|
||||
syntax_expanders.insert(intern("cfg"),
|
||||
builtin_normal_expander(
|
||||
ext::cfg::expand_cfg));
|
||||
syntax_expanders.insert(intern("trace_macros"),
|
||||
builtin_normal_expander(
|
||||
ext::trace_macros::expand_trace_macros));
|
||||
syntax_expanders
|
||||
}
|
||||
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
/// The compiler code necessary to support the cfg! extension, which expands to
|
||||
/// a literal `true` or `false` based on whether the given cfg matches the
|
||||
/// current compilation environment.
|
||||
|
||||
use ast;
|
||||
use codemap::Span;
|
||||
use ext::base::*;
|
||||
use ext::base;
|
||||
use ext::build::AstBuilder;
|
||||
use attr;
|
||||
use attr::*;
|
||||
use parse::token;
|
||||
use config::CfgDiagReal;
|
||||
|
||||
pub fn expand_cfg<'cx>(cx: &mut ExtCtxt,
|
||||
sp: Span,
|
||||
tts: &[ast::TokenTree])
|
||||
-> Box<base::MacResult+'static> {
|
||||
let mut p = cx.new_parser_from_tts(tts);
|
||||
let cfg = panictry!(p.parse_meta_item());
|
||||
|
||||
if !panictry!(p.eat(&token::Eof)){
|
||||
cx.span_err(sp, "expected 1 cfg-pattern");
|
||||
return DummyResult::expr(sp);
|
||||
}
|
||||
|
||||
let matches_cfg = {
|
||||
let mut diag = CfgDiagReal {
|
||||
diag: &cx.parse_sess.span_diagnostic,
|
||||
feature_gated_cfgs: cx.feature_gated_cfgs,
|
||||
};
|
||||
attr::cfg_matches(&cx.cfg, &cfg, &mut diag)
|
||||
};
|
||||
MacEager::expr(cx.expr_bool(sp, matches_cfg))
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use ast;
|
||||
use codemap;
|
||||
use ext::base;
|
||||
use ext::build::AstBuilder;
|
||||
use parse::token;
|
||||
|
||||
use std::string::String;
|
||||
|
||||
pub fn expand_syntax_ext(cx: &mut base::ExtCtxt,
|
||||
sp: codemap::Span,
|
||||
tts: &[ast::TokenTree])
|
||||
-> Box<base::MacResult+'static> {
|
||||
let es = match base::get_exprs_from_tts(cx, sp, tts) {
|
||||
Some(e) => e,
|
||||
None => return base::DummyResult::expr(sp)
|
||||
};
|
||||
let mut accumulator = String::new();
|
||||
for e in es {
|
||||
match e.node {
|
||||
ast::ExprLit(ref lit) => {
|
||||
match lit.node {
|
||||
ast::LitStr(ref s, _) |
|
||||
ast::LitFloat(ref s, _) |
|
||||
ast::LitFloatUnsuffixed(ref s) => {
|
||||
accumulator.push_str(&s);
|
||||
}
|
||||
ast::LitChar(c) => {
|
||||
accumulator.push(c);
|
||||
}
|
||||
ast::LitInt(i, ast::UnsignedIntLit(_)) |
|
||||
ast::LitInt(i, ast::SignedIntLit(_, ast::Plus)) |
|
||||
ast::LitInt(i, ast::UnsuffixedIntLit(ast::Plus)) => {
|
||||
accumulator.push_str(&format!("{}", i));
|
||||
}
|
||||
ast::LitInt(i, ast::SignedIntLit(_, ast::Minus)) |
|
||||
ast::LitInt(i, ast::UnsuffixedIntLit(ast::Minus)) => {
|
||||
accumulator.push_str(&format!("-{}", i));
|
||||
}
|
||||
ast::LitBool(b) => {
|
||||
accumulator.push_str(&format!("{}", b));
|
||||
}
|
||||
ast::LitByte(..) |
|
||||
ast::LitByteStr(..) => {
|
||||
cx.span_err(e.span, "cannot concatenate a byte string literal");
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
cx.span_err(e.span, "expected a literal");
|
||||
}
|
||||
}
|
||||
}
|
||||
base::MacEager::expr(cx.expr_str(
|
||||
sp,
|
||||
token::intern_and_get_ident(&accumulator[..])))
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use ast::{self, TokenTree};
|
||||
use codemap::Span;
|
||||
use ext::base::*;
|
||||
use ext::base;
|
||||
use feature_gate;
|
||||
use parse::token;
|
||||
use parse::token::str_to_ident;
|
||||
use ptr::P;
|
||||
|
||||
pub fn expand_syntax_ext<'cx>(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree])
|
||||
-> Box<base::MacResult+'cx> {
|
||||
if !cx.ecfg.enable_concat_idents() {
|
||||
feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic,
|
||||
"concat_idents",
|
||||
sp,
|
||||
feature_gate::GateIssue::Language,
|
||||
feature_gate::EXPLAIN_CONCAT_IDENTS);
|
||||
return base::DummyResult::expr(sp);
|
||||
}
|
||||
|
||||
let mut res_str = String::new();
|
||||
for (i, e) in tts.iter().enumerate() {
|
||||
if i & 1 == 1 {
|
||||
match *e {
|
||||
TokenTree::Token(_, token::Comma) => {},
|
||||
_ => {
|
||||
cx.span_err(sp, "concat_idents! expecting comma.");
|
||||
return DummyResult::expr(sp);
|
||||
},
|
||||
}
|
||||
} else {
|
||||
match *e {
|
||||
TokenTree::Token(_, token::Ident(ident, _)) => {
|
||||
res_str.push_str(&ident.name.as_str())
|
||||
},
|
||||
_ => {
|
||||
cx.span_err(sp, "concat_idents! requires ident args.");
|
||||
return DummyResult::expr(sp);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
let res = str_to_ident(&res_str);
|
||||
|
||||
let e = P(ast::Expr {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
node: ast::ExprPath(None,
|
||||
ast::Path {
|
||||
span: sp,
|
||||
global: false,
|
||||
segments: vec!(
|
||||
ast::PathSegment {
|
||||
identifier: res,
|
||||
parameters: ast::PathParameters::none(),
|
||||
}
|
||||
)
|
||||
}
|
||||
),
|
||||
span: sp,
|
||||
attrs: None,
|
||||
});
|
||||
MacEager::expr(e)
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use ast::MetaItem;
|
||||
use codemap::Span;
|
||||
use ext::base::{ExtCtxt, Annotatable};
|
||||
use ext::deriving::generic::*;
|
||||
use ext::deriving::generic::ty::*;
|
||||
|
||||
pub fn expand_deriving_unsafe_bound(cx: &mut ExtCtxt,
|
||||
span: Span,
|
||||
_: &MetaItem,
|
||||
_: &Annotatable,
|
||||
_: &mut FnMut(Annotatable))
|
||||
{
|
||||
cx.span_err(span, "this unsafe trait should be implemented explicitly");
|
||||
}
|
||||
|
||||
pub fn expand_deriving_copy(cx: &mut ExtCtxt,
|
||||
span: Span,
|
||||
mitem: &MetaItem,
|
||||
item: &Annotatable,
|
||||
push: &mut FnMut(Annotatable))
|
||||
{
|
||||
let mut v = cx.crate_root.map(|s| vec![s]).unwrap_or(Vec::new());
|
||||
v.push("marker");
|
||||
v.push("Copy");
|
||||
let path = Path::new(v);
|
||||
|
||||
let trait_def = TraitDef {
|
||||
span: span,
|
||||
attributes: Vec::new(),
|
||||
path: path,
|
||||
additional_bounds: Vec::new(),
|
||||
generics: LifetimeBounds::empty(),
|
||||
is_unsafe: false,
|
||||
methods: Vec::new(),
|
||||
associated_types: Vec::new(),
|
||||
};
|
||||
|
||||
trait_def.expand(cx, mitem, item, push);
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use ast::{MetaItem, Expr};
|
||||
use codemap::Span;
|
||||
use ext::base::{ExtCtxt, Annotatable};
|
||||
use ext::build::AstBuilder;
|
||||
use ext::deriving::generic::*;
|
||||
use ext::deriving::generic::ty::*;
|
||||
use parse::token::InternedString;
|
||||
use ptr::P;
|
||||
|
||||
pub fn expand_deriving_clone(cx: &mut ExtCtxt,
|
||||
span: Span,
|
||||
mitem: &MetaItem,
|
||||
item: &Annotatable,
|
||||
push: &mut FnMut(Annotatable))
|
||||
{
|
||||
let inline = cx.meta_word(span, InternedString::new("inline"));
|
||||
let attrs = vec!(cx.attribute(span, inline));
|
||||
let trait_def = TraitDef {
|
||||
span: span,
|
||||
attributes: Vec::new(),
|
||||
path: path_std!(cx, core::clone::Clone),
|
||||
additional_bounds: Vec::new(),
|
||||
generics: LifetimeBounds::empty(),
|
||||
is_unsafe: false,
|
||||
methods: vec!(
|
||||
MethodDef {
|
||||
name: "clone",
|
||||
generics: LifetimeBounds::empty(),
|
||||
explicit_self: borrowed_explicit_self(),
|
||||
args: Vec::new(),
|
||||
ret_ty: Self_,
|
||||
attributes: attrs,
|
||||
is_unsafe: false,
|
||||
combine_substructure: combine_substructure(Box::new(|c, s, sub| {
|
||||
cs_clone("Clone", c, s, sub)
|
||||
})),
|
||||
}
|
||||
),
|
||||
associated_types: Vec::new(),
|
||||
};
|
||||
|
||||
trait_def.expand(cx, mitem, item, push)
|
||||
}
|
||||
|
||||
fn cs_clone(
|
||||
name: &str,
|
||||
cx: &mut ExtCtxt, trait_span: Span,
|
||||
substr: &Substructure) -> P<Expr> {
|
||||
let ctor_path;
|
||||
let all_fields;
|
||||
let fn_path = cx.std_path(&["clone", "Clone", "clone"]);
|
||||
let subcall = |field: &FieldInfo| {
|
||||
let args = vec![cx.expr_addr_of(field.span, field.self_.clone())];
|
||||
|
||||
cx.expr_call_global(field.span, fn_path.clone(), args)
|
||||
};
|
||||
|
||||
match *substr.fields {
|
||||
Struct(ref af) => {
|
||||
ctor_path = cx.path(trait_span, vec![substr.type_ident]);
|
||||
all_fields = af;
|
||||
}
|
||||
EnumMatching(_, variant, ref af) => {
|
||||
ctor_path = cx.path(trait_span, vec![substr.type_ident, variant.node.name]);
|
||||
all_fields = af;
|
||||
},
|
||||
EnumNonMatchingCollapsed (..) => {
|
||||
cx.span_bug(trait_span,
|
||||
&format!("non-matching enum variants in \
|
||||
`derive({})`", name))
|
||||
}
|
||||
StaticEnum(..) | StaticStruct(..) => {
|
||||
cx.span_bug(trait_span,
|
||||
&format!("static method in `derive({})`", name))
|
||||
}
|
||||
}
|
||||
|
||||
if !all_fields.is_empty() && all_fields[0].name.is_none() {
|
||||
// enum-like
|
||||
let subcalls = all_fields.iter().map(subcall).collect();
|
||||
let path = cx.expr_path(ctor_path);
|
||||
cx.expr_call(trait_span, path, subcalls)
|
||||
} else {
|
||||
// struct-like
|
||||
let fields = all_fields.iter().map(|field| {
|
||||
let ident = match field.name {
|
||||
Some(i) => i,
|
||||
None => {
|
||||
cx.span_bug(trait_span,
|
||||
&format!("unnamed field in normal struct in \
|
||||
`derive({})`", name))
|
||||
}
|
||||
};
|
||||
cx.field_imm(field.span, ident, subcall(field))
|
||||
}).collect::<Vec<_>>();
|
||||
|
||||
if fields.is_empty() {
|
||||
// no fields, so construct like `None`
|
||||
cx.expr_path(ctor_path)
|
||||
} else {
|
||||
cx.expr_struct(trait_span, ctor_path, fields)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use ast::{MetaItem, Expr};
|
||||
use codemap::Span;
|
||||
use ext::base::{ExtCtxt, Annotatable};
|
||||
use ext::build::AstBuilder;
|
||||
use ext::deriving::generic::*;
|
||||
use ext::deriving::generic::ty::*;
|
||||
use parse::token::InternedString;
|
||||
use ptr::P;
|
||||
|
||||
pub fn expand_deriving_eq(cx: &mut ExtCtxt,
|
||||
span: Span,
|
||||
mitem: &MetaItem,
|
||||
item: &Annotatable,
|
||||
push: &mut FnMut(Annotatable))
|
||||
{
|
||||
fn cs_total_eq_assert(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> {
|
||||
cs_same_method(
|
||||
|cx, span, exprs| {
|
||||
// create `a.<method>(); b.<method>(); c.<method>(); ...`
|
||||
// (where method is `assert_receiver_is_total_eq`)
|
||||
let stmts = exprs.into_iter().map(|e| cx.stmt_expr(e)).collect();
|
||||
let block = cx.block(span, stmts, None);
|
||||
cx.expr_block(block)
|
||||
},
|
||||
Box::new(|cx, sp, _, _| {
|
||||
cx.span_bug(sp, "non matching enums in derive(Eq)?") }),
|
||||
cx,
|
||||
span,
|
||||
substr
|
||||
)
|
||||
}
|
||||
|
||||
let inline = cx.meta_word(span, InternedString::new("inline"));
|
||||
let hidden = cx.meta_word(span, InternedString::new("hidden"));
|
||||
let doc = cx.meta_list(span, InternedString::new("doc"), vec!(hidden));
|
||||
let attrs = vec!(cx.attribute(span, inline),
|
||||
cx.attribute(span, doc));
|
||||
let trait_def = TraitDef {
|
||||
span: span,
|
||||
attributes: Vec::new(),
|
||||
path: path_std!(cx, core::cmp::Eq),
|
||||
additional_bounds: Vec::new(),
|
||||
generics: LifetimeBounds::empty(),
|
||||
is_unsafe: false,
|
||||
methods: vec!(
|
||||
MethodDef {
|
||||
name: "assert_receiver_is_total_eq",
|
||||
generics: LifetimeBounds::empty(),
|
||||
explicit_self: borrowed_explicit_self(),
|
||||
args: vec!(),
|
||||
ret_ty: nil_ty(),
|
||||
attributes: attrs,
|
||||
is_unsafe: false,
|
||||
combine_substructure: combine_substructure(Box::new(|a, b, c| {
|
||||
cs_total_eq_assert(a, b, c)
|
||||
}))
|
||||
}
|
||||
),
|
||||
associated_types: Vec::new(),
|
||||
};
|
||||
trait_def.expand(cx, mitem, item, push)
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use ast;
|
||||
use ast::{MetaItem, Expr};
|
||||
use codemap::Span;
|
||||
use ext::base::{ExtCtxt, Annotatable};
|
||||
use ext::build::AstBuilder;
|
||||
use ext::deriving::generic::*;
|
||||
use ext::deriving::generic::ty::*;
|
||||
use parse::token::InternedString;
|
||||
use ptr::P;
|
||||
|
||||
pub fn expand_deriving_ord(cx: &mut ExtCtxt,
|
||||
span: Span,
|
||||
mitem: &MetaItem,
|
||||
item: &Annotatable,
|
||||
push: &mut FnMut(Annotatable))
|
||||
{
|
||||
let inline = cx.meta_word(span, InternedString::new("inline"));
|
||||
let attrs = vec!(cx.attribute(span, inline));
|
||||
let trait_def = TraitDef {
|
||||
span: span,
|
||||
attributes: Vec::new(),
|
||||
path: path_std!(cx, core::cmp::Ord),
|
||||
additional_bounds: Vec::new(),
|
||||
generics: LifetimeBounds::empty(),
|
||||
is_unsafe: false,
|
||||
methods: vec!(
|
||||
MethodDef {
|
||||
name: "cmp",
|
||||
generics: LifetimeBounds::empty(),
|
||||
explicit_self: borrowed_explicit_self(),
|
||||
args: vec!(borrowed_self()),
|
||||
ret_ty: Literal(path_std!(cx, core::cmp::Ordering)),
|
||||
attributes: attrs,
|
||||
is_unsafe: false,
|
||||
combine_substructure: combine_substructure(Box::new(|a, b, c| {
|
||||
cs_cmp(a, b, c)
|
||||
})),
|
||||
}
|
||||
),
|
||||
associated_types: Vec::new(),
|
||||
};
|
||||
|
||||
trait_def.expand(cx, mitem, item, push)
|
||||
}
|
||||
|
||||
|
||||
pub fn ordering_collapsed(cx: &mut ExtCtxt,
|
||||
span: Span,
|
||||
self_arg_tags: &[ast::Ident]) -> P<ast::Expr> {
|
||||
let lft = cx.expr_ident(span, self_arg_tags[0]);
|
||||
let rgt = cx.expr_addr_of(span, cx.expr_ident(span, self_arg_tags[1]));
|
||||
cx.expr_method_call(span, lft, cx.ident_of("cmp"), vec![rgt])
|
||||
}
|
||||
|
||||
pub fn cs_cmp(cx: &mut ExtCtxt, span: Span,
|
||||
substr: &Substructure) -> P<Expr> {
|
||||
let test_id = cx.ident_of("__test");
|
||||
let equals_path = cx.path_global(span,
|
||||
cx.std_path(&["cmp", "Ordering", "Equal"]));
|
||||
|
||||
let cmp_path = cx.std_path(&["cmp", "Ord", "cmp"]);
|
||||
|
||||
/*
|
||||
Builds:
|
||||
|
||||
let __test = ::std::cmp::Ord::cmp(&self_field1, &other_field1);
|
||||
if other == ::std::cmp::Ordering::Equal {
|
||||
let __test = ::std::cmp::Ord::cmp(&self_field2, &other_field2);
|
||||
if __test == ::std::cmp::Ordering::Equal {
|
||||
...
|
||||
} else {
|
||||
__test
|
||||
}
|
||||
} else {
|
||||
__test
|
||||
}
|
||||
|
||||
FIXME #6449: These `if`s could/should be `match`es.
|
||||
*/
|
||||
cs_fold(
|
||||
// foldr nests the if-elses correctly, leaving the first field
|
||||
// as the outermost one, and the last as the innermost.
|
||||
false,
|
||||
|cx, span, old, self_f, other_fs| {
|
||||
// let __test = new;
|
||||
// if __test == ::std::cmp::Ordering::Equal {
|
||||
// old
|
||||
// } else {
|
||||
// __test
|
||||
// }
|
||||
|
||||
let new = {
|
||||
let other_f = match (other_fs.len(), other_fs.get(0)) {
|
||||
(1, Some(o_f)) => o_f,
|
||||
_ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`"),
|
||||
};
|
||||
|
||||
let args = vec![
|
||||
cx.expr_addr_of(span, self_f),
|
||||
cx.expr_addr_of(span, other_f.clone()),
|
||||
];
|
||||
|
||||
cx.expr_call_global(span, cmp_path.clone(), args)
|
||||
};
|
||||
|
||||
let assign = cx.stmt_let(span, false, test_id, new);
|
||||
|
||||
let cond = cx.expr_binary(span, ast::BiEq,
|
||||
cx.expr_ident(span, test_id),
|
||||
cx.expr_path(equals_path.clone()));
|
||||
let if_ = cx.expr_if(span,
|
||||
cond,
|
||||
old, Some(cx.expr_ident(span, test_id)));
|
||||
cx.expr_block(cx.block(span, vec!(assign), Some(if_)))
|
||||
},
|
||||
cx.expr_path(equals_path.clone()),
|
||||
Box::new(|cx, span, (self_args, tag_tuple), _non_self_args| {
|
||||
if self_args.len() != 2 {
|
||||
cx.span_bug(span, "not exactly 2 arguments in `derives(Ord)`")
|
||||
} else {
|
||||
ordering_collapsed(cx, span, tag_tuple)
|
||||
}
|
||||
}),
|
||||
cx, span, substr)
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use ast::{MetaItem, Expr, self};
|
||||
use codemap::Span;
|
||||
use ext::base::{ExtCtxt, Annotatable};
|
||||
use ext::build::AstBuilder;
|
||||
use ext::deriving::generic::*;
|
||||
use ext::deriving::generic::ty::*;
|
||||
use parse::token::InternedString;
|
||||
use ptr::P;
|
||||
|
||||
pub fn expand_deriving_partial_eq(cx: &mut ExtCtxt,
|
||||
span: Span,
|
||||
mitem: &MetaItem,
|
||||
item: &Annotatable,
|
||||
push: &mut FnMut(Annotatable))
|
||||
{
|
||||
// structures are equal if all fields are equal, and non equal, if
|
||||
// any fields are not equal or if the enum variants are different
|
||||
fn cs_eq(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> {
|
||||
cs_fold(
|
||||
true, // use foldl
|
||||
|cx, span, subexpr, self_f, other_fs| {
|
||||
let other_f = match (other_fs.len(), other_fs.get(0)) {
|
||||
(1, Some(o_f)) => o_f,
|
||||
_ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialEq)`")
|
||||
};
|
||||
|
||||
let eq = cx.expr_binary(span, ast::BiEq, self_f, other_f.clone());
|
||||
|
||||
cx.expr_binary(span, ast::BiAnd, subexpr, eq)
|
||||
},
|
||||
cx.expr_bool(span, true),
|
||||
Box::new(|cx, span, _, _| cx.expr_bool(span, false)),
|
||||
cx, span, substr)
|
||||
}
|
||||
fn cs_ne(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> {
|
||||
cs_fold(
|
||||
true, // use foldl
|
||||
|cx, span, subexpr, self_f, other_fs| {
|
||||
let other_f = match (other_fs.len(), other_fs.get(0)) {
|
||||
(1, Some(o_f)) => o_f,
|
||||
_ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialEq)`")
|
||||
};
|
||||
|
||||
let eq = cx.expr_binary(span, ast::BiNe, self_f, other_f.clone());
|
||||
|
||||
cx.expr_binary(span, ast::BiOr, subexpr, eq)
|
||||
},
|
||||
cx.expr_bool(span, false),
|
||||
Box::new(|cx, span, _, _| cx.expr_bool(span, true)),
|
||||
cx, span, substr)
|
||||
}
|
||||
|
||||
macro_rules! md {
|
||||
($name:expr, $f:ident) => { {
|
||||
let inline = cx.meta_word(span, InternedString::new("inline"));
|
||||
let attrs = vec!(cx.attribute(span, inline));
|
||||
MethodDef {
|
||||
name: $name,
|
||||
generics: LifetimeBounds::empty(),
|
||||
explicit_self: borrowed_explicit_self(),
|
||||
args: vec!(borrowed_self()),
|
||||
ret_ty: Literal(path_local!(bool)),
|
||||
attributes: attrs,
|
||||
is_unsafe: false,
|
||||
combine_substructure: combine_substructure(Box::new(|a, b, c| {
|
||||
$f(a, b, c)
|
||||
}))
|
||||
}
|
||||
} }
|
||||
}
|
||||
|
||||
let trait_def = TraitDef {
|
||||
span: span,
|
||||
attributes: Vec::new(),
|
||||
path: path_std!(cx, core::cmp::PartialEq),
|
||||
additional_bounds: Vec::new(),
|
||||
generics: LifetimeBounds::empty(),
|
||||
is_unsafe: false,
|
||||
methods: vec!(
|
||||
md!("eq", cs_eq),
|
||||
md!("ne", cs_ne)
|
||||
),
|
||||
associated_types: Vec::new(),
|
||||
};
|
||||
trait_def.expand(cx, mitem, item, push)
|
||||
}
|
||||
@@ -1,232 +0,0 @@
|
||||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
pub use self::OrderingOp::*;
|
||||
|
||||
use ast;
|
||||
use ast::{MetaItem, Expr};
|
||||
use codemap::Span;
|
||||
use ext::base::{ExtCtxt, Annotatable};
|
||||
use ext::build::AstBuilder;
|
||||
use ext::deriving::generic::*;
|
||||
use ext::deriving::generic::ty::*;
|
||||
use parse::token::InternedString;
|
||||
use ptr::P;
|
||||
|
||||
pub fn expand_deriving_partial_ord(cx: &mut ExtCtxt,
|
||||
span: Span,
|
||||
mitem: &MetaItem,
|
||||
item: &Annotatable,
|
||||
push: &mut FnMut(Annotatable))
|
||||
{
|
||||
macro_rules! md {
|
||||
($name:expr, $op:expr, $equal:expr) => { {
|
||||
let inline = cx.meta_word(span, InternedString::new("inline"));
|
||||
let attrs = vec!(cx.attribute(span, inline));
|
||||
MethodDef {
|
||||
name: $name,
|
||||
generics: LifetimeBounds::empty(),
|
||||
explicit_self: borrowed_explicit_self(),
|
||||
args: vec!(borrowed_self()),
|
||||
ret_ty: Literal(path_local!(bool)),
|
||||
attributes: attrs,
|
||||
is_unsafe: false,
|
||||
combine_substructure: combine_substructure(Box::new(|cx, span, substr| {
|
||||
cs_op($op, $equal, cx, span, substr)
|
||||
}))
|
||||
}
|
||||
} }
|
||||
}
|
||||
|
||||
let ordering_ty = Literal(path_std!(cx, core::cmp::Ordering));
|
||||
let ret_ty = Literal(Path::new_(pathvec_std!(cx, core::option::Option),
|
||||
None,
|
||||
vec![Box::new(ordering_ty)],
|
||||
true));
|
||||
|
||||
let inline = cx.meta_word(span, InternedString::new("inline"));
|
||||
let attrs = vec!(cx.attribute(span, inline));
|
||||
|
||||
let partial_cmp_def = MethodDef {
|
||||
name: "partial_cmp",
|
||||
generics: LifetimeBounds::empty(),
|
||||
explicit_self: borrowed_explicit_self(),
|
||||
args: vec![borrowed_self()],
|
||||
ret_ty: ret_ty,
|
||||
attributes: attrs,
|
||||
is_unsafe: false,
|
||||
combine_substructure: combine_substructure(Box::new(|cx, span, substr| {
|
||||
cs_partial_cmp(cx, span, substr)
|
||||
}))
|
||||
};
|
||||
|
||||
let trait_def = TraitDef {
|
||||
span: span,
|
||||
attributes: vec![],
|
||||
path: path_std!(cx, core::cmp::PartialOrd),
|
||||
additional_bounds: vec![],
|
||||
generics: LifetimeBounds::empty(),
|
||||
is_unsafe: false,
|
||||
methods: vec![
|
||||
partial_cmp_def,
|
||||
md!("lt", true, false),
|
||||
md!("le", true, true),
|
||||
md!("gt", false, false),
|
||||
md!("ge", false, true)
|
||||
],
|
||||
associated_types: Vec::new(),
|
||||
};
|
||||
trait_def.expand(cx, mitem, item, push)
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum OrderingOp {
|
||||
PartialCmpOp, LtOp, LeOp, GtOp, GeOp,
|
||||
}
|
||||
|
||||
pub fn some_ordering_collapsed(cx: &mut ExtCtxt,
|
||||
span: Span,
|
||||
op: OrderingOp,
|
||||
self_arg_tags: &[ast::Ident]) -> P<ast::Expr> {
|
||||
let lft = cx.expr_ident(span, self_arg_tags[0]);
|
||||
let rgt = cx.expr_addr_of(span, cx.expr_ident(span, self_arg_tags[1]));
|
||||
let op_str = match op {
|
||||
PartialCmpOp => "partial_cmp",
|
||||
LtOp => "lt", LeOp => "le",
|
||||
GtOp => "gt", GeOp => "ge",
|
||||
};
|
||||
cx.expr_method_call(span, lft, cx.ident_of(op_str), vec![rgt])
|
||||
}
|
||||
|
||||
pub fn cs_partial_cmp(cx: &mut ExtCtxt, span: Span,
|
||||
substr: &Substructure) -> P<Expr> {
|
||||
let test_id = cx.ident_of("__test");
|
||||
let ordering = cx.path_global(span,
|
||||
cx.std_path(&["cmp", "Ordering", "Equal"]));
|
||||
let ordering = cx.expr_path(ordering);
|
||||
let equals_expr = cx.expr_some(span, ordering);
|
||||
|
||||
let partial_cmp_path = cx.std_path(&["cmp", "PartialOrd", "partial_cmp"]);
|
||||
|
||||
/*
|
||||
Builds:
|
||||
|
||||
let __test = ::std::cmp::PartialOrd::partial_cmp(&self_field1, &other_field1);
|
||||
if __test == ::std::option::Option::Some(::std::cmp::Ordering::Equal) {
|
||||
let __test = ::std::cmp::PartialOrd::partial_cmp(&self_field2, &other_field2);
|
||||
if __test == ::std::option::Option::Some(::std::cmp::Ordering::Equal) {
|
||||
...
|
||||
} else {
|
||||
__test
|
||||
}
|
||||
} else {
|
||||
__test
|
||||
}
|
||||
|
||||
FIXME #6449: These `if`s could/should be `match`es.
|
||||
*/
|
||||
cs_fold(
|
||||
// foldr nests the if-elses correctly, leaving the first field
|
||||
// as the outermost one, and the last as the innermost.
|
||||
false,
|
||||
|cx, span, old, self_f, other_fs| {
|
||||
// let __test = new;
|
||||
// if __test == Some(::std::cmp::Ordering::Equal) {
|
||||
// old
|
||||
// } else {
|
||||
// __test
|
||||
// }
|
||||
|
||||
let new = {
|
||||
let other_f = match (other_fs.len(), other_fs.get(0)) {
|
||||
(1, Some(o_f)) => o_f,
|
||||
_ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`"),
|
||||
};
|
||||
|
||||
let args = vec![
|
||||
cx.expr_addr_of(span, self_f),
|
||||
cx.expr_addr_of(span, other_f.clone()),
|
||||
];
|
||||
|
||||
cx.expr_call_global(span, partial_cmp_path.clone(), args)
|
||||
};
|
||||
|
||||
let assign = cx.stmt_let(span, false, test_id, new);
|
||||
|
||||
let cond = cx.expr_binary(span, ast::BiEq,
|
||||
cx.expr_ident(span, test_id),
|
||||
equals_expr.clone());
|
||||
let if_ = cx.expr_if(span,
|
||||
cond,
|
||||
old, Some(cx.expr_ident(span, test_id)));
|
||||
cx.expr_block(cx.block(span, vec!(assign), Some(if_)))
|
||||
},
|
||||
equals_expr.clone(),
|
||||
Box::new(|cx, span, (self_args, tag_tuple), _non_self_args| {
|
||||
if self_args.len() != 2 {
|
||||
cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`")
|
||||
} else {
|
||||
some_ordering_collapsed(cx, span, PartialCmpOp, tag_tuple)
|
||||
}
|
||||
}),
|
||||
cx, span, substr)
|
||||
}
|
||||
|
||||
/// Strict inequality.
|
||||
fn cs_op(less: bool, equal: bool, cx: &mut ExtCtxt,
|
||||
span: Span, substr: &Substructure) -> P<Expr> {
|
||||
let op = if less {ast::BiLt} else {ast::BiGt};
|
||||
cs_fold(
|
||||
false, // need foldr,
|
||||
|cx, span, subexpr, self_f, other_fs| {
|
||||
/*
|
||||
build up a series of chain ||'s and &&'s from the inside
|
||||
out (hence foldr) to get lexical ordering, i.e. for op ==
|
||||
`ast::lt`
|
||||
|
||||
```
|
||||
self.f1 < other.f1 || (!(other.f1 < self.f1) &&
|
||||
(self.f2 < other.f2 || (!(other.f2 < self.f2) &&
|
||||
(false)
|
||||
))
|
||||
)
|
||||
```
|
||||
|
||||
The optimiser should remove the redundancy. We explicitly
|
||||
get use the binops to avoid auto-deref dereferencing too many
|
||||
layers of pointers, if the type includes pointers.
|
||||
*/
|
||||
let other_f = match (other_fs.len(), other_fs.get(0)) {
|
||||
(1, Some(o_f)) => o_f,
|
||||
_ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`")
|
||||
};
|
||||
|
||||
let cmp = cx.expr_binary(span, op, self_f.clone(), other_f.clone());
|
||||
|
||||
let not_cmp = cx.expr_unary(span, ast::UnNot,
|
||||
cx.expr_binary(span, op, other_f.clone(), self_f));
|
||||
|
||||
let and = cx.expr_binary(span, ast::BiAnd, not_cmp, subexpr);
|
||||
cx.expr_binary(span, ast::BiOr, cmp, and)
|
||||
},
|
||||
cx.expr_bool(span, equal),
|
||||
Box::new(|cx, span, (self_args, tag_tuple), _non_self_args| {
|
||||
if self_args.len() != 2 {
|
||||
cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`")
|
||||
} else {
|
||||
let op = match (less, equal) {
|
||||
(true, true) => LeOp, (true, false) => LtOp,
|
||||
(false, true) => GeOp, (false, false) => GtOp,
|
||||
};
|
||||
some_ordering_collapsed(cx, span, op, tag_tuple)
|
||||
}
|
||||
}),
|
||||
cx, span, substr)
|
||||
}
|
||||
@@ -1,155 +0,0 @@
|
||||
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use ast;
|
||||
use ast::{MetaItem, Expr};
|
||||
use codemap::{Span, respan};
|
||||
use ext::base::{ExtCtxt, Annotatable};
|
||||
use ext::build::AstBuilder;
|
||||
use ext::deriving::generic::*;
|
||||
use ext::deriving::generic::ty::*;
|
||||
use parse::token;
|
||||
use ptr::P;
|
||||
|
||||
pub fn expand_deriving_debug(cx: &mut ExtCtxt,
|
||||
span: Span,
|
||||
mitem: &MetaItem,
|
||||
item: &Annotatable,
|
||||
push: &mut FnMut(Annotatable))
|
||||
{
|
||||
// &mut ::std::fmt::Formatter
|
||||
let fmtr = Ptr(Box::new(Literal(path_std!(cx, core::fmt::Formatter))),
|
||||
Borrowed(None, ast::MutMutable));
|
||||
|
||||
let trait_def = TraitDef {
|
||||
span: span,
|
||||
attributes: Vec::new(),
|
||||
path: path_std!(cx, core::fmt::Debug),
|
||||
additional_bounds: Vec::new(),
|
||||
generics: LifetimeBounds::empty(),
|
||||
is_unsafe: false,
|
||||
methods: vec![
|
||||
MethodDef {
|
||||
name: "fmt",
|
||||
generics: LifetimeBounds::empty(),
|
||||
explicit_self: borrowed_explicit_self(),
|
||||
args: vec!(fmtr),
|
||||
ret_ty: Literal(path_std!(cx, core::fmt::Result)),
|
||||
attributes: Vec::new(),
|
||||
is_unsafe: false,
|
||||
combine_substructure: combine_substructure(Box::new(|a, b, c| {
|
||||
show_substructure(a, b, c)
|
||||
}))
|
||||
}
|
||||
],
|
||||
associated_types: Vec::new(),
|
||||
};
|
||||
trait_def.expand(cx, mitem, item, push)
|
||||
}
|
||||
|
||||
/// We use the debug builders to do the heavy lifting here
|
||||
fn show_substructure(cx: &mut ExtCtxt, span: Span,
|
||||
substr: &Substructure) -> P<Expr> {
|
||||
// build fmt.debug_struct(<name>).field(<fieldname>, &<fieldval>)....build()
|
||||
// or fmt.debug_tuple(<name>).field(&<fieldval>)....build()
|
||||
// based on the "shape".
|
||||
let ident = match *substr.fields {
|
||||
Struct(_) => substr.type_ident,
|
||||
EnumMatching(_, v, _) => v.node.name,
|
||||
EnumNonMatchingCollapsed(..) | StaticStruct(..) | StaticEnum(..) => {
|
||||
cx.span_bug(span, "nonsensical .fields in `#[derive(Debug)]`")
|
||||
}
|
||||
};
|
||||
|
||||
// We want to make sure we have the expn_id set so that we can use unstable methods
|
||||
let span = Span { expn_id: cx.backtrace(), .. span };
|
||||
let name = cx.expr_lit(span, ast::Lit_::LitStr(ident.name.as_str(),
|
||||
ast::StrStyle::CookedStr));
|
||||
let builder = token::str_to_ident("builder");
|
||||
let builder_expr = cx.expr_ident(span, builder.clone());
|
||||
|
||||
let fmt = substr.nonself_args[0].clone();
|
||||
|
||||
let stmts = match *substr.fields {
|
||||
Struct(ref fields) | EnumMatching(_, _, ref fields) => {
|
||||
let mut stmts = vec![];
|
||||
if fields.is_empty() || fields[0].name.is_none() {
|
||||
// tuple struct/"normal" variant
|
||||
let expr = cx.expr_method_call(span,
|
||||
fmt,
|
||||
token::str_to_ident("debug_tuple"),
|
||||
vec![name]);
|
||||
stmts.push(cx.stmt_let(span, true, builder, expr));
|
||||
|
||||
for field in fields {
|
||||
// Use double indirection to make sure this works for unsized types
|
||||
let field = cx.expr_addr_of(field.span, field.self_.clone());
|
||||
let field = cx.expr_addr_of(field.span, field);
|
||||
|
||||
let expr = cx.expr_method_call(span,
|
||||
builder_expr.clone(),
|
||||
token::str_to_ident("field"),
|
||||
vec![field]);
|
||||
|
||||
// Use `let _ = expr;` to avoid triggering the
|
||||
// unused_results lint.
|
||||
stmts.push(stmt_let_undescore(cx, span, expr));
|
||||
}
|
||||
} else {
|
||||
// normal struct/struct variant
|
||||
let expr = cx.expr_method_call(span,
|
||||
fmt,
|
||||
token::str_to_ident("debug_struct"),
|
||||
vec![name]);
|
||||
stmts.push(cx.stmt_let(span, true, builder, expr));
|
||||
|
||||
for field in fields {
|
||||
let name = cx.expr_lit(field.span, ast::Lit_::LitStr(
|
||||
field.name.unwrap().name.as_str(),
|
||||
ast::StrStyle::CookedStr));
|
||||
|
||||
// Use double indirection to make sure this works for unsized types
|
||||
let field = cx.expr_addr_of(field.span, field.self_.clone());
|
||||
let field = cx.expr_addr_of(field.span, field);
|
||||
let expr = cx.expr_method_call(span,
|
||||
builder_expr.clone(),
|
||||
token::str_to_ident("field"),
|
||||
vec![name, field]);
|
||||
stmts.push(stmt_let_undescore(cx, span, expr));
|
||||
}
|
||||
}
|
||||
stmts
|
||||
}
|
||||
_ => unreachable!()
|
||||
};
|
||||
|
||||
let expr = cx.expr_method_call(span,
|
||||
builder_expr,
|
||||
token::str_to_ident("finish"),
|
||||
vec![]);
|
||||
|
||||
let block = cx.block(span, stmts, Some(expr));
|
||||
cx.expr_block(block)
|
||||
}
|
||||
|
||||
fn stmt_let_undescore(cx: &mut ExtCtxt,
|
||||
sp: Span,
|
||||
expr: P<ast::Expr>) -> P<ast::Stmt> {
|
||||
let local = P(ast::Local {
|
||||
pat: cx.pat_wild(sp),
|
||||
ty: None,
|
||||
init: Some(expr),
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
span: sp,
|
||||
attrs: None,
|
||||
});
|
||||
let decl = respan(sp, ast::DeclLocal(local));
|
||||
P(respan(sp, ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID)))
|
||||
}
|
||||
@@ -1,223 +0,0 @@
|
||||
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! The compiler code necessary for `#[derive(Decodable)]`. See encodable.rs for more.
|
||||
|
||||
use ast;
|
||||
use ast::{MetaItem, Expr, MutMutable};
|
||||
use codemap::Span;
|
||||
use ext::base::{ExtCtxt, Annotatable};
|
||||
use ext::build::AstBuilder;
|
||||
use ext::deriving::generic::*;
|
||||
use ext::deriving::generic::ty::*;
|
||||
use parse::token::InternedString;
|
||||
use parse::token;
|
||||
use ptr::P;
|
||||
|
||||
pub fn expand_deriving_rustc_decodable(cx: &mut ExtCtxt,
|
||||
span: Span,
|
||||
mitem: &MetaItem,
|
||||
item: &Annotatable,
|
||||
push: &mut FnMut(Annotatable))
|
||||
{
|
||||
expand_deriving_decodable_imp(cx, span, mitem, item, push, "rustc_serialize")
|
||||
}
|
||||
|
||||
pub fn expand_deriving_decodable(cx: &mut ExtCtxt,
|
||||
span: Span,
|
||||
mitem: &MetaItem,
|
||||
item: &Annotatable,
|
||||
push: &mut FnMut(Annotatable))
|
||||
{
|
||||
expand_deriving_decodable_imp(cx, span, mitem, item, push, "serialize")
|
||||
}
|
||||
|
||||
fn expand_deriving_decodable_imp(cx: &mut ExtCtxt,
|
||||
span: Span,
|
||||
mitem: &MetaItem,
|
||||
item: &Annotatable,
|
||||
push: &mut FnMut(Annotatable),
|
||||
krate: &'static str)
|
||||
{
|
||||
if cx.crate_root != Some("std") {
|
||||
// FIXME(#21880): lift this requirement.
|
||||
cx.span_err(span, "this trait cannot be derived with #![no_std] \
|
||||
or #![no_core]");
|
||||
return
|
||||
}
|
||||
|
||||
let trait_def = TraitDef {
|
||||
span: span,
|
||||
attributes: Vec::new(),
|
||||
path: Path::new_(vec!(krate, "Decodable"), None, vec!(), true),
|
||||
additional_bounds: Vec::new(),
|
||||
generics: LifetimeBounds::empty(),
|
||||
is_unsafe: false,
|
||||
methods: vec!(
|
||||
MethodDef {
|
||||
name: "decode",
|
||||
generics: LifetimeBounds {
|
||||
lifetimes: Vec::new(),
|
||||
bounds: vec!(("__D", vec!(Path::new_(
|
||||
vec!(krate, "Decoder"), None,
|
||||
vec!(), true))))
|
||||
},
|
||||
explicit_self: None,
|
||||
args: vec!(Ptr(Box::new(Literal(Path::new_local("__D"))),
|
||||
Borrowed(None, MutMutable))),
|
||||
ret_ty: Literal(Path::new_(
|
||||
pathvec_std!(cx, core::result::Result),
|
||||
None,
|
||||
vec!(Box::new(Self_), Box::new(Literal(Path::new_(
|
||||
vec!["__D", "Error"], None, vec![], false
|
||||
)))),
|
||||
true
|
||||
)),
|
||||
attributes: Vec::new(),
|
||||
is_unsafe: false,
|
||||
combine_substructure: combine_substructure(Box::new(|a, b, c| {
|
||||
decodable_substructure(a, b, c, krate)
|
||||
})),
|
||||
}
|
||||
),
|
||||
associated_types: Vec::new(),
|
||||
};
|
||||
|
||||
trait_def.expand(cx, mitem, item, push)
|
||||
}
|
||||
|
||||
fn decodable_substructure(cx: &mut ExtCtxt, trait_span: Span,
|
||||
substr: &Substructure,
|
||||
krate: &str) -> P<Expr> {
|
||||
let decoder = substr.nonself_args[0].clone();
|
||||
let recurse = vec!(cx.ident_of(krate),
|
||||
cx.ident_of("Decodable"),
|
||||
cx.ident_of("decode"));
|
||||
let exprdecode = cx.expr_path(cx.path_global(trait_span, recurse));
|
||||
// throw an underscore in front to suppress unused variable warnings
|
||||
let blkarg = cx.ident_of("_d");
|
||||
let blkdecoder = cx.expr_ident(trait_span, blkarg);
|
||||
|
||||
return match *substr.fields {
|
||||
StaticStruct(_, ref summary) => {
|
||||
let nfields = match *summary {
|
||||
Unnamed(ref fields) => fields.len(),
|
||||
Named(ref fields) => fields.len()
|
||||
};
|
||||
let read_struct_field = cx.ident_of("read_struct_field");
|
||||
|
||||
let path = cx.path_ident(trait_span, substr.type_ident);
|
||||
let result = decode_static_fields(cx,
|
||||
trait_span,
|
||||
path,
|
||||
summary,
|
||||
|cx, span, name, field| {
|
||||
cx.expr_try(span,
|
||||
cx.expr_method_call(span, blkdecoder.clone(), read_struct_field,
|
||||
vec!(cx.expr_str(span, name),
|
||||
cx.expr_usize(span, field),
|
||||
exprdecode.clone())))
|
||||
});
|
||||
let result = cx.expr_ok(trait_span, result);
|
||||
cx.expr_method_call(trait_span,
|
||||
decoder,
|
||||
cx.ident_of("read_struct"),
|
||||
vec!(
|
||||
cx.expr_str(trait_span, substr.type_ident.name.as_str()),
|
||||
cx.expr_usize(trait_span, nfields),
|
||||
cx.lambda_expr_1(trait_span, result, blkarg)
|
||||
))
|
||||
}
|
||||
StaticEnum(_, ref fields) => {
|
||||
let variant = cx.ident_of("i");
|
||||
|
||||
let mut arms = Vec::new();
|
||||
let mut variants = Vec::new();
|
||||
let rvariant_arg = cx.ident_of("read_enum_variant_arg");
|
||||
|
||||
for (i, &(ident, v_span, ref parts)) in fields.iter().enumerate() {
|
||||
variants.push(cx.expr_str(v_span, ident.name.as_str()));
|
||||
|
||||
let path = cx.path(trait_span, vec![substr.type_ident, ident]);
|
||||
let decoded = decode_static_fields(cx,
|
||||
v_span,
|
||||
path,
|
||||
parts,
|
||||
|cx, span, _, field| {
|
||||
let idx = cx.expr_usize(span, field);
|
||||
cx.expr_try(span,
|
||||
cx.expr_method_call(span, blkdecoder.clone(), rvariant_arg,
|
||||
vec!(idx, exprdecode.clone())))
|
||||
});
|
||||
|
||||
arms.push(cx.arm(v_span,
|
||||
vec!(cx.pat_lit(v_span, cx.expr_usize(v_span, i))),
|
||||
decoded));
|
||||
}
|
||||
|
||||
arms.push(cx.arm_unreachable(trait_span));
|
||||
|
||||
let result = cx.expr_ok(trait_span,
|
||||
cx.expr_match(trait_span,
|
||||
cx.expr_ident(trait_span, variant), arms));
|
||||
let lambda = cx.lambda_expr(trait_span, vec!(blkarg, variant), result);
|
||||
let variant_vec = cx.expr_vec(trait_span, variants);
|
||||
let variant_vec = cx.expr_addr_of(trait_span, variant_vec);
|
||||
let result = cx.expr_method_call(trait_span, blkdecoder,
|
||||
cx.ident_of("read_enum_variant"),
|
||||
vec!(variant_vec, lambda));
|
||||
cx.expr_method_call(trait_span,
|
||||
decoder,
|
||||
cx.ident_of("read_enum"),
|
||||
vec!(
|
||||
cx.expr_str(trait_span, substr.type_ident.name.as_str()),
|
||||
cx.lambda_expr_1(trait_span, result, blkarg)
|
||||
))
|
||||
}
|
||||
_ => cx.bug("expected StaticEnum or StaticStruct in derive(Decodable)")
|
||||
};
|
||||
}
|
||||
|
||||
/// Create a decoder for a single enum variant/struct:
|
||||
/// - `outer_pat_path` is the path to this enum variant/struct
|
||||
/// - `getarg` should retrieve the `usize`-th field with name `@str`.
|
||||
fn decode_static_fields<F>(cx: &mut ExtCtxt,
|
||||
trait_span: Span,
|
||||
outer_pat_path: ast::Path,
|
||||
fields: &StaticFields,
|
||||
mut getarg: F)
|
||||
-> P<Expr> where
|
||||
F: FnMut(&mut ExtCtxt, Span, InternedString, usize) -> P<Expr>,
|
||||
{
|
||||
match *fields {
|
||||
Unnamed(ref fields) => {
|
||||
let path_expr = cx.expr_path(outer_pat_path);
|
||||
if fields.is_empty() {
|
||||
path_expr
|
||||
} else {
|
||||
let fields = fields.iter().enumerate().map(|(i, &span)| {
|
||||
getarg(cx, span,
|
||||
token::intern_and_get_ident(&format!("_field{}", i)),
|
||||
i)
|
||||
}).collect();
|
||||
|
||||
cx.expr_call(trait_span, path_expr, fields)
|
||||
}
|
||||
}
|
||||
Named(ref fields) => {
|
||||
// use the field's span to get nicer error messages.
|
||||
let fields = fields.iter().enumerate().map(|(i, &(ident, span))| {
|
||||
let arg = getarg(cx, span, ident.name.as_str(), i);
|
||||
cx.field_imm(span, ident, arg)
|
||||
}).collect();
|
||||
cx.expr_struct(trait_span, outer_pat_path, fields)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use ast::{MetaItem, Expr};
|
||||
use codemap::Span;
|
||||
use ext::base::{ExtCtxt, Annotatable};
|
||||
use ext::build::AstBuilder;
|
||||
use ext::deriving::generic::*;
|
||||
use ext::deriving::generic::ty::*;
|
||||
use parse::token::InternedString;
|
||||
use ptr::P;
|
||||
|
||||
pub fn expand_deriving_default(cx: &mut ExtCtxt,
|
||||
span: Span,
|
||||
mitem: &MetaItem,
|
||||
item: &Annotatable,
|
||||
push: &mut FnMut(Annotatable))
|
||||
{
|
||||
let inline = cx.meta_word(span, InternedString::new("inline"));
|
||||
let attrs = vec!(cx.attribute(span, inline));
|
||||
let trait_def = TraitDef {
|
||||
span: span,
|
||||
attributes: Vec::new(),
|
||||
path: path_std!(cx, core::default::Default),
|
||||
additional_bounds: Vec::new(),
|
||||
generics: LifetimeBounds::empty(),
|
||||
is_unsafe: false,
|
||||
methods: vec!(
|
||||
MethodDef {
|
||||
name: "default",
|
||||
generics: LifetimeBounds::empty(),
|
||||
explicit_self: None,
|
||||
args: Vec::new(),
|
||||
ret_ty: Self_,
|
||||
attributes: attrs,
|
||||
is_unsafe: false,
|
||||
combine_substructure: combine_substructure(Box::new(|a, b, c| {
|
||||
default_substructure(a, b, c)
|
||||
}))
|
||||
}
|
||||
),
|
||||
associated_types: Vec::new(),
|
||||
};
|
||||
trait_def.expand(cx, mitem, item, push)
|
||||
}
|
||||
|
||||
fn default_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P<Expr> {
|
||||
let default_ident = cx.std_path(&["default", "Default", "default"]);
|
||||
let default_call = |span| cx.expr_call_global(span, default_ident.clone(), Vec::new());
|
||||
|
||||
return match *substr.fields {
|
||||
StaticStruct(_, ref summary) => {
|
||||
match *summary {
|
||||
Unnamed(ref fields) => {
|
||||
if fields.is_empty() {
|
||||
cx.expr_ident(trait_span, substr.type_ident)
|
||||
} else {
|
||||
let exprs = fields.iter().map(|sp| default_call(*sp)).collect();
|
||||
cx.expr_call_ident(trait_span, substr.type_ident, exprs)
|
||||
}
|
||||
}
|
||||
Named(ref fields) => {
|
||||
let default_fields = fields.iter().map(|&(ident, span)| {
|
||||
cx.field_imm(span, ident, default_call(span))
|
||||
}).collect();
|
||||
cx.expr_struct_ident(trait_span, substr.type_ident, default_fields)
|
||||
}
|
||||
}
|
||||
}
|
||||
StaticEnum(..) => {
|
||||
cx.span_err(trait_span, "`Default` cannot be derived for enums, only structs");
|
||||
// let compilation continue
|
||||
cx.expr_usize(trait_span, 0)
|
||||
}
|
||||
_ => cx.span_bug(trait_span, "Non-static method in `derive(Default)`")
|
||||
};
|
||||
}
|
||||
@@ -1,288 +0,0 @@
|
||||
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! The compiler code necessary to implement the `#[derive(Encodable)]`
|
||||
//! (and `Decodable`, in decodable.rs) extension. The idea here is that
|
||||
//! type-defining items may be tagged with `#[derive(Encodable, Decodable)]`.
|
||||
//!
|
||||
//! For example, a type like:
|
||||
//!
|
||||
//! ```ignore
|
||||
//! #[derive(Encodable, Decodable)]
|
||||
//! struct Node { id: usize }
|
||||
//! ```
|
||||
//!
|
||||
//! would generate two implementations like:
|
||||
//!
|
||||
//! ```ignore
|
||||
//! impl<S: Encoder<E>, E> Encodable<S, E> for Node {
|
||||
//! fn encode(&self, s: &mut S) -> Result<(), E> {
|
||||
//! s.emit_struct("Node", 1, |this| {
|
||||
//! this.emit_struct_field("id", 0, |this| {
|
||||
//! Encodable::encode(&self.id, this)
|
||||
//! /* this.emit_usize(self.id) can also be used */
|
||||
//! })
|
||||
//! })
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! impl<D: Decoder<E>, E> Decodable<D, E> for Node {
|
||||
//! fn decode(d: &mut D) -> Result<Node, E> {
|
||||
//! d.read_struct("Node", 1, |this| {
|
||||
//! match this.read_struct_field("id", 0, |this| Decodable::decode(this)) {
|
||||
//! Ok(id) => Ok(Node { id: id }),
|
||||
//! Err(e) => Err(e),
|
||||
//! }
|
||||
//! })
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Other interesting scenarios are when the item has type parameters or
|
||||
//! references other non-built-in types. A type definition like:
|
||||
//!
|
||||
//! ```ignore
|
||||
//! #[derive(Encodable, Decodable)]
|
||||
//! struct Spanned<T> { node: T, span: Span }
|
||||
//! ```
|
||||
//!
|
||||
//! would yield functions like:
|
||||
//!
|
||||
//! ```ignore
|
||||
//! impl<
|
||||
//! S: Encoder<E>,
|
||||
//! E,
|
||||
//! T: Encodable<S, E>
|
||||
//! > Encodable<S, E> for Spanned<T> {
|
||||
//! fn encode(&self, s: &mut S) -> Result<(), E> {
|
||||
//! s.emit_struct("Spanned", 2, |this| {
|
||||
//! this.emit_struct_field("node", 0, |this| self.node.encode(this))
|
||||
//! .unwrap();
|
||||
//! this.emit_struct_field("span", 1, |this| self.span.encode(this))
|
||||
//! })
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! impl<
|
||||
//! D: Decoder<E>,
|
||||
//! E,
|
||||
//! T: Decodable<D, E>
|
||||
//! > Decodable<D, E> for Spanned<T> {
|
||||
//! fn decode(d: &mut D) -> Result<Spanned<T>, E> {
|
||||
//! d.read_struct("Spanned", 2, |this| {
|
||||
//! Ok(Spanned {
|
||||
//! node: this.read_struct_field("node", 0, |this| Decodable::decode(this))
|
||||
//! .unwrap(),
|
||||
//! span: this.read_struct_field("span", 1, |this| Decodable::decode(this))
|
||||
//! .unwrap(),
|
||||
//! })
|
||||
//! })
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use ast::{MetaItem, Expr, ExprRet, MutMutable};
|
||||
use codemap::Span;
|
||||
use ext::base::{ExtCtxt,Annotatable};
|
||||
use ext::build::AstBuilder;
|
||||
use ext::deriving::generic::*;
|
||||
use ext::deriving::generic::ty::*;
|
||||
use parse::token;
|
||||
use ptr::P;
|
||||
|
||||
pub fn expand_deriving_rustc_encodable(cx: &mut ExtCtxt,
|
||||
span: Span,
|
||||
mitem: &MetaItem,
|
||||
item: &Annotatable,
|
||||
push: &mut FnMut(Annotatable))
|
||||
{
|
||||
expand_deriving_encodable_imp(cx, span, mitem, item, push, "rustc_serialize")
|
||||
}
|
||||
|
||||
pub fn expand_deriving_encodable(cx: &mut ExtCtxt,
|
||||
span: Span,
|
||||
mitem: &MetaItem,
|
||||
item: &Annotatable,
|
||||
push: &mut FnMut(Annotatable))
|
||||
{
|
||||
expand_deriving_encodable_imp(cx, span, mitem, item, push, "serialize")
|
||||
}
|
||||
|
||||
fn expand_deriving_encodable_imp(cx: &mut ExtCtxt,
|
||||
span: Span,
|
||||
mitem: &MetaItem,
|
||||
item: &Annotatable,
|
||||
push: &mut FnMut(Annotatable),
|
||||
krate: &'static str)
|
||||
{
|
||||
if cx.crate_root != Some("std") {
|
||||
// FIXME(#21880): lift this requirement.
|
||||
cx.span_err(span, "this trait cannot be derived with #![no_std] \
|
||||
or #![no_core]");
|
||||
return;
|
||||
}
|
||||
|
||||
let trait_def = TraitDef {
|
||||
span: span,
|
||||
attributes: Vec::new(),
|
||||
path: Path::new_(vec!(krate, "Encodable"), None, vec!(), true),
|
||||
additional_bounds: Vec::new(),
|
||||
generics: LifetimeBounds::empty(),
|
||||
is_unsafe: false,
|
||||
methods: vec!(
|
||||
MethodDef {
|
||||
name: "encode",
|
||||
generics: LifetimeBounds {
|
||||
lifetimes: Vec::new(),
|
||||
bounds: vec!(("__S", vec!(Path::new_(
|
||||
vec!(krate, "Encoder"), None,
|
||||
vec!(), true))))
|
||||
},
|
||||
explicit_self: borrowed_explicit_self(),
|
||||
args: vec!(Ptr(Box::new(Literal(Path::new_local("__S"))),
|
||||
Borrowed(None, MutMutable))),
|
||||
ret_ty: Literal(Path::new_(
|
||||
pathvec_std!(cx, core::result::Result),
|
||||
None,
|
||||
vec!(Box::new(Tuple(Vec::new())), Box::new(Literal(Path::new_(
|
||||
vec!["__S", "Error"], None, vec![], false
|
||||
)))),
|
||||
true
|
||||
)),
|
||||
attributes: Vec::new(),
|
||||
is_unsafe: false,
|
||||
combine_substructure: combine_substructure(Box::new(|a, b, c| {
|
||||
encodable_substructure(a, b, c)
|
||||
})),
|
||||
}
|
||||
),
|
||||
associated_types: Vec::new(),
|
||||
};
|
||||
|
||||
trait_def.expand(cx, mitem, item, push)
|
||||
}
|
||||
|
||||
fn encodable_substructure(cx: &mut ExtCtxt, trait_span: Span,
|
||||
substr: &Substructure) -> P<Expr> {
|
||||
let encoder = substr.nonself_args[0].clone();
|
||||
// throw an underscore in front to suppress unused variable warnings
|
||||
let blkarg = cx.ident_of("_e");
|
||||
let blkencoder = cx.expr_ident(trait_span, blkarg);
|
||||
let encode = cx.ident_of("encode");
|
||||
|
||||
return match *substr.fields {
|
||||
Struct(ref fields) => {
|
||||
let emit_struct_field = cx.ident_of("emit_struct_field");
|
||||
let mut stmts = Vec::new();
|
||||
for (i, &FieldInfo {
|
||||
name,
|
||||
ref self_,
|
||||
span,
|
||||
..
|
||||
}) in fields.iter().enumerate() {
|
||||
let name = match name {
|
||||
Some(id) => id.name.as_str(),
|
||||
None => {
|
||||
token::intern_and_get_ident(&format!("_field{}", i))
|
||||
}
|
||||
};
|
||||
let enc = cx.expr_method_call(span, self_.clone(),
|
||||
encode, vec!(blkencoder.clone()));
|
||||
let lambda = cx.lambda_expr_1(span, enc, blkarg);
|
||||
let call = cx.expr_method_call(span, blkencoder.clone(),
|
||||
emit_struct_field,
|
||||
vec!(cx.expr_str(span, name),
|
||||
cx.expr_usize(span, i),
|
||||
lambda));
|
||||
|
||||
// last call doesn't need a try!
|
||||
let last = fields.len() - 1;
|
||||
let call = if i != last {
|
||||
cx.expr_try(span, call)
|
||||
} else {
|
||||
cx.expr(span, ExprRet(Some(call)))
|
||||
};
|
||||
stmts.push(cx.stmt_expr(call));
|
||||
}
|
||||
|
||||
// unit structs have no fields and need to return Ok()
|
||||
if stmts.is_empty() {
|
||||
let ret_ok = cx.expr(trait_span,
|
||||
ExprRet(Some(cx.expr_ok(trait_span,
|
||||
cx.expr_tuple(trait_span, vec![])))));
|
||||
stmts.push(cx.stmt_expr(ret_ok));
|
||||
}
|
||||
|
||||
let blk = cx.lambda_stmts_1(trait_span, stmts, blkarg);
|
||||
cx.expr_method_call(trait_span,
|
||||
encoder,
|
||||
cx.ident_of("emit_struct"),
|
||||
vec!(
|
||||
cx.expr_str(trait_span, substr.type_ident.name.as_str()),
|
||||
cx.expr_usize(trait_span, fields.len()),
|
||||
blk
|
||||
))
|
||||
}
|
||||
|
||||
EnumMatching(idx, variant, ref fields) => {
|
||||
// We're not generating an AST that the borrow checker is expecting,
|
||||
// so we need to generate a unique local variable to take the
|
||||
// mutable loan out on, otherwise we get conflicts which don't
|
||||
// actually exist.
|
||||
let me = cx.stmt_let(trait_span, false, blkarg, encoder);
|
||||
let encoder = cx.expr_ident(trait_span, blkarg);
|
||||
let emit_variant_arg = cx.ident_of("emit_enum_variant_arg");
|
||||
let mut stmts = Vec::new();
|
||||
if !fields.is_empty() {
|
||||
let last = fields.len() - 1;
|
||||
for (i, &FieldInfo { ref self_, span, .. }) in fields.iter().enumerate() {
|
||||
let enc = cx.expr_method_call(span, self_.clone(),
|
||||
encode, vec!(blkencoder.clone()));
|
||||
let lambda = cx.lambda_expr_1(span, enc, blkarg);
|
||||
let call = cx.expr_method_call(span, blkencoder.clone(),
|
||||
emit_variant_arg,
|
||||
vec!(cx.expr_usize(span, i),
|
||||
lambda));
|
||||
let call = if i != last {
|
||||
cx.expr_try(span, call)
|
||||
} else {
|
||||
cx.expr(span, ExprRet(Some(call)))
|
||||
};
|
||||
stmts.push(cx.stmt_expr(call));
|
||||
}
|
||||
} else {
|
||||
let ret_ok = cx.expr(trait_span,
|
||||
ExprRet(Some(cx.expr_ok(trait_span,
|
||||
cx.expr_tuple(trait_span, vec![])))));
|
||||
stmts.push(cx.stmt_expr(ret_ok));
|
||||
}
|
||||
|
||||
let blk = cx.lambda_stmts_1(trait_span, stmts, blkarg);
|
||||
let name = cx.expr_str(trait_span, variant.node.name.name.as_str());
|
||||
let call = cx.expr_method_call(trait_span, blkencoder,
|
||||
cx.ident_of("emit_enum_variant"),
|
||||
vec!(name,
|
||||
cx.expr_usize(trait_span, idx),
|
||||
cx.expr_usize(trait_span, fields.len()),
|
||||
blk));
|
||||
let blk = cx.lambda_expr_1(trait_span, call, blkarg);
|
||||
let ret = cx.expr_method_call(trait_span,
|
||||
encoder,
|
||||
cx.ident_of("emit_enum"),
|
||||
vec!(
|
||||
cx.expr_str(trait_span, substr.type_ident.name.as_str()),
|
||||
blk
|
||||
));
|
||||
cx.expr_block(cx.block(trait_span, vec!(me), Some(ret)))
|
||||
}
|
||||
|
||||
_ => cx.bug("expected Struct or EnumMatching in derive(Encodable)")
|
||||
};
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,283 +0,0 @@
|
||||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! A mini version of ast::Ty, which is easier to use, and features an explicit `Self` type to use
|
||||
//! when specifying impls to be derived.
|
||||
|
||||
pub use self::PtrTy::*;
|
||||
pub use self::Ty::*;
|
||||
|
||||
use ast;
|
||||
use ast::{Expr,Generics,Ident};
|
||||
use ext::base::ExtCtxt;
|
||||
use ext::build::AstBuilder;
|
||||
use codemap::{Span,respan};
|
||||
use owned_slice::OwnedSlice;
|
||||
use parse::token::special_idents;
|
||||
use ptr::P;
|
||||
|
||||
/// The types of pointers
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub enum PtrTy<'a> {
|
||||
/// &'lifetime mut
|
||||
Borrowed(Option<&'a str>, ast::Mutability),
|
||||
/// *mut
|
||||
Raw(ast::Mutability),
|
||||
}
|
||||
|
||||
/// A path, e.g. `::std::option::Option::<i32>` (global). Has support
|
||||
/// for type parameters and a lifetime.
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct Path<'a> {
|
||||
pub path: Vec<&'a str> ,
|
||||
pub lifetime: Option<&'a str>,
|
||||
pub params: Vec<Box<Ty<'a>>>,
|
||||
pub global: bool,
|
||||
}
|
||||
|
||||
impl<'a> Path<'a> {
|
||||
pub fn new<'r>(path: Vec<&'r str> ) -> Path<'r> {
|
||||
Path::new_(path, None, Vec::new(), true)
|
||||
}
|
||||
pub fn new_local<'r>(path: &'r str) -> Path<'r> {
|
||||
Path::new_(vec!( path ), None, Vec::new(), false)
|
||||
}
|
||||
pub fn new_<'r>(path: Vec<&'r str> ,
|
||||
lifetime: Option<&'r str>,
|
||||
params: Vec<Box<Ty<'r>>>,
|
||||
global: bool)
|
||||
-> Path<'r> {
|
||||
Path {
|
||||
path: path,
|
||||
lifetime: lifetime,
|
||||
params: params,
|
||||
global: global
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_ty(&self,
|
||||
cx: &ExtCtxt,
|
||||
span: Span,
|
||||
self_ty: Ident,
|
||||
self_generics: &Generics)
|
||||
-> P<ast::Ty> {
|
||||
cx.ty_path(self.to_path(cx, span, self_ty, self_generics))
|
||||
}
|
||||
pub fn to_path(&self,
|
||||
cx: &ExtCtxt,
|
||||
span: Span,
|
||||
self_ty: Ident,
|
||||
self_generics: &Generics)
|
||||
-> ast::Path {
|
||||
let idents = self.path.iter().map(|s| cx.ident_of(*s)).collect();
|
||||
let lt = mk_lifetimes(cx, span, &self.lifetime);
|
||||
let tys = self.params.iter().map(|t| t.to_ty(cx, span, self_ty, self_generics)).collect();
|
||||
|
||||
cx.path_all(span, self.global, idents, lt, tys, Vec::new())
|
||||
}
|
||||
}
|
||||
|
||||
/// A type. Supports pointers, Self, and literals
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub enum Ty<'a> {
|
||||
Self_,
|
||||
/// &/Box/ Ty
|
||||
Ptr(Box<Ty<'a>>, PtrTy<'a>),
|
||||
/// mod::mod::Type<[lifetime], [Params...]>, including a plain type
|
||||
/// parameter, and things like `i32`
|
||||
Literal(Path<'a>),
|
||||
/// includes unit
|
||||
Tuple(Vec<Ty<'a>> )
|
||||
}
|
||||
|
||||
pub fn borrowed_ptrty<'r>() -> PtrTy<'r> {
|
||||
Borrowed(None, ast::MutImmutable)
|
||||
}
|
||||
pub fn borrowed<'r>(ty: Box<Ty<'r>>) -> Ty<'r> {
|
||||
Ptr(ty, borrowed_ptrty())
|
||||
}
|
||||
|
||||
pub fn borrowed_explicit_self<'r>() -> Option<Option<PtrTy<'r>>> {
|
||||
Some(Some(borrowed_ptrty()))
|
||||
}
|
||||
|
||||
pub fn borrowed_self<'r>() -> Ty<'r> {
|
||||
borrowed(Box::new(Self_))
|
||||
}
|
||||
|
||||
pub fn nil_ty<'r>() -> Ty<'r> {
|
||||
Tuple(Vec::new())
|
||||
}
|
||||
|
||||
fn mk_lifetime(cx: &ExtCtxt, span: Span, lt: &Option<&str>) -> Option<ast::Lifetime> {
|
||||
match *lt {
|
||||
Some(ref s) => Some(cx.lifetime(span, cx.ident_of(*s).name)),
|
||||
None => None
|
||||
}
|
||||
}
|
||||
|
||||
fn mk_lifetimes(cx: &ExtCtxt, span: Span, lt: &Option<&str>) -> Vec<ast::Lifetime> {
|
||||
match *lt {
|
||||
Some(ref s) => vec!(cx.lifetime(span, cx.ident_of(*s).name)),
|
||||
None => vec!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Ty<'a> {
|
||||
pub fn to_ty(&self,
|
||||
cx: &ExtCtxt,
|
||||
span: Span,
|
||||
self_ty: Ident,
|
||||
self_generics: &Generics)
|
||||
-> P<ast::Ty> {
|
||||
match *self {
|
||||
Ptr(ref ty, ref ptr) => {
|
||||
let raw_ty = ty.to_ty(cx, span, self_ty, self_generics);
|
||||
match *ptr {
|
||||
Borrowed(ref lt, mutbl) => {
|
||||
let lt = mk_lifetime(cx, span, lt);
|
||||
cx.ty_rptr(span, raw_ty, lt, mutbl)
|
||||
}
|
||||
Raw(mutbl) => cx.ty_ptr(span, raw_ty, mutbl)
|
||||
}
|
||||
}
|
||||
Literal(ref p) => { p.to_ty(cx, span, self_ty, self_generics) }
|
||||
Self_ => {
|
||||
cx.ty_path(self.to_path(cx, span, self_ty, self_generics))
|
||||
}
|
||||
Tuple(ref fields) => {
|
||||
let ty = ast::TyTup(fields.iter()
|
||||
.map(|f| f.to_ty(cx, span, self_ty, self_generics))
|
||||
.collect());
|
||||
cx.ty(span, ty)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_path(&self,
|
||||
cx: &ExtCtxt,
|
||||
span: Span,
|
||||
self_ty: Ident,
|
||||
self_generics: &Generics)
|
||||
-> ast::Path {
|
||||
match *self {
|
||||
Self_ => {
|
||||
let self_params = self_generics.ty_params.map(|ty_param| {
|
||||
cx.ty_ident(span, ty_param.ident)
|
||||
});
|
||||
let lifetimes = self_generics.lifetimes.iter()
|
||||
.map(|d| d.lifetime)
|
||||
.collect();
|
||||
|
||||
cx.path_all(span, false, vec!(self_ty), lifetimes,
|
||||
self_params.into_vec(), Vec::new())
|
||||
}
|
||||
Literal(ref p) => {
|
||||
p.to_path(cx, span, self_ty, self_generics)
|
||||
}
|
||||
Ptr(..) => { cx.span_bug(span, "pointer in a path in generic `derive`") }
|
||||
Tuple(..) => { cx.span_bug(span, "tuple in a path in generic `derive`") }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn mk_ty_param(cx: &ExtCtxt,
|
||||
span: Span,
|
||||
name: &str,
|
||||
bounds: &[Path],
|
||||
self_ident: Ident,
|
||||
self_generics: &Generics)
|
||||
-> ast::TyParam {
|
||||
let bounds =
|
||||
bounds.iter().map(|b| {
|
||||
let path = b.to_path(cx, span, self_ident, self_generics);
|
||||
cx.typarambound(path)
|
||||
}).collect();
|
||||
cx.typaram(span, cx.ident_of(name), bounds, None)
|
||||
}
|
||||
|
||||
fn mk_generics(lifetimes: Vec<ast::LifetimeDef>, ty_params: Vec<ast::TyParam>)
|
||||
-> Generics {
|
||||
Generics {
|
||||
lifetimes: lifetimes,
|
||||
ty_params: OwnedSlice::from_vec(ty_params),
|
||||
where_clause: ast::WhereClause {
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
predicates: Vec::new(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Lifetimes and bounds on type parameters
|
||||
#[derive(Clone)]
|
||||
pub struct LifetimeBounds<'a> {
|
||||
pub lifetimes: Vec<(&'a str, Vec<&'a str>)>,
|
||||
pub bounds: Vec<(&'a str, Vec<Path<'a>>)>,
|
||||
}
|
||||
|
||||
impl<'a> LifetimeBounds<'a> {
|
||||
pub fn empty() -> LifetimeBounds<'a> {
|
||||
LifetimeBounds {
|
||||
lifetimes: Vec::new(), bounds: Vec::new()
|
||||
}
|
||||
}
|
||||
pub fn to_generics(&self,
|
||||
cx: &ExtCtxt,
|
||||
span: Span,
|
||||
self_ty: Ident,
|
||||
self_generics: &Generics)
|
||||
-> Generics {
|
||||
let lifetimes = self.lifetimes.iter().map(|&(ref lt, ref bounds)| {
|
||||
let bounds =
|
||||
bounds.iter().map(
|
||||
|b| cx.lifetime(span, cx.ident_of(*b).name)).collect();
|
||||
cx.lifetime_def(span, cx.ident_of(*lt).name, bounds)
|
||||
}).collect();
|
||||
let ty_params = self.bounds.iter().map(|t| {
|
||||
match *t {
|
||||
(ref name, ref bounds) => {
|
||||
mk_ty_param(cx,
|
||||
span,
|
||||
*name,
|
||||
bounds,
|
||||
self_ty,
|
||||
self_generics)
|
||||
}
|
||||
}
|
||||
}).collect();
|
||||
mk_generics(lifetimes, ty_params)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_explicit_self(cx: &ExtCtxt, span: Span, self_ptr: &Option<PtrTy>)
|
||||
-> (P<Expr>, ast::ExplicitSelf) {
|
||||
// this constructs a fresh `self` path, which will match the fresh `self` binding
|
||||
// created below.
|
||||
let self_path = cx.expr_self(span);
|
||||
match *self_ptr {
|
||||
None => {
|
||||
(self_path, respan(span, ast::SelfValue(special_idents::self_)))
|
||||
}
|
||||
Some(ref ptr) => {
|
||||
let self_ty = respan(
|
||||
span,
|
||||
match *ptr {
|
||||
Borrowed(ref lt, mutbl) => {
|
||||
let lt = lt.map(|s| cx.lifetime(span, cx.ident_of(s).name));
|
||||
ast::SelfRegion(lt, mutbl, special_idents::self_)
|
||||
}
|
||||
Raw(_) => cx.span_bug(span, "attempted to use *self in deriving definition")
|
||||
});
|
||||
let self_expr = cx.expr_deref(span, self_path);
|
||||
(self_expr, self_ty)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use ast::{MetaItem, Expr, MutMutable};
|
||||
use codemap::Span;
|
||||
use ext::base::{ExtCtxt, Annotatable};
|
||||
use ext::build::AstBuilder;
|
||||
use ext::deriving::generic::*;
|
||||
use ext::deriving::generic::ty::*;
|
||||
use ptr::P;
|
||||
|
||||
pub fn expand_deriving_hash(cx: &mut ExtCtxt,
|
||||
span: Span,
|
||||
mitem: &MetaItem,
|
||||
item: &Annotatable,
|
||||
push: &mut FnMut(Annotatable))
|
||||
{
|
||||
|
||||
let path = Path::new_(pathvec_std!(cx, core::hash::Hash), None,
|
||||
vec!(), true);
|
||||
let arg = Path::new_local("__H");
|
||||
let hash_trait_def = TraitDef {
|
||||
span: span,
|
||||
attributes: Vec::new(),
|
||||
path: path,
|
||||
additional_bounds: Vec::new(),
|
||||
generics: LifetimeBounds::empty(),
|
||||
is_unsafe: false,
|
||||
methods: vec!(
|
||||
MethodDef {
|
||||
name: "hash",
|
||||
generics: LifetimeBounds {
|
||||
lifetimes: Vec::new(),
|
||||
bounds: vec![("__H",
|
||||
vec![path_std!(cx, core::hash::Hasher)])],
|
||||
},
|
||||
explicit_self: borrowed_explicit_self(),
|
||||
args: vec!(Ptr(Box::new(Literal(arg)), Borrowed(None, MutMutable))),
|
||||
ret_ty: nil_ty(),
|
||||
attributes: vec![],
|
||||
is_unsafe: false,
|
||||
combine_substructure: combine_substructure(Box::new(|a, b, c| {
|
||||
hash_substructure(a, b, c)
|
||||
}))
|
||||
}
|
||||
),
|
||||
associated_types: Vec::new(),
|
||||
};
|
||||
|
||||
hash_trait_def.expand(cx, mitem, item, push);
|
||||
}
|
||||
|
||||
fn hash_substructure(cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P<Expr> {
|
||||
let state_expr = match (substr.nonself_args.len(), substr.nonself_args.get(0)) {
|
||||
(1, Some(o_f)) => o_f,
|
||||
_ => cx.span_bug(trait_span, "incorrect number of arguments in `derive(Hash)`")
|
||||
};
|
||||
let call_hash = |span, thing_expr| {
|
||||
let hash_path = {
|
||||
let strs = cx.std_path(&["hash", "Hash", "hash"]);
|
||||
|
||||
cx.expr_path(cx.path_global(span, strs))
|
||||
};
|
||||
let ref_thing = cx.expr_addr_of(span, thing_expr);
|
||||
let expr = cx.expr_call(span, hash_path, vec!(ref_thing, state_expr.clone()));
|
||||
cx.stmt_expr(expr)
|
||||
};
|
||||
let mut stmts = Vec::new();
|
||||
|
||||
let fields = match *substr.fields {
|
||||
Struct(ref fs) => fs,
|
||||
EnumMatching(index, variant, ref fs) => {
|
||||
// Determine the discriminant. We will feed this value to the byte
|
||||
// iteration function.
|
||||
let discriminant = match variant.node.disr_expr {
|
||||
Some(ref d) => d.clone(),
|
||||
None => cx.expr_usize(trait_span, index)
|
||||
};
|
||||
|
||||
stmts.push(call_hash(trait_span, discriminant));
|
||||
|
||||
fs
|
||||
}
|
||||
_ => cx.span_bug(trait_span, "impossible substructure in `derive(Hash)`")
|
||||
};
|
||||
|
||||
for &FieldInfo { ref self_, span, .. } in fields {
|
||||
stmts.push(call_hash(span, self_.clone()));
|
||||
}
|
||||
|
||||
cx.expr_block(cx.block(trait_span, stmts, None))
|
||||
}
|
||||
@@ -1,201 +0,0 @@
|
||||
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! The compiler code necessary to implement the `#[derive]` extensions.
|
||||
//!
|
||||
//! FIXME (#2810): hygiene. Search for "__" strings (in other files too). We also assume "extra" is
|
||||
//! the standard library, and "std" is the core library.
|
||||
|
||||
use ast::{MetaItem, MetaWord};
|
||||
use attr::AttrMetaMethods;
|
||||
use ext::base::{ExtCtxt, SyntaxEnv, MultiDecorator, MultiItemDecorator, MultiModifier, Annotatable};
|
||||
use ext::build::AstBuilder;
|
||||
use feature_gate;
|
||||
use codemap::Span;
|
||||
use parse::token::{intern, intern_and_get_ident};
|
||||
|
||||
macro_rules! pathvec {
|
||||
($($x:ident)::+) => (
|
||||
vec![ $( stringify!($x) ),+ ]
|
||||
)
|
||||
}
|
||||
|
||||
macro_rules! path {
|
||||
($($x:tt)*) => (
|
||||
::ext::deriving::generic::ty::Path::new( pathvec!( $($x)* ) )
|
||||
)
|
||||
}
|
||||
|
||||
macro_rules! path_local {
|
||||
($x:ident) => (
|
||||
::ext::deriving::generic::ty::Path::new_local(stringify!($x))
|
||||
)
|
||||
}
|
||||
|
||||
macro_rules! pathvec_std {
|
||||
($cx:expr, $first:ident :: $($rest:ident)::+) => ({
|
||||
let mut v = pathvec!($($rest)::+);
|
||||
if let Some(s) = $cx.crate_root {
|
||||
v.insert(0, s);
|
||||
}
|
||||
v
|
||||
})
|
||||
}
|
||||
|
||||
macro_rules! path_std {
|
||||
($($x:tt)*) => (
|
||||
::ext::deriving::generic::ty::Path::new( pathvec_std!( $($x)* ) )
|
||||
)
|
||||
}
|
||||
|
||||
pub mod bounds;
|
||||
pub mod clone;
|
||||
pub mod encodable;
|
||||
pub mod decodable;
|
||||
pub mod hash;
|
||||
pub mod debug;
|
||||
pub mod default;
|
||||
pub mod primitive;
|
||||
|
||||
#[path="cmp/partial_eq.rs"]
|
||||
pub mod partial_eq;
|
||||
#[path="cmp/eq.rs"]
|
||||
pub mod eq;
|
||||
#[path="cmp/partial_ord.rs"]
|
||||
pub mod partial_ord;
|
||||
#[path="cmp/ord.rs"]
|
||||
pub mod ord;
|
||||
|
||||
|
||||
pub mod generic;
|
||||
|
||||
fn expand_derive(cx: &mut ExtCtxt,
|
||||
span: Span,
|
||||
mitem: &MetaItem,
|
||||
annotatable: Annotatable)
|
||||
-> Annotatable {
|
||||
annotatable.map_item_or(|item| {
|
||||
item.map(|mut item| {
|
||||
if mitem.value_str().is_some() {
|
||||
cx.span_err(mitem.span, "unexpected value in `derive`");
|
||||
}
|
||||
|
||||
let traits = mitem.meta_item_list().unwrap_or(&[]);
|
||||
if traits.is_empty() {
|
||||
cx.span_warn(mitem.span, "empty trait list in `derive`");
|
||||
}
|
||||
|
||||
for titem in traits.iter().rev() {
|
||||
let tname = match titem.node {
|
||||
MetaWord(ref tname) => tname,
|
||||
_ => {
|
||||
cx.span_err(titem.span, "malformed `derive` entry");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
if !(is_builtin_trait(tname) || cx.ecfg.enable_custom_derive()) {
|
||||
feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic,
|
||||
"custom_derive",
|
||||
titem.span,
|
||||
feature_gate::GateIssue::Language,
|
||||
feature_gate::EXPLAIN_CUSTOM_DERIVE);
|
||||
continue;
|
||||
}
|
||||
|
||||
// #[derive(Foo, Bar)] expands to #[derive_Foo] #[derive_Bar]
|
||||
item.attrs.push(cx.attribute(titem.span, cx.meta_word(titem.span,
|
||||
intern_and_get_ident(&format!("derive_{}", tname)))));
|
||||
}
|
||||
|
||||
item
|
||||
})
|
||||
}, |a| {
|
||||
cx.span_err(span, "`derive` can only be applied to items");
|
||||
a
|
||||
})
|
||||
}
|
||||
|
||||
macro_rules! derive_traits {
|
||||
($( $name:expr => $func:path, )+) => {
|
||||
pub fn register_all(env: &mut SyntaxEnv) {
|
||||
// Define the #[derive_*] extensions.
|
||||
$({
|
||||
struct DeriveExtension;
|
||||
|
||||
impl MultiItemDecorator for DeriveExtension {
|
||||
fn expand(&self,
|
||||
ecx: &mut ExtCtxt,
|
||||
sp: Span,
|
||||
mitem: &MetaItem,
|
||||
annotatable: &Annotatable,
|
||||
push: &mut FnMut(Annotatable)) {
|
||||
warn_if_deprecated(ecx, sp, $name);
|
||||
$func(ecx, sp, mitem, annotatable, push);
|
||||
}
|
||||
}
|
||||
|
||||
env.insert(intern(concat!("derive_", $name)),
|
||||
MultiDecorator(Box::new(DeriveExtension)));
|
||||
})+
|
||||
|
||||
env.insert(intern("derive"),
|
||||
MultiModifier(Box::new(expand_derive)));
|
||||
}
|
||||
|
||||
fn is_builtin_trait(name: &str) -> bool {
|
||||
match name {
|
||||
$( $name )|+ => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
derive_traits! {
|
||||
"Clone" => clone::expand_deriving_clone,
|
||||
|
||||
"Hash" => hash::expand_deriving_hash,
|
||||
|
||||
"RustcEncodable" => encodable::expand_deriving_rustc_encodable,
|
||||
|
||||
"RustcDecodable" => decodable::expand_deriving_rustc_decodable,
|
||||
|
||||
"PartialEq" => partial_eq::expand_deriving_partial_eq,
|
||||
"Eq" => eq::expand_deriving_eq,
|
||||
"PartialOrd" => partial_ord::expand_deriving_partial_ord,
|
||||
"Ord" => ord::expand_deriving_ord,
|
||||
|
||||
"Debug" => debug::expand_deriving_debug,
|
||||
|
||||
"Default" => default::expand_deriving_default,
|
||||
|
||||
"FromPrimitive" => primitive::expand_deriving_from_primitive,
|
||||
|
||||
"Send" => bounds::expand_deriving_unsafe_bound,
|
||||
"Sync" => bounds::expand_deriving_unsafe_bound,
|
||||
"Copy" => bounds::expand_deriving_copy,
|
||||
|
||||
// deprecated
|
||||
"Encodable" => encodable::expand_deriving_encodable,
|
||||
"Decodable" => decodable::expand_deriving_decodable,
|
||||
}
|
||||
|
||||
#[inline] // because `name` is a compile-time constant
|
||||
fn warn_if_deprecated(ecx: &mut ExtCtxt, sp: Span, name: &str) {
|
||||
if let Some(replacement) = match name {
|
||||
"Encodable" => Some("RustcEncodable"),
|
||||
"Decodable" => Some("RustcDecodable"),
|
||||
_ => None,
|
||||
} {
|
||||
ecx.span_warn(sp, &format!("derive({}) is deprecated in favor of derive({})",
|
||||
name, replacement));
|
||||
}
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use ast::{MetaItem, Expr};
|
||||
use ast;
|
||||
use codemap::Span;
|
||||
use ext::base::{ExtCtxt, Annotatable};
|
||||
use ext::build::AstBuilder;
|
||||
use ext::deriving::generic::*;
|
||||
use ext::deriving::generic::ty::*;
|
||||
use parse::token::InternedString;
|
||||
use ptr::P;
|
||||
|
||||
pub fn expand_deriving_from_primitive(cx: &mut ExtCtxt,
|
||||
span: Span,
|
||||
mitem: &MetaItem,
|
||||
item: &Annotatable,
|
||||
push: &mut FnMut(Annotatable))
|
||||
{
|
||||
let inline = cx.meta_word(span, InternedString::new("inline"));
|
||||
let attrs = vec!(cx.attribute(span, inline));
|
||||
let trait_def = TraitDef {
|
||||
span: span,
|
||||
attributes: Vec::new(),
|
||||
path: path_std!(cx, core::num::FromPrimitive),
|
||||
additional_bounds: Vec::new(),
|
||||
generics: LifetimeBounds::empty(),
|
||||
is_unsafe: false,
|
||||
methods: vec!(
|
||||
MethodDef {
|
||||
name: "from_i64",
|
||||
generics: LifetimeBounds::empty(),
|
||||
explicit_self: None,
|
||||
args: vec!(Literal(path_local!(i64))),
|
||||
ret_ty: Literal(Path::new_(pathvec_std!(cx, core::option::Option),
|
||||
None,
|
||||
vec!(Box::new(Self_)),
|
||||
true)),
|
||||
// #[inline] liable to cause code-bloat
|
||||
attributes: attrs.clone(),
|
||||
is_unsafe: false,
|
||||
combine_substructure: combine_substructure(Box::new(|c, s, sub| {
|
||||
cs_from("i64", c, s, sub)
|
||||
})),
|
||||
},
|
||||
MethodDef {
|
||||
name: "from_u64",
|
||||
generics: LifetimeBounds::empty(),
|
||||
explicit_self: None,
|
||||
args: vec!(Literal(path_local!(u64))),
|
||||
ret_ty: Literal(Path::new_(pathvec_std!(cx, core::option::Option),
|
||||
None,
|
||||
vec!(Box::new(Self_)),
|
||||
true)),
|
||||
// #[inline] liable to cause code-bloat
|
||||
attributes: attrs,
|
||||
is_unsafe: false,
|
||||
combine_substructure: combine_substructure(Box::new(|c, s, sub| {
|
||||
cs_from("u64", c, s, sub)
|
||||
})),
|
||||
}
|
||||
),
|
||||
associated_types: Vec::new(),
|
||||
};
|
||||
|
||||
trait_def.expand(cx, mitem, item, push)
|
||||
}
|
||||
|
||||
fn cs_from(name: &str, cx: &mut ExtCtxt, trait_span: Span, substr: &Substructure) -> P<Expr> {
|
||||
let n = match (substr.nonself_args.len(), substr.nonself_args.get(0)) {
|
||||
(1, Some(o_f)) => o_f,
|
||||
_ => cx.span_bug(trait_span, "incorrect number of arguments in `derive(FromPrimitive)`")
|
||||
};
|
||||
|
||||
match *substr.fields {
|
||||
StaticStruct(..) => {
|
||||
cx.span_err(trait_span, "`FromPrimitive` cannot be derived for structs");
|
||||
return cx.expr_fail(trait_span, InternedString::new(""));
|
||||
}
|
||||
StaticEnum(enum_def, _) => {
|
||||
if enum_def.variants.is_empty() {
|
||||
cx.span_err(trait_span,
|
||||
"`FromPrimitive` cannot be derived for enums with no variants");
|
||||
return cx.expr_fail(trait_span, InternedString::new(""));
|
||||
}
|
||||
|
||||
let mut arms = Vec::new();
|
||||
|
||||
for variant in &enum_def.variants {
|
||||
let def = &variant.node.data;
|
||||
if !def.is_unit() {
|
||||
cx.span_err(trait_span, "`FromPrimitive` cannot be derived \
|
||||
for enums with non-unit variants");
|
||||
return cx.expr_fail(trait_span,
|
||||
InternedString::new(""));
|
||||
}
|
||||
|
||||
let span = variant.span;
|
||||
|
||||
// expr for `$n == $variant as $name`
|
||||
let path = cx.path(span, vec![substr.type_ident, variant.node.name]);
|
||||
let variant = cx.expr_path(path);
|
||||
let ty = cx.ty_ident(span, cx.ident_of(name));
|
||||
let cast = cx.expr_cast(span, variant.clone(), ty);
|
||||
let guard = cx.expr_binary(span, ast::BiEq, n.clone(), cast);
|
||||
|
||||
// expr for `Some($variant)`
|
||||
let body = cx.expr_some(span, variant);
|
||||
|
||||
// arm for `_ if $guard => $body`
|
||||
let arm = ast::Arm {
|
||||
attrs: vec!(),
|
||||
pats: vec!(cx.pat_wild(span)),
|
||||
guard: Some(guard),
|
||||
body: body,
|
||||
};
|
||||
|
||||
arms.push(arm);
|
||||
}
|
||||
|
||||
// arm for `_ => None`
|
||||
let arm = ast::Arm {
|
||||
attrs: vec!(),
|
||||
pats: vec!(cx.pat_wild(trait_span)),
|
||||
guard: None,
|
||||
body: cx.expr_none(trait_span),
|
||||
};
|
||||
arms.push(arm);
|
||||
|
||||
cx.expr_match(trait_span, n.clone(), arms)
|
||||
}
|
||||
_ => cx.span_bug(trait_span, "expected StaticEnum in derive(FromPrimitive)")
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
/*
|
||||
* The compiler code necessary to support the env! extension. Eventually this
|
||||
* should all get sucked into either the compiler syntax extension plugin
|
||||
* interface.
|
||||
*/
|
||||
|
||||
use ast;
|
||||
use codemap::Span;
|
||||
use ext::base::*;
|
||||
use ext::base;
|
||||
use ext::build::AstBuilder;
|
||||
use parse::token;
|
||||
|
||||
use std::env;
|
||||
|
||||
pub fn expand_option_env<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
|
||||
-> Box<base::MacResult+'cx> {
|
||||
let var = match get_single_str_from_tts(cx, sp, tts, "option_env!") {
|
||||
None => return DummyResult::expr(sp),
|
||||
Some(v) => v
|
||||
};
|
||||
|
||||
let e = match env::var(&var[..]) {
|
||||
Err(..) => {
|
||||
cx.expr_path(cx.path_all(sp,
|
||||
true,
|
||||
cx.std_path(&["option", "Option", "None"]),
|
||||
Vec::new(),
|
||||
vec!(cx.ty_rptr(sp,
|
||||
cx.ty_ident(sp,
|
||||
cx.ident_of("str")),
|
||||
Some(cx.lifetime(sp,
|
||||
cx.ident_of(
|
||||
"'static").name)),
|
||||
ast::MutImmutable)),
|
||||
Vec::new()))
|
||||
}
|
||||
Ok(s) => {
|
||||
cx.expr_call_global(sp,
|
||||
cx.std_path(&["option", "Option", "Some"]),
|
||||
vec!(cx.expr_str(sp,
|
||||
token::intern_and_get_ident(
|
||||
&s[..]))))
|
||||
}
|
||||
};
|
||||
MacEager::expr(e)
|
||||
}
|
||||
|
||||
pub fn expand_env<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
|
||||
-> Box<base::MacResult+'cx> {
|
||||
let mut exprs = match get_exprs_from_tts(cx, sp, tts) {
|
||||
Some(ref exprs) if exprs.is_empty() => {
|
||||
cx.span_err(sp, "env! takes 1 or 2 arguments");
|
||||
return DummyResult::expr(sp);
|
||||
}
|
||||
None => return DummyResult::expr(sp),
|
||||
Some(exprs) => exprs.into_iter()
|
||||
};
|
||||
|
||||
let var = match expr_to_string(cx,
|
||||
exprs.next().unwrap(),
|
||||
"expected string literal") {
|
||||
None => return DummyResult::expr(sp),
|
||||
Some((v, _style)) => v
|
||||
};
|
||||
let msg = match exprs.next() {
|
||||
None => {
|
||||
token::intern_and_get_ident(&format!("environment variable `{}` \
|
||||
not defined",
|
||||
var))
|
||||
}
|
||||
Some(second) => {
|
||||
match expr_to_string(cx, second, "expected string literal") {
|
||||
None => return DummyResult::expr(sp),
|
||||
Some((s, _style)) => s
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match exprs.next() {
|
||||
None => {}
|
||||
Some(_) => {
|
||||
cx.span_err(sp, "env! takes 1 or 2 arguments");
|
||||
return DummyResult::expr(sp);
|
||||
}
|
||||
}
|
||||
|
||||
let e = match env::var(&var[..]) {
|
||||
Err(_) => {
|
||||
cx.span_err(sp, &msg);
|
||||
cx.expr_usize(sp, 0)
|
||||
}
|
||||
Ok(s) => cx.expr_str(sp, token::intern_and_get_ident(&s))
|
||||
};
|
||||
MacEager::expr(e)
|
||||
}
|
||||
@@ -21,7 +21,7 @@ use attr::{AttrMetaMethods, WithAttrs};
|
||||
use codemap;
|
||||
use codemap::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
|
||||
use ext::base::*;
|
||||
use feature_gate::{self, Features, GatedCfgAttr};
|
||||
use feature_gate::{self, Features};
|
||||
use fold;
|
||||
use fold::*;
|
||||
use util::move_map::MoveMap;
|
||||
@@ -1276,15 +1276,11 @@ impl<'feat> ExpansionConfig<'feat> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expand_crate<'feat>(parse_sess: &parse::ParseSess,
|
||||
cfg: ExpansionConfig<'feat>,
|
||||
// these are the macros being imported to this crate:
|
||||
imported_macros: Vec<ast::MacroDef>,
|
||||
user_exts: Vec<NamedSyntaxExtension>,
|
||||
feature_gated_cfgs: &mut Vec<GatedCfgAttr>,
|
||||
c: Crate) -> (Crate, HashSet<Name>) {
|
||||
let mut cx = ExtCtxt::new(parse_sess, c.config.clone(), cfg,
|
||||
feature_gated_cfgs);
|
||||
pub fn expand_crate(mut cx: ExtCtxt,
|
||||
// these are the macros being imported to this crate:
|
||||
imported_macros: Vec<ast::MacroDef>,
|
||||
user_exts: Vec<NamedSyntaxExtension>,
|
||||
c: Crate) -> (Crate, HashSet<Name>) {
|
||||
if std_inject::no_core(&c) {
|
||||
cx.crate_root = None;
|
||||
} else if std_inject::no_std(&c) {
|
||||
@@ -1305,7 +1301,7 @@ pub fn expand_crate<'feat>(parse_sess: &parse::ParseSess,
|
||||
|
||||
let mut ret = expander.fold_crate(c);
|
||||
ret.exported_macros = expander.cx.exported_macros.clone();
|
||||
parse_sess.span_diagnostic.handler().abort_if_errors();
|
||||
cx.parse_sess.span_diagnostic.handler().abort_if_errors();
|
||||
ret
|
||||
};
|
||||
return (ret, cx.syntax_env.names);
|
||||
|
||||
@@ -1,715 +0,0 @@
|
||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use self::ArgumentType::*;
|
||||
use self::Position::*;
|
||||
|
||||
use ast;
|
||||
use codemap::{Span, respan};
|
||||
use ext::base::*;
|
||||
use ext::base;
|
||||
use ext::build::AstBuilder;
|
||||
use fmt_macros as parse;
|
||||
use fold::Folder;
|
||||
use parse::token::special_idents;
|
||||
use parse::token;
|
||||
use ptr::P;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum ArgumentType {
|
||||
Known(String),
|
||||
Unsigned
|
||||
}
|
||||
|
||||
enum Position {
|
||||
Exact(usize),
|
||||
Named(String),
|
||||
}
|
||||
|
||||
struct Context<'a, 'b:'a> {
|
||||
ecx: &'a mut ExtCtxt<'b>,
|
||||
/// The macro's call site. References to unstable formatting internals must
|
||||
/// use this span to pass the stability checker.
|
||||
macsp: Span,
|
||||
/// The span of the format string literal.
|
||||
fmtsp: Span,
|
||||
|
||||
/// Parsed argument expressions and the types that we've found so far for
|
||||
/// them.
|
||||
args: Vec<P<ast::Expr>>,
|
||||
arg_types: Vec<Option<ArgumentType>>,
|
||||
/// Parsed named expressions and the types that we've found for them so far.
|
||||
/// Note that we keep a side-array of the ordering of the named arguments
|
||||
/// found to be sure that we can translate them in the same order that they
|
||||
/// were declared in.
|
||||
names: HashMap<String, P<ast::Expr>>,
|
||||
name_types: HashMap<String, ArgumentType>,
|
||||
name_ordering: Vec<String>,
|
||||
|
||||
/// The latest consecutive literal strings, or empty if there weren't any.
|
||||
literal: String,
|
||||
|
||||
/// Collection of the compiled `rt::Argument` structures
|
||||
pieces: Vec<P<ast::Expr>>,
|
||||
/// Collection of string literals
|
||||
str_pieces: Vec<P<ast::Expr>>,
|
||||
/// Stays `true` if all formatting parameters are default (as in "{}{}").
|
||||
all_pieces_simple: bool,
|
||||
|
||||
name_positions: HashMap<String, usize>,
|
||||
|
||||
/// Updated as arguments are consumed or methods are entered
|
||||
nest_level: usize,
|
||||
next_arg: usize,
|
||||
}
|
||||
|
||||
/// Parses the arguments from the given list of tokens, returning None
|
||||
/// if there's a parse error so we can continue parsing other format!
|
||||
/// expressions.
|
||||
///
|
||||
/// If parsing succeeds, the return value is:
|
||||
/// ```ignore
|
||||
/// Some((fmtstr, unnamed arguments, ordering of named arguments,
|
||||
/// named arguments))
|
||||
/// ```
|
||||
fn parse_args(ecx: &mut ExtCtxt, sp: Span, tts: &[ast::TokenTree])
|
||||
-> Option<(P<ast::Expr>, Vec<P<ast::Expr>>, Vec<String>,
|
||||
HashMap<String, P<ast::Expr>>)> {
|
||||
let mut args = Vec::new();
|
||||
let mut names = HashMap::<String, P<ast::Expr>>::new();
|
||||
let mut order = Vec::new();
|
||||
|
||||
let mut p = ecx.new_parser_from_tts(tts);
|
||||
|
||||
if p.token == token::Eof {
|
||||
ecx.span_err(sp, "requires at least a format string argument");
|
||||
return None;
|
||||
}
|
||||
let fmtstr = panictry!(p.parse_expr());
|
||||
let mut named = false;
|
||||
while p.token != token::Eof {
|
||||
if !panictry!(p.eat(&token::Comma)) {
|
||||
ecx.span_err(sp, "expected token: `,`");
|
||||
return None;
|
||||
}
|
||||
if p.token == token::Eof { break } // accept trailing commas
|
||||
if named || (p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq)) {
|
||||
named = true;
|
||||
let ident = match p.token {
|
||||
token::Ident(i, _) => {
|
||||
panictry!(p.bump());
|
||||
i
|
||||
}
|
||||
_ if named => {
|
||||
ecx.span_err(p.span,
|
||||
"expected ident, positional arguments \
|
||||
cannot follow named arguments");
|
||||
return None;
|
||||
}
|
||||
_ => {
|
||||
ecx.span_err(p.span,
|
||||
&format!("expected ident for named argument, found `{}`",
|
||||
p.this_token_to_string()));
|
||||
return None;
|
||||
}
|
||||
};
|
||||
let name: &str = &ident.name.as_str();
|
||||
|
||||
panictry!(p.expect(&token::Eq));
|
||||
let e = panictry!(p.parse_expr());
|
||||
match names.get(name) {
|
||||
None => {}
|
||||
Some(prev) => {
|
||||
ecx.span_err(e.span,
|
||||
&format!("duplicate argument named `{}`",
|
||||
name));
|
||||
ecx.parse_sess.span_diagnostic.span_note(prev.span, "previously here");
|
||||
continue
|
||||
}
|
||||
}
|
||||
order.push(name.to_string());
|
||||
names.insert(name.to_string(), e);
|
||||
} else {
|
||||
args.push(panictry!(p.parse_expr()));
|
||||
}
|
||||
}
|
||||
Some((fmtstr, args, order, names))
|
||||
}
|
||||
|
||||
impl<'a, 'b> Context<'a, 'b> {
|
||||
/// Verifies one piece of a parse string. All errors are not emitted as
|
||||
/// fatal so we can continue giving errors about this and possibly other
|
||||
/// format strings.
|
||||
fn verify_piece(&mut self, p: &parse::Piece) {
|
||||
match *p {
|
||||
parse::String(..) => {}
|
||||
parse::NextArgument(ref arg) => {
|
||||
// width/precision first, if they have implicit positional
|
||||
// parameters it makes more sense to consume them first.
|
||||
self.verify_count(arg.format.width);
|
||||
self.verify_count(arg.format.precision);
|
||||
|
||||
// argument second, if it's an implicit positional parameter
|
||||
// it's written second, so it should come after width/precision.
|
||||
let pos = match arg.position {
|
||||
parse::ArgumentNext => {
|
||||
let i = self.next_arg;
|
||||
if self.check_positional_ok() {
|
||||
self.next_arg += 1;
|
||||
}
|
||||
Exact(i)
|
||||
}
|
||||
parse::ArgumentIs(i) => Exact(i),
|
||||
parse::ArgumentNamed(s) => Named(s.to_string()),
|
||||
};
|
||||
|
||||
let ty = Known(arg.format.ty.to_string());
|
||||
self.verify_arg_type(pos, ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_count(&mut self, c: parse::Count) {
|
||||
match c {
|
||||
parse::CountImplied | parse::CountIs(..) => {}
|
||||
parse::CountIsParam(i) => {
|
||||
self.verify_arg_type(Exact(i), Unsigned);
|
||||
}
|
||||
parse::CountIsName(s) => {
|
||||
self.verify_arg_type(Named(s.to_string()), Unsigned);
|
||||
}
|
||||
parse::CountIsNextParam => {
|
||||
if self.check_positional_ok() {
|
||||
let next_arg = self.next_arg;
|
||||
self.verify_arg_type(Exact(next_arg), Unsigned);
|
||||
self.next_arg += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_positional_ok(&mut self) -> bool {
|
||||
if self.nest_level != 0 {
|
||||
self.ecx.span_err(self.fmtsp, "cannot use implicit positional \
|
||||
arguments nested inside methods");
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fn describe_num_args(&self) -> String {
|
||||
match self.args.len() {
|
||||
0 => "no arguments given".to_string(),
|
||||
1 => "there is 1 argument".to_string(),
|
||||
x => format!("there are {} arguments", x),
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_arg_type(&mut self, arg: Position, ty: ArgumentType) {
|
||||
match arg {
|
||||
Exact(arg) => {
|
||||
if self.args.len() <= arg {
|
||||
let msg = format!("invalid reference to argument `{}` ({})",
|
||||
arg, self.describe_num_args());
|
||||
|
||||
self.ecx.span_err(self.fmtsp, &msg[..]);
|
||||
return;
|
||||
}
|
||||
{
|
||||
let arg_type = match self.arg_types[arg] {
|
||||
None => None,
|
||||
Some(ref x) => Some(x)
|
||||
};
|
||||
self.verify_same(self.args[arg].span, &ty, arg_type);
|
||||
}
|
||||
if self.arg_types[arg].is_none() {
|
||||
self.arg_types[arg] = Some(ty);
|
||||
}
|
||||
}
|
||||
|
||||
Named(name) => {
|
||||
let span = match self.names.get(&name) {
|
||||
Some(e) => e.span,
|
||||
None => {
|
||||
let msg = format!("there is no argument named `{}`", name);
|
||||
self.ecx.span_err(self.fmtsp, &msg[..]);
|
||||
return;
|
||||
}
|
||||
};
|
||||
self.verify_same(span, &ty, self.name_types.get(&name));
|
||||
if !self.name_types.contains_key(&name) {
|
||||
self.name_types.insert(name.clone(), ty);
|
||||
}
|
||||
// Assign this named argument a slot in the arguments array if
|
||||
// it hasn't already been assigned a slot.
|
||||
if !self.name_positions.contains_key(&name) {
|
||||
let slot = self.name_positions.len();
|
||||
self.name_positions.insert(name, slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// When we're keeping track of the types that are declared for certain
|
||||
/// arguments, we assume that `None` means we haven't seen this argument
|
||||
/// yet, `Some(None)` means that we've seen the argument, but no format was
|
||||
/// specified, and `Some(Some(x))` means that the argument was declared to
|
||||
/// have type `x`.
|
||||
///
|
||||
/// Obviously `Some(Some(x)) != Some(Some(y))`, but we consider it true
|
||||
/// that: `Some(None) == Some(Some(x))`
|
||||
fn verify_same(&self,
|
||||
sp: Span,
|
||||
ty: &ArgumentType,
|
||||
before: Option<&ArgumentType>) {
|
||||
let cur = match before {
|
||||
None => return,
|
||||
Some(t) => t,
|
||||
};
|
||||
if *ty == *cur {
|
||||
return
|
||||
}
|
||||
match (cur, ty) {
|
||||
(&Known(ref cur), &Known(ref ty)) => {
|
||||
self.ecx.span_err(sp,
|
||||
&format!("argument redeclared with type `{}` when \
|
||||
it was previously `{}`",
|
||||
*ty,
|
||||
*cur));
|
||||
}
|
||||
(&Known(ref cur), _) => {
|
||||
self.ecx.span_err(sp,
|
||||
&format!("argument used to format with `{}` was \
|
||||
attempted to not be used for formatting",
|
||||
*cur));
|
||||
}
|
||||
(_, &Known(ref ty)) => {
|
||||
self.ecx.span_err(sp,
|
||||
&format!("argument previously used as a format \
|
||||
argument attempted to be used as `{}`",
|
||||
*ty));
|
||||
}
|
||||
(_, _) => {
|
||||
self.ecx.span_err(sp, "argument declared with multiple formats");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn rtpath(ecx: &ExtCtxt, s: &str) -> Vec<ast::Ident> {
|
||||
ecx.std_path(&["fmt", "rt", "v1", s])
|
||||
}
|
||||
|
||||
fn trans_count(&self, c: parse::Count) -> P<ast::Expr> {
|
||||
let sp = self.macsp;
|
||||
let count = |c, arg| {
|
||||
let mut path = Context::rtpath(self.ecx, "Count");
|
||||
path.push(self.ecx.ident_of(c));
|
||||
match arg {
|
||||
Some(arg) => self.ecx.expr_call_global(sp, path, vec![arg]),
|
||||
None => self.ecx.expr_path(self.ecx.path_global(sp, path)),
|
||||
}
|
||||
};
|
||||
match c {
|
||||
parse::CountIs(i) => count("Is", Some(self.ecx.expr_usize(sp, i))),
|
||||
parse::CountIsParam(i) => {
|
||||
count("Param", Some(self.ecx.expr_usize(sp, i)))
|
||||
}
|
||||
parse::CountImplied => count("Implied", None),
|
||||
parse::CountIsNextParam => count("NextParam", None),
|
||||
parse::CountIsName(n) => {
|
||||
let i = match self.name_positions.get(n) {
|
||||
Some(&i) => i,
|
||||
None => 0, // error already emitted elsewhere
|
||||
};
|
||||
let i = i + self.args.len();
|
||||
count("Param", Some(self.ecx.expr_usize(sp, i)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Translate the accumulated string literals to a literal expression
|
||||
fn trans_literal_string(&mut self) -> P<ast::Expr> {
|
||||
let sp = self.fmtsp;
|
||||
let s = token::intern_and_get_ident(&self.literal);
|
||||
self.literal.clear();
|
||||
self.ecx.expr_str(sp, s)
|
||||
}
|
||||
|
||||
/// Translate a `parse::Piece` to a static `rt::Argument` or append
|
||||
/// to the `literal` string.
|
||||
fn trans_piece(&mut self, piece: &parse::Piece) -> Option<P<ast::Expr>> {
|
||||
let sp = self.macsp;
|
||||
match *piece {
|
||||
parse::String(s) => {
|
||||
self.literal.push_str(s);
|
||||
None
|
||||
}
|
||||
parse::NextArgument(ref arg) => {
|
||||
// Translate the position
|
||||
let pos = {
|
||||
let pos = |c, arg| {
|
||||
let mut path = Context::rtpath(self.ecx, "Position");
|
||||
path.push(self.ecx.ident_of(c));
|
||||
match arg {
|
||||
Some(i) => {
|
||||
let arg = self.ecx.expr_usize(sp, i);
|
||||
self.ecx.expr_call_global(sp, path, vec![arg])
|
||||
}
|
||||
None => {
|
||||
self.ecx.expr_path(self.ecx.path_global(sp, path))
|
||||
}
|
||||
}
|
||||
};
|
||||
match arg.position {
|
||||
// These two have a direct mapping
|
||||
parse::ArgumentNext => pos("Next", None),
|
||||
parse::ArgumentIs(i) => pos("At", Some(i)),
|
||||
|
||||
// Named arguments are converted to positional arguments
|
||||
// at the end of the list of arguments
|
||||
parse::ArgumentNamed(n) => {
|
||||
let i = match self.name_positions.get(n) {
|
||||
Some(&i) => i,
|
||||
None => 0, // error already emitted elsewhere
|
||||
};
|
||||
let i = i + self.args.len();
|
||||
pos("At", Some(i))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let simple_arg = parse::Argument {
|
||||
position: parse::ArgumentNext,
|
||||
format: parse::FormatSpec {
|
||||
fill: arg.format.fill,
|
||||
align: parse::AlignUnknown,
|
||||
flags: 0,
|
||||
precision: parse::CountImplied,
|
||||
width: parse::CountImplied,
|
||||
ty: arg.format.ty
|
||||
}
|
||||
};
|
||||
|
||||
let fill = match arg.format.fill { Some(c) => c, None => ' ' };
|
||||
|
||||
if *arg != simple_arg || fill != ' ' {
|
||||
self.all_pieces_simple = false;
|
||||
}
|
||||
|
||||
// Translate the format
|
||||
let fill = self.ecx.expr_lit(sp, ast::LitChar(fill));
|
||||
let align = |name| {
|
||||
let mut p = Context::rtpath(self.ecx, "Alignment");
|
||||
p.push(self.ecx.ident_of(name));
|
||||
self.ecx.path_global(sp, p)
|
||||
};
|
||||
let align = match arg.format.align {
|
||||
parse::AlignLeft => align("Left"),
|
||||
parse::AlignRight => align("Right"),
|
||||
parse::AlignCenter => align("Center"),
|
||||
parse::AlignUnknown => align("Unknown"),
|
||||
};
|
||||
let align = self.ecx.expr_path(align);
|
||||
let flags = self.ecx.expr_u32(sp, arg.format.flags);
|
||||
let prec = self.trans_count(arg.format.precision);
|
||||
let width = self.trans_count(arg.format.width);
|
||||
let path = self.ecx.path_global(sp, Context::rtpath(self.ecx, "FormatSpec"));
|
||||
let fmt = self.ecx.expr_struct(sp, path, vec!(
|
||||
self.ecx.field_imm(sp, self.ecx.ident_of("fill"), fill),
|
||||
self.ecx.field_imm(sp, self.ecx.ident_of("align"), align),
|
||||
self.ecx.field_imm(sp, self.ecx.ident_of("flags"), flags),
|
||||
self.ecx.field_imm(sp, self.ecx.ident_of("precision"), prec),
|
||||
self.ecx.field_imm(sp, self.ecx.ident_of("width"), width)));
|
||||
|
||||
let path = self.ecx.path_global(sp, Context::rtpath(self.ecx, "Argument"));
|
||||
Some(self.ecx.expr_struct(sp, path, vec!(
|
||||
self.ecx.field_imm(sp, self.ecx.ident_of("position"), pos),
|
||||
self.ecx.field_imm(sp, self.ecx.ident_of("format"), fmt))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn static_array(ecx: &mut ExtCtxt,
|
||||
name: &str,
|
||||
piece_ty: P<ast::Ty>,
|
||||
pieces: Vec<P<ast::Expr>>)
|
||||
-> P<ast::Expr> {
|
||||
let sp = piece_ty.span;
|
||||
let ty = ecx.ty_rptr(sp,
|
||||
ecx.ty(sp, ast::TyVec(piece_ty)),
|
||||
Some(ecx.lifetime(sp, special_idents::static_lifetime.name)),
|
||||
ast::MutImmutable);
|
||||
let slice = ecx.expr_vec_slice(sp, pieces);
|
||||
// static instead of const to speed up codegen by not requiring this to be inlined
|
||||
let st = ast::ItemStatic(ty, ast::MutImmutable, slice);
|
||||
|
||||
let name = ecx.ident_of(name);
|
||||
let item = ecx.item(sp, name, vec![], st);
|
||||
let decl = respan(sp, ast::DeclItem(item));
|
||||
|
||||
// Wrap the declaration in a block so that it forms a single expression.
|
||||
ecx.expr_block(ecx.block(sp,
|
||||
vec![P(respan(sp, ast::StmtDecl(P(decl), ast::DUMMY_NODE_ID)))],
|
||||
Some(ecx.expr_ident(sp, name))))
|
||||
}
|
||||
|
||||
/// Actually builds the expression which the iformat! block will be expanded
|
||||
/// to
|
||||
fn into_expr(mut self) -> P<ast::Expr> {
|
||||
let mut locals = Vec::new();
|
||||
let mut names = vec![None; self.name_positions.len()];
|
||||
let mut pats = Vec::new();
|
||||
let mut heads = Vec::new();
|
||||
|
||||
// First, build up the static array which will become our precompiled
|
||||
// format "string"
|
||||
let static_lifetime = self.ecx.lifetime(self.fmtsp, special_idents::static_lifetime.name);
|
||||
let piece_ty = self.ecx.ty_rptr(
|
||||
self.fmtsp,
|
||||
self.ecx.ty_ident(self.fmtsp, self.ecx.ident_of("str")),
|
||||
Some(static_lifetime),
|
||||
ast::MutImmutable);
|
||||
let pieces = Context::static_array(self.ecx,
|
||||
"__STATIC_FMTSTR",
|
||||
piece_ty,
|
||||
self.str_pieces);
|
||||
|
||||
|
||||
// Right now there is a bug such that for the expression:
|
||||
// foo(bar(&1))
|
||||
// the lifetime of `1` doesn't outlast the call to `bar`, so it's not
|
||||
// valid for the call to `foo`. To work around this all arguments to the
|
||||
// format! string are shoved into locals. Furthermore, we shove the address
|
||||
// of each variable because we don't want to move out of the arguments
|
||||
// passed to this function.
|
||||
for (i, e) in self.args.into_iter().enumerate() {
|
||||
let arg_ty = match self.arg_types[i].as_ref() {
|
||||
Some(ty) => ty,
|
||||
None => continue // error already generated
|
||||
};
|
||||
|
||||
let name = self.ecx.ident_of(&format!("__arg{}", i));
|
||||
pats.push(self.ecx.pat_ident(e.span, name));
|
||||
locals.push(Context::format_arg(self.ecx, self.macsp, e.span, arg_ty,
|
||||
self.ecx.expr_ident(e.span, name)));
|
||||
heads.push(self.ecx.expr_addr_of(e.span, e));
|
||||
}
|
||||
for name in &self.name_ordering {
|
||||
let e = match self.names.remove(name) {
|
||||
Some(e) => e,
|
||||
None => continue
|
||||
};
|
||||
let arg_ty = match self.name_types.get(name) {
|
||||
Some(ty) => ty,
|
||||
None => continue
|
||||
};
|
||||
|
||||
let lname = self.ecx.ident_of(&format!("__arg{}",
|
||||
*name));
|
||||
pats.push(self.ecx.pat_ident(e.span, lname));
|
||||
names[*self.name_positions.get(name).unwrap()] =
|
||||
Some(Context::format_arg(self.ecx, self.macsp, e.span, arg_ty,
|
||||
self.ecx.expr_ident(e.span, lname)));
|
||||
heads.push(self.ecx.expr_addr_of(e.span, e));
|
||||
}
|
||||
|
||||
// Now create a vector containing all the arguments
|
||||
let args = locals.into_iter().chain(names.into_iter().map(|a| a.unwrap()));
|
||||
|
||||
let args_array = self.ecx.expr_vec(self.fmtsp, args.collect());
|
||||
|
||||
// Constructs an AST equivalent to:
|
||||
//
|
||||
// match (&arg0, &arg1) {
|
||||
// (tmp0, tmp1) => args_array
|
||||
// }
|
||||
//
|
||||
// It was:
|
||||
//
|
||||
// let tmp0 = &arg0;
|
||||
// let tmp1 = &arg1;
|
||||
// args_array
|
||||
//
|
||||
// Because of #11585 the new temporary lifetime rule, the enclosing
|
||||
// statements for these temporaries become the let's themselves.
|
||||
// If one or more of them are RefCell's, RefCell borrow() will also
|
||||
// end there; they don't last long enough for args_array to use them.
|
||||
// The match expression solves the scope problem.
|
||||
//
|
||||
// Note, it may also very well be transformed to:
|
||||
//
|
||||
// match arg0 {
|
||||
// ref tmp0 => {
|
||||
// match arg1 => {
|
||||
// ref tmp1 => args_array } } }
|
||||
//
|
||||
// But the nested match expression is proved to perform not as well
|
||||
// as series of let's; the first approach does.
|
||||
let pat = self.ecx.pat_tuple(self.fmtsp, pats);
|
||||
let arm = self.ecx.arm(self.fmtsp, vec!(pat), args_array);
|
||||
let head = self.ecx.expr(self.fmtsp, ast::ExprTup(heads));
|
||||
let result = self.ecx.expr_match(self.fmtsp, head, vec!(arm));
|
||||
|
||||
let args_slice = self.ecx.expr_addr_of(self.fmtsp, result);
|
||||
|
||||
// Now create the fmt::Arguments struct with all our locals we created.
|
||||
let (fn_name, fn_args) = if self.all_pieces_simple {
|
||||
("new_v1", vec![pieces, args_slice])
|
||||
} else {
|
||||
// Build up the static array which will store our precompiled
|
||||
// nonstandard placeholders, if there are any.
|
||||
let piece_ty = self.ecx.ty_path(self.ecx.path_global(
|
||||
self.macsp,
|
||||
Context::rtpath(self.ecx, "Argument")));
|
||||
let fmt = Context::static_array(self.ecx,
|
||||
"__STATIC_FMTARGS",
|
||||
piece_ty,
|
||||
self.pieces);
|
||||
|
||||
("new_v1_formatted", vec![pieces, args_slice, fmt])
|
||||
};
|
||||
|
||||
let path = self.ecx.std_path(&["fmt", "Arguments", fn_name]);
|
||||
self.ecx.expr_call_global(self.macsp, path, fn_args)
|
||||
}
|
||||
|
||||
fn format_arg(ecx: &ExtCtxt, macsp: Span, sp: Span,
|
||||
ty: &ArgumentType, arg: P<ast::Expr>)
|
||||
-> P<ast::Expr> {
|
||||
let trait_ = match *ty {
|
||||
Known(ref tyname) => {
|
||||
match &tyname[..] {
|
||||
"" => "Display",
|
||||
"?" => "Debug",
|
||||
"e" => "LowerExp",
|
||||
"E" => "UpperExp",
|
||||
"o" => "Octal",
|
||||
"p" => "Pointer",
|
||||
"b" => "Binary",
|
||||
"x" => "LowerHex",
|
||||
"X" => "UpperHex",
|
||||
_ => {
|
||||
ecx.span_err(sp,
|
||||
&format!("unknown format trait `{}`",
|
||||
*tyname));
|
||||
"Dummy"
|
||||
}
|
||||
}
|
||||
}
|
||||
Unsigned => {
|
||||
let path = ecx.std_path(&["fmt", "ArgumentV1", "from_usize"]);
|
||||
return ecx.expr_call_global(macsp, path, vec![arg])
|
||||
}
|
||||
};
|
||||
|
||||
let path = ecx.std_path(&["fmt", trait_, "fmt"]);
|
||||
let format_fn = ecx.path_global(sp, path);
|
||||
let path = ecx.std_path(&["fmt", "ArgumentV1", "new"]);
|
||||
ecx.expr_call_global(macsp, path, vec![arg, ecx.expr_path(format_fn)])
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expand_format_args<'cx>(ecx: &'cx mut ExtCtxt, sp: Span,
|
||||
tts: &[ast::TokenTree])
|
||||
-> Box<base::MacResult+'cx> {
|
||||
|
||||
match parse_args(ecx, sp, tts) {
|
||||
Some((efmt, args, order, names)) => {
|
||||
MacEager::expr(expand_preparsed_format_args(ecx, sp, efmt,
|
||||
args, order, names))
|
||||
}
|
||||
None => DummyResult::expr(sp)
|
||||
}
|
||||
}
|
||||
|
||||
/// Take the various parts of `format_args!(efmt, args..., name=names...)`
|
||||
/// and construct the appropriate formatting expression.
|
||||
pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, sp: Span,
|
||||
efmt: P<ast::Expr>,
|
||||
args: Vec<P<ast::Expr>>,
|
||||
name_ordering: Vec<String>,
|
||||
names: HashMap<String, P<ast::Expr>>)
|
||||
-> P<ast::Expr> {
|
||||
let arg_types: Vec<_> = (0..args.len()).map(|_| None).collect();
|
||||
let macsp = ecx.call_site();
|
||||
// Expand the format literal so that efmt.span will have a backtrace. This
|
||||
// is essential for locating a bug when the format literal is generated in
|
||||
// a macro. (e.g. println!("{}"), which uses concat!($fmt, "\n")).
|
||||
let efmt = ecx.expander().fold_expr(efmt);
|
||||
let mut cx = Context {
|
||||
ecx: ecx,
|
||||
args: args,
|
||||
arg_types: arg_types,
|
||||
names: names,
|
||||
name_positions: HashMap::new(),
|
||||
name_types: HashMap::new(),
|
||||
name_ordering: name_ordering,
|
||||
nest_level: 0,
|
||||
next_arg: 0,
|
||||
literal: String::new(),
|
||||
pieces: Vec::new(),
|
||||
str_pieces: Vec::new(),
|
||||
all_pieces_simple: true,
|
||||
macsp: macsp,
|
||||
fmtsp: efmt.span,
|
||||
};
|
||||
let fmt = match expr_to_string(cx.ecx,
|
||||
efmt,
|
||||
"format argument must be a string literal.") {
|
||||
Some((fmt, _)) => fmt,
|
||||
None => return DummyResult::raw_expr(sp)
|
||||
};
|
||||
|
||||
let mut parser = parse::Parser::new(&fmt);
|
||||
|
||||
loop {
|
||||
match parser.next() {
|
||||
Some(piece) => {
|
||||
if !parser.errors.is_empty() { break }
|
||||
cx.verify_piece(&piece);
|
||||
match cx.trans_piece(&piece) {
|
||||
Some(piece) => {
|
||||
let s = cx.trans_literal_string();
|
||||
cx.str_pieces.push(s);
|
||||
cx.pieces.push(piece);
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
None => break
|
||||
}
|
||||
}
|
||||
if !parser.errors.is_empty() {
|
||||
cx.ecx.span_err(cx.fmtsp, &format!("invalid format string: {}",
|
||||
parser.errors.remove(0)));
|
||||
return DummyResult::raw_expr(sp);
|
||||
}
|
||||
if !cx.literal.is_empty() {
|
||||
let s = cx.trans_literal_string();
|
||||
cx.str_pieces.push(s);
|
||||
}
|
||||
|
||||
// Make sure that all arguments were used and all arguments have types.
|
||||
for (i, ty) in cx.arg_types.iter().enumerate() {
|
||||
if ty.is_none() {
|
||||
cx.ecx.span_err(cx.args[i].span, "argument never used");
|
||||
}
|
||||
}
|
||||
for (name, e) in &cx.names {
|
||||
if !cx.name_types.contains_key(name) {
|
||||
cx.ecx.span_err(e.span, "named argument never used");
|
||||
}
|
||||
}
|
||||
|
||||
cx.into_expr()
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use ast;
|
||||
use codemap;
|
||||
use ext::base;
|
||||
use feature_gate;
|
||||
use print;
|
||||
|
||||
pub fn expand_syntax_ext<'cx>(cx: &'cx mut base::ExtCtxt,
|
||||
sp: codemap::Span,
|
||||
tts: &[ast::TokenTree])
|
||||
-> Box<base::MacResult+'cx> {
|
||||
if !cx.ecfg.enable_log_syntax() {
|
||||
feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic,
|
||||
"log_syntax",
|
||||
sp,
|
||||
feature_gate::GateIssue::Language,
|
||||
feature_gate::EXPLAIN_LOG_SYNTAX);
|
||||
return base::DummyResult::any(sp);
|
||||
}
|
||||
|
||||
println!("{}", print::pprust::tts_to_string(tts));
|
||||
|
||||
// any so that `log_syntax` can be invoked as an expression and item.
|
||||
base::DummyResult::any(sp)
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use ast::TokenTree;
|
||||
use codemap::Span;
|
||||
use ext::base::ExtCtxt;
|
||||
use ext::base;
|
||||
use feature_gate;
|
||||
use parse::token::keywords;
|
||||
|
||||
|
||||
pub fn expand_trace_macros(cx: &mut ExtCtxt,
|
||||
sp: Span,
|
||||
tt: &[TokenTree])
|
||||
-> Box<base::MacResult+'static> {
|
||||
if !cx.ecfg.enable_trace_macros() {
|
||||
feature_gate::emit_feature_err(&cx.parse_sess.span_diagnostic,
|
||||
"trace_macros",
|
||||
sp,
|
||||
feature_gate::GateIssue::Language,
|
||||
feature_gate::EXPLAIN_TRACE_MACROS);
|
||||
return base::DummyResult::any(sp);
|
||||
}
|
||||
|
||||
match (tt.len(), tt.first()) {
|
||||
(1, Some(&TokenTree::Token(_, ref tok))) if tok.is_keyword(keywords::True) => {
|
||||
cx.set_trace_macros(true);
|
||||
}
|
||||
(1, Some(&TokenTree::Token(_, ref tok))) if tok.is_keyword(keywords::False) => {
|
||||
cx.set_trace_macros(false);
|
||||
}
|
||||
_ => cx.span_err(sp, "trace_macros! accepts only `true` or `false`"),
|
||||
}
|
||||
|
||||
base::DummyResult::any(sp)
|
||||
}
|
||||
Reference in New Issue
Block a user