| use std::map::HashMap; |
| use middle::ty; |
| use middle::ty::{arg, canon_mode}; |
| use middle::ty::{bound_copy, bound_const, bound_owned, bound_send, |
| bound_trait}; |
| use middle::ty::{bound_region, br_anon, br_named, br_self, br_cap_avoid}; |
| use middle::ty::{ck_block, ck_box, ck_uniq, ctxt, field, method}; |
| use middle::ty::{mt, t, param_bound}; |
| use middle::ty::{re_bound, re_free, re_scope, re_var, re_static, region}; |
| use middle::ty::{ty_bool, ty_bot, ty_box, ty_class, ty_enum}; |
| use middle::ty::{ty_estr, ty_evec, ty_float, ty_fn, ty_trait, ty_int}; |
| use middle::ty::{ty_nil, ty_opaque_box, ty_opaque_closure_ptr, ty_param}; |
| use middle::ty::{ty_ptr, ty_rec, ty_rptr, ty_self, ty_tup}; |
| use middle::ty::{ty_type, ty_uniq, ty_uint, ty_infer}; |
| use middle::ty::{ty_unboxed_vec, vid}; |
| use metadata::encoder; |
| use syntax::codemap; |
| use syntax::codemap::span; |
| use syntax::print::pprust; |
| use syntax::print::pprust::{path_to_str, proto_to_str, |
| mode_to_str, purity_to_str}; |
| use syntax::{ast, ast_util}; |
| use syntax::ast_map; |
| use driver::session::session; |
| |
| fn note_and_explain_region(cx: ctxt, |
| prefix: ~str, |
| region: ty::region, |
| suffix: ~str) { |
| match explain_region_and_span(cx, region) { |
| (str, Some(span)) => { |
| cx.sess.span_note( |
| span, |
| fmt!("%s%s%s", prefix, str, suffix)); |
| } |
| (str, None) => { |
| cx.sess.note( |
| fmt!("%s%s%s", prefix, str, suffix)); |
| } |
| } |
| } |
| |
| /// Returns a string like "the block at 27:31" that attempts to explain a |
| /// lifetime in a way it might plausibly be understood. |
| fn explain_region(cx: ctxt, region: ty::region) -> ~str { |
| let (res, _) = explain_region_and_span(cx, region); |
| return res; |
| } |
| |
| |
| fn explain_region_and_span(cx: ctxt, region: ty::region) |
| -> (~str, Option<span>) |
| { |
| return match region { |
| re_scope(node_id) => { |
| match cx.items.find(node_id) { |
| Some(ast_map::node_block(blk)) => { |
| explain_span(cx, ~"block", blk.span) |
| } |
| Some(ast_map::node_expr(expr)) => { |
| match expr.node { |
| ast::expr_call(*) => explain_span(cx, ~"call", expr.span), |
| ast::expr_match(*) => explain_span(cx, ~"match", expr.span), |
| _ => explain_span(cx, ~"expression", expr.span) |
| } |
| } |
| Some(_) | None => { |
| // this really should not happen |
| (fmt!("unknown scope: %d. Please report a bug.", node_id), |
| None) |
| } |
| } |
| } |
| |
| re_free(id, br) => { |
| let prefix = match br { |
| br_anon(idx) => fmt!("the anonymous lifetime #%u defined on", |
| idx + 1), |
| _ => fmt!("the lifetime %s as defined on", |
| bound_region_to_str(cx, br)) |
| }; |
| |
| match cx.items.find(id) { |
| Some(ast_map::node_block(blk)) => { |
| let (msg, opt_span) = explain_span(cx, ~"block", blk.span); |
| (fmt!("%s %s", prefix, msg), opt_span) |
| } |
| Some(_) | None => { |
| // this really should not happen |
| (fmt!("%s node %d", prefix, id), None) |
| } |
| } |
| } |
| |
| re_static => { (~"the static lifetime", None) } |
| |
| // I believe these cases should not occur (except when debugging, |
| // perhaps) |
| re_var(_) | re_bound(_) => { |
| (fmt!("lifetime %?", region), None) |
| } |
| }; |
| |
| fn explain_span(cx: ctxt, heading: ~str, span: span) |
| -> (~str, Option<span>) |
| { |
| let lo = codemap::lookup_char_pos_adj(cx.sess.codemap, span.lo); |
| (fmt!("the %s at %u:%u", heading, lo.line, lo.col), Some(span)) |
| } |
| } |
| |
| fn bound_region_to_str(cx: ctxt, br: bound_region) -> ~str { |
| match br { |
| br_named(id) => fmt!("&%s", cx.sess.str_of(id)), |
| br_self if cx.sess.verbose() => ~"&<self>", |
| br_self => ~"&self", |
| |
| br_anon(idx) => { |
| if cx.sess.verbose() {fmt!("&%u", idx)} else {~"&"} |
| } |
| |
| br_cap_avoid(id, br) => { |
| if cx.sess.verbose() { |
| fmt!("br_cap_avoid(%?, %s)", id, bound_region_to_str(cx, *br)) |
| } else { |
| bound_region_to_str(cx, *br) |
| } |
| } |
| } |
| } |
| |
| fn re_scope_id_to_str(cx: ctxt, node_id: ast::node_id) -> ~str { |
| match cx.items.find(node_id) { |
| Some(ast_map::node_block(blk)) => { |
| fmt!("<block at %s>", |
| codemap::span_to_str(blk.span, cx.sess.codemap)) |
| } |
| Some(ast_map::node_expr(expr)) => { |
| match expr.node { |
| ast::expr_call(*) => { |
| fmt!("<call at %s>", |
| codemap::span_to_str(expr.span, cx.sess.codemap)) |
| } |
| ast::expr_match(*) => { |
| fmt!("<alt at %s>", |
| codemap::span_to_str(expr.span, cx.sess.codemap)) |
| } |
| ast::expr_assign_op(*) | |
| ast::expr_field(*) | |
| ast::expr_unary(*) | |
| ast::expr_binary(*) | |
| ast::expr_index(*) => { |
| fmt!("<method at %s>", |
| codemap::span_to_str(expr.span, cx.sess.codemap)) |
| } |
| _ => { |
| fmt!("<expression at %s>", |
| codemap::span_to_str(expr.span, cx.sess.codemap)) |
| } |
| } |
| } |
| None => { |
| fmt!("<unknown-%d>", node_id) |
| } |
| _ => { cx.sess.bug( |
| fmt!("re_scope refers to %s", |
| ast_map::node_id_to_str(cx.items, node_id, |
| cx.sess.parse_sess.interner))) } |
| } |
| } |
| |
| // In general, if you are giving a region error message, |
| // you should use `explain_region()` or, better yet, |
| // `note_and_explain_region()` |
| fn region_to_str(cx: ctxt, region: region) -> ~str { |
| if cx.sess.verbose() { |
| return fmt!("&%?", region); |
| } |
| |
| // These printouts are concise. They do not contain all the information |
| // the user might want to diagnose an error, but there is basically no way |
| // to fit that into a short string. Hence the recommendation to use |
| // `explain_region()` or `note_and_explain_region()`. |
| match region { |
| re_scope(_) => ~"&", |
| re_bound(br) => bound_region_to_str(cx, br), |
| re_free(_, br) => bound_region_to_str(cx, br), |
| re_var(_) => ~"&", |
| re_static => ~"&static" |
| } |
| } |
| |
| fn mt_to_str(cx: ctxt, m: mt) -> ~str { |
| let mstr = match m.mutbl { |
| ast::m_mutbl => ~"mut ", |
| ast::m_imm => ~"", |
| ast::m_const => ~"const " |
| }; |
| return mstr + ty_to_str(cx, m.ty); |
| } |
| |
| fn vstore_to_str(cx: ctxt, vs: ty::vstore) -> ~str { |
| match vs { |
| ty::vstore_fixed(n) => fmt!("%u", n), |
| ty::vstore_uniq => ~"~", |
| ty::vstore_box => ~"@", |
| ty::vstore_slice(r) => region_to_str(cx, r) |
| } |
| } |
| |
| fn vstore_ty_to_str(cx: ctxt, ty: ~str, vs: ty::vstore) -> ~str { |
| match vs { |
| ty::vstore_fixed(_) => { |
| fmt!("%s/%s", ty, vstore_to_str(cx, vs)) |
| } |
| ty::vstore_slice(_) => { |
| fmt!("%s/%s", vstore_to_str(cx, vs), ty) |
| } |
| _ => fmt!("%s%s", vstore_to_str(cx, vs), ty) |
| } |
| } |
| |
| fn proto_ty_to_str(cx: ctxt, proto: ty::fn_proto) -> ~str { |
| match proto { |
| ty::proto_bare => ~"", |
| ty::proto_vstore(vstore) => vstore_to_str(cx, vstore) |
| } |
| } |
| |
| fn expr_repr(cx: ctxt, expr: @ast::expr) -> ~str { |
| fmt!("expr(%d: %s)", |
| expr.id, |
| pprust::expr_to_str(expr, cx.sess.intr())) |
| } |
| |
| fn tys_to_str(cx: ctxt, ts: ~[t]) -> ~str { |
| let tstrs = ts.map(|t| ty_to_str(cx, *t)); |
| fmt!("[%s]", str::connect(tstrs, ", ")) |
| } |
| |
| fn bound_to_str(cx: ctxt, b: param_bound) -> ~str { |
| match b { |
| bound_copy => ~"copy", |
| bound_owned => ~"owned", |
| bound_send => ~"send", |
| bound_const => ~"const", |
| bound_trait(t) => ty_to_str(cx, t) |
| } |
| } |
| |
| fn ty_to_str(cx: ctxt, typ: t) -> ~str { |
| fn fn_input_to_str(cx: ctxt, input: {mode: ast::mode, ty: t}) -> |
| ~str { |
| let {mode, ty} = input; |
| let modestr = match canon_mode(cx, mode) { |
| ast::infer(_) => ~"", |
| ast::expl(m) => { |
| if !ty::type_needs_infer(ty) && |
| m == ty::default_arg_mode_for_ty(cx, ty) { |
| ~"" |
| } else { |
| mode_to_str(ast::expl(m)) + ":" |
| } |
| } |
| }; |
| modestr + ty_to_str(cx, ty) |
| } |
| fn fn_to_str(cx: ctxt, purity: ast::purity, proto: ty::fn_proto, |
| ident: Option<ast::ident>, |
| inputs: ~[arg], output: t, cf: ast::ret_style) -> ~str { |
| let mut s; |
| |
| s = match purity { |
| ast::impure_fn => ~"", |
| _ => purity_to_str(purity) + ~" " |
| }; |
| |
| s += ~"fn"; |
| |
| s += proto_ty_to_str(cx, proto); |
| match ident { |
| Some(i) => { s += ~" "; s += cx.sess.str_of(i); } |
| _ => { } |
| } |
| s += ~"("; |
| let mut strs = ~[]; |
| for inputs.each |a| { strs.push(fn_input_to_str(cx, *a)); } |
| s += str::connect(strs, ~", "); |
| s += ~")"; |
| if ty::get(output).sty != ty_nil { |
| s += ~" -> "; |
| match cf { |
| ast::noreturn => { s += ~"!"; } |
| ast::return_val => { s += ty_to_str(cx, output); } |
| } |
| } |
| return s; |
| } |
| fn method_to_str(cx: ctxt, m: method) -> ~str { |
| return fn_to_str( |
| cx, m.fty.meta.purity, m.fty.meta.proto, Some(m.ident), |
| m.fty.sig.inputs, m.fty.sig.output, |
| m.fty.meta.ret_style) + ~";"; |
| } |
| fn field_to_str(cx: ctxt, f: field) -> ~str { |
| return cx.sess.str_of(f.ident) + ~": " + mt_to_str(cx, f.mt); |
| } |
| |
| // if there is an id, print that instead of the structural type: |
| for ty::type_def_id(typ).each |def_id| { |
| // note that this typedef cannot have type parameters |
| return ast_map::path_to_str(ty::item_path(cx, *def_id), |
| cx.sess.intr()); |
| } |
| |
| // pretty print the structural type representation: |
| return match ty::get(typ).sty { |
| ty_nil => ~"()", |
| ty_bot => ~"_|_", |
| ty_bool => ~"bool", |
| ty_int(ast::ty_i) => ~"int", |
| ty_int(ast::ty_char) => ~"char", |
| ty_int(t) => ast_util::int_ty_to_str(t), |
| ty_uint(ast::ty_u) => ~"uint", |
| ty_uint(t) => ast_util::uint_ty_to_str(t), |
| ty_float(ast::ty_f) => ~"float", |
| ty_float(t) => ast_util::float_ty_to_str(t), |
| ty_box(tm) => ~"@" + mt_to_str(cx, tm), |
| ty_uniq(tm) => ~"~" + mt_to_str(cx, tm), |
| ty_ptr(tm) => ~"*" + mt_to_str(cx, tm), |
| ty_rptr(r, tm) => { |
| let rs = region_to_str(cx, r); |
| if rs == ~"&" { |
| rs + mt_to_str(cx, tm) |
| } else { |
| rs + ~"/" + mt_to_str(cx, tm) |
| } |
| } |
| ty_unboxed_vec(tm) => { ~"unboxed_vec<" + mt_to_str(cx, tm) + ~">" } |
| ty_type => ~"type", |
| ty_rec(elems) => { |
| let mut strs: ~[~str] = ~[]; |
| for elems.each |fld| { strs.push(field_to_str(cx, *fld)); } |
| ~"{" + str::connect(strs, ~",") + ~"}" |
| } |
| ty_tup(elems) => { |
| let mut strs = ~[]; |
| for elems.each |elem| { strs.push(ty_to_str(cx, *elem)); } |
| ~"(" + str::connect(strs, ~",") + ~")" |
| } |
| ty_fn(ref f) => { |
| fn_to_str(cx, f.meta.purity, f.meta.proto, None, f.sig.inputs, |
| f.sig.output, f.meta.ret_style) |
| } |
| ty_infer(infer_ty) => infer_ty.to_str(), |
| ty_param({idx: id, _}) => { |
| ~"'" + str::from_bytes(~[('a' as u8) + (id as u8)]) |
| } |
| ty_self => ~"self", |
| ty_enum(did, substs) | ty_class(did, substs) => { |
| let path = ty::item_path(cx, did); |
| let base = ast_map::path_to_str(path, cx.sess.intr()); |
| parameterized(cx, base, substs.self_r, substs.tps) |
| } |
| ty_trait(did, substs, vs) => { |
| let path = ty::item_path(cx, did); |
| let base = ast_map::path_to_str(path, cx.sess.intr()); |
| let result = parameterized(cx, base, substs.self_r, substs.tps); |
| vstore_ty_to_str(cx, result, vs) |
| } |
| ty_evec(mt, vs) => { |
| vstore_ty_to_str(cx, fmt!("[%s]", mt_to_str(cx, mt)), vs) |
| } |
| ty_estr(vs) => vstore_ty_to_str(cx, ~"str", vs), |
| ty_opaque_box => ~"@?", |
| ty_opaque_closure_ptr(ck_block) => ~"closure&", |
| ty_opaque_closure_ptr(ck_box) => ~"closure@", |
| ty_opaque_closure_ptr(ck_uniq) => ~"closure~" |
| } |
| } |
| |
| fn parameterized(cx: ctxt, |
| base: ~str, |
| self_r: Option<ty::region>, |
| tps: ~[ty::t]) -> ~str { |
| |
| let r_str = match self_r { |
| None => ~"", |
| Some(r) => { |
| fmt!("/%s", region_to_str(cx, r)) |
| } |
| }; |
| |
| if vec::len(tps) > 0u { |
| let strs = vec::map(tps, |t| ty_to_str(cx, *t)); |
| fmt!("%s%s<%s>", base, r_str, str::connect(strs, ~",")) |
| } else { |
| fmt!("%s%s", base, r_str) |
| } |
| } |
| |
| fn ty_to_short_str(cx: ctxt, typ: t) -> ~str { |
| let mut s = encoder::encoded_ty(cx, typ); |
| if str::len(s) >= 32u { s = str::slice(s, 0u, 32u); } |
| return s; |
| } |
| |
| // Local Variables: |
| // mode: rust |
| // fill-column: 78; |
| // indent-tabs-mode: nil |
| // c-basic-offset: 4 |
| // buffer-file-coding-system: utf-8-unix |
| // End: |