Move built-in syntax extensions to a separate crate

This commit is contained in:
Seo Sanghyeon
2015-12-10 23:23:14 +09:00
parent 8f031bf962
commit f9ba107824
30 changed files with 310 additions and 320 deletions

View File

@@ -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,
}))
}

View File

@@ -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
}

View File

@@ -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))
}

View File

@@ -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[..])))
}

View File

@@ -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)
}

View File

@@ -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);
}

View File

@@ -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)
}
}
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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)))
}

View File

@@ -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)
}
}
}

View File

@@ -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)`")
};
}

View File

@@ -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

View File

@@ -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)
}
}
}

View File

@@ -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))
}

View File

@@ -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));
}
}

View File

@@ -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)")
}
}

View File

@@ -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)
}

View File

@@ -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);

View File

@@ -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()
}

View File

@@ -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)
}

View File

@@ -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)
}