1 // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
16 use diagnostic::SpanHandler;
19 use parse::token::{get_ident_interner, IdentInterner};
21 use util::small_vector::SmallVector;
24 use std::cell::RefCell;
25 use extra::smallintmap::SmallIntMap;
27 #[deriving(Clone, Eq)]
32 // A pretty name can come from an `impl` block. We attempt to select a
33 // reasonable name for debuggers to see, but to guarantee uniqueness with
34 // other paths the hash should also be taken into account during symbol
36 PathPrettyName(Ident, u64),
40 pub fn ident(&self) -> Ident {
44 PathPrettyName(ident, _) => ident
49 pub type Path = ~[PathElem];
51 pub fn path_to_str_with_sep(p: &[PathElem], sep: &str, itr: @IdentInterner)
53 let strs = p.map(|e| {
55 PathMod(s) | PathName(s) | PathPrettyName(s, _) => {
63 pub fn path_ident_to_str(p: &Path, i: Ident, itr: @IdentInterner) -> ~str {
65 itr.get(i.name).into_owned()
67 let string = itr.get(i.name);
68 format!("{}::{}", path_to_str(*p, itr), string.as_slice())
72 pub fn path_to_str(p: &[PathElem], itr: @IdentInterner) -> ~str {
73 path_to_str_with_sep(p, "::", itr)
76 pub fn path_elem_to_str(pe: PathElem, itr: @IdentInterner) -> ~str {
78 PathMod(s) | PathName(s) | PathPrettyName(s, _) => {
79 itr.get(s.name).into_owned()
84 /// write a "pretty" version of `ty` to `out`. This is designed so
85 /// that symbols of `impl`'d methods give some hint of where they came
86 /// from, even if it's hard to read (previously they would all just be
87 /// listed as `__extensions__::method_name::hash`, with no indication
89 // FIXME: these dollar signs and the names in general are actually a
90 // relic of $ being one of the very few valid symbol names on
91 // unix. These kinds of details shouldn't be exposed way up here
93 fn pretty_ty(ty: &Ty, itr: @IdentInterner, out: &mut ~str) {
94 let (prefix, subty) = match ty.node {
95 TyUniq(ty) => ("$UP$", &*ty),
96 TyBox(ty) => ("$SP$", &*ty),
97 TyPtr(MutTy { ty, mutbl }) => (if mutbl == MutMutable {"$RPmut$"} else {"$RP$"},
99 TyRptr(_, MutTy { ty, mutbl }) => (if mutbl == MutMutable {"$BPmut$"} else {"$BP$"},
102 TyVec(ty) => ("$VEC$", &*ty),
103 TyFixedLengthVec(ty, _) => ("$FIXEDVEC$", &*ty),
105 // these can't be represented as <prefix><contained ty>, so
106 // need custom handling.
107 TyNil => { out.push_str("$NIL$"); return }
108 TyPath(ref path, _, _) => {
109 out.push_str(itr.get(path.segments
117 out.push_str(format!("$TUP_{}$", tys.len()));
118 for subty in tys.iter() {
119 pretty_ty(*subty, itr, out);
125 // meh, better than nothing.
126 TyBot => { out.push_str("$BOT$"); return }
127 TyClosure(..) => { out.push_str("$CLOSURE$"); return }
128 TyBareFn(..) => { out.push_str("$FN$"); return }
129 TyTypeof(..) => { out.push_str("$TYPEOF$"); return }
130 TyInfer(..) => { out.push_str("$INFER$"); return }
134 out.push_str(prefix);
135 pretty_ty(subty, itr, out);
138 pub fn impl_pretty_name(trait_ref: &Option<TraitRef>, ty: &Ty) -> PathElem {
139 let itr = get_ident_interner();
141 let hash = (trait_ref, ty).hash();
144 None => pretty = ~"",
145 Some(ref trait_ref) => {
146 pretty = itr.get(trait_ref.path.segments.last().unwrap().identifier.name)
148 pretty.push_char('$');
151 pretty_ty(ty, itr, &mut pretty);
153 PathPrettyName(Ident::new(itr.gensym(pretty)), hash)
158 NodeItem(@Item, @Path),
159 NodeForeignItem(@ForeignItem, AbiSet, Visibility, @Path),
160 NodeTraitMethod(@TraitMethod, DefId /* trait did */,
161 @Path /* path to the trait */),
162 NodeMethod(@Method, DefId /* impl did */, @Path /* path to the impl */),
164 /// NodeVariant represents a variant of an enum, e.g., for
165 /// `enum A { B, C, D }`, there would be a NodeItem for `A`, and a
166 /// NodeVariant item for each of `B`, `C`, and `D`.
167 NodeVariant(P<Variant>, @Item, @Path),
174 /// NodeStructCtor represents a tuple struct.
175 NodeStructCtor(@StructDef, @Item, @Path),
176 NodeCalleeScope(@Expr)
180 pub fn with_attrs<T>(&self, f: |Option<&[Attribute]>| -> T) -> T {
181 let attrs = match *self {
182 NodeItem(i, _) => Some(i.attrs.as_slice()),
183 NodeForeignItem(fi, _, _, _) => Some(fi.attrs.as_slice()),
184 NodeTraitMethod(tm, _, _) => match *tm {
185 Required(ref type_m) => Some(type_m.attrs.as_slice()),
186 Provided(m) => Some(m.attrs.as_slice())
188 NodeMethod(m, _, _) => Some(m.attrs.as_slice()),
189 NodeVariant(ref v, _, _) => Some(v.node.attrs.as_slice()),
190 // unit/tuple structs take the attributes straight from
191 // the struct definition.
192 NodeStructCtor(_, strct, _) => Some(strct.attrs.as_slice()),
200 /// NodeIds are sequential integers from 0, so we can be
201 /// super-compact by storing them in a vector. Not everything with
202 /// a NodeId is in the map, but empirically the occupancy is about
203 /// 75-80%, so there's not too much overhead (certainly less than
204 /// a hashmap, since they (at the time of writing) have a maximum
205 /// of 75% occupancy). (The additional overhead of the Option<>
206 /// inside the SmallIntMap could be removed by adding an extra
207 /// empty variant to Node and storing a vector here, but that was
208 /// found to not make much difference.)
210 /// Also, indexing is pretty quick when you've got a vector and
211 /// plain old integers.
212 priv map: @RefCell<SmallIntMap<Node>>
216 /// Retrieve the Node corresponding to `id`, failing if it cannot
218 pub fn get(&self, id: ast::NodeId) -> Node {
219 let map = self.map.borrow();
220 *map.get().get(&(id as uint))
222 /// Retrieve the Node corresponding to `id`, returning None if
224 pub fn find(&self, id: ast::NodeId) -> Option<Node> {
225 let map = self.map.borrow();
226 map.get().find(&(id as uint)).map(|&n| n)
231 fn new_id(&self, id: ast::NodeId) -> ast::NodeId {
234 fn new_span(&self, span: Span) -> Span {
247 fn insert(&self, id: ast::NodeId, node: Node) {
248 let mut map = self.map.map.borrow_mut();
249 map.get().insert(id as uint, node);
253 impl<F: FoldOps> Folder for Ctx<F> {
254 fn new_id(&mut self, id: ast::NodeId) -> ast::NodeId {
255 self.fold_ops.new_id(id)
258 fn new_span(&mut self, span: Span) -> Span {
259 self.fold_ops.new_span(span)
262 fn fold_item(&mut self, i: @Item) -> SmallVector<@Item> {
263 // clone is FIXME #2543
264 let item_path = @self.path.clone();
265 self.path.push(match i.node {
266 ItemImpl(_, ref maybe_trait, ty, _) => {
267 // Right now the ident on impls is __extensions__ which isn't
268 // very pretty when debugging, so attempt to select a better
270 impl_pretty_name(maybe_trait, ty)
272 ItemMod(_) | ItemForeignMod(_) => PathMod(i.ident),
273 _ => PathName(i.ident)
276 let i = fold::noop_fold_item(i, self).expect_one("expected one item");
277 self.insert(i.id, NodeItem(i, item_path));
280 ItemImpl(_, _, _, ref ms) => {
281 // clone is FIXME #2543
282 let p = @self.path.clone();
283 let impl_did = ast_util::local_def(i.id);
284 for &m in ms.iter() {
285 self.insert(m.id, NodeMethod(m, impl_did, p));
289 ItemEnum(ref enum_definition, _) => {
290 // clone is FIXME #2543
291 let p = @self.path.clone();
292 for &v in enum_definition.variants.iter() {
293 self.insert(v.node.id, NodeVariant(v, i, p));
296 ItemForeignMod(ref nm) => {
297 for nitem in nm.items.iter() {
298 // Compute the visibility for this native item.
299 let visibility = nitem.vis.inherit_from(i.vis);
301 self.insert(nitem.id,
302 // Anonymous extern mods go in the parent scope.
303 NodeForeignItem(*nitem, nm.abis, visibility, item_path));
306 ItemStruct(struct_def, _) => {
307 // If this is a tuple-like struct, register the constructor.
308 match struct_def.ctor_id {
311 // clone is FIXME #2543
312 let p = @self.path.clone();
313 self.insert(ctor_id, NodeStructCtor(struct_def, i, p));
317 ItemTrait(_, ref traits, ref methods) => {
318 for t in traits.iter() {
319 self.insert(t.ref_id, NodeItem(i, item_path));
322 // clone is FIXME #2543
323 let p = @self.path.clone();
324 for tm in methods.iter() {
325 let d_id = ast_util::local_def(i.id);
328 self.insert(m.id, NodeTraitMethod(@(*tm).clone(), d_id, p));
331 self.insert(m.id, NodeTraitMethod(@Provided(m), d_id, p));
339 self.path.pop().unwrap();
344 fn fold_pat(&mut self, pat: @Pat) -> @Pat {
345 let pat = fold::noop_fold_pat(pat, self);
348 // Note: this is at least *potentially* a pattern...
349 self.insert(pat.id, NodeLocal(pat));
357 fn fold_expr(&mut self, expr: @Expr) -> @Expr {
358 let expr = fold::noop_fold_expr(expr, self);
360 self.insert(expr.id, NodeExpr(expr));
362 // Expressions which are or might be calls:
364 let r = expr.get_callee_id();
365 for callee_id in r.iter() {
366 self.insert(*callee_id, NodeCalleeScope(expr));
373 fn fold_stmt(&mut self, stmt: &Stmt) -> SmallVector<@Stmt> {
374 let stmt = fold::noop_fold_stmt(stmt, self).expect_one("expected one statement");
375 self.insert(ast_util::stmt_id(stmt), NodeStmt(stmt));
376 SmallVector::one(stmt)
379 fn fold_method(&mut self, m: @Method) -> @Method {
380 self.path.push(PathName(m.ident));
381 let m = fold::noop_fold_method(m, self);
386 fn fold_fn_decl(&mut self, decl: &FnDecl) -> P<FnDecl> {
387 let decl = fold::noop_fold_fn_decl(decl, self);
388 for a in decl.inputs.iter() {
389 self.insert(a.id, NodeArg(a.pat));
394 fn fold_block(&mut self, block: P<Block>) -> P<Block> {
395 let block = fold::noop_fold_block(block, self);
396 self.insert(block.id, NodeBlock(block));
401 pub fn map_crate<F: 'static + FoldOps>(diag: @SpanHandler, c: Crate,
402 fold_ops: F) -> (Crate, Map) {
404 map: Map { map: @RefCell::new(SmallIntMap::new()) },
409 let crate = cx.fold_crate(c);
411 if log_enabled!(logging::DEBUG) {
412 let map = cx.map.map.borrow();
413 // this only makes sense for ordered stores; note the
414 // enumerate to count the number of entries.
415 let (entries_less_1, (largest_id, _)) =
416 map.get().iter().enumerate().last().expect("AST map was empty after folding?");
418 let entries = entries_less_1 + 1;
419 let vector_length = largest_id + 1;
420 debug!("The AST map has {} entries with a maximum of {}: occupancy {:.1}%",
421 entries, vector_length, (entries as f64 / vector_length as f64) * 100.);
427 // Used for items loaded from external crate that are being inlined into this
428 // crate. The `path` should be the path to the item but should not include
430 pub fn map_decoded_item<F: 'static + FoldOps>(diag: @SpanHandler,
434 fold_ii: |&mut Ctx<F>| -> InlinedItem)
436 // I believe it is ok for the local IDs of inlined items from other crates
437 // to overlap with the local ids from this crate, so just generate the ids
446 let ii = fold_ii(&mut cx);
448 // Methods get added to the AST map when their impl is visited. Since we
449 // don't decode and instantiate the impl, but just the method, we have to
450 // add it to the table now. Likewise with foreign items.
452 IIItem(..) => {} // fallthrough
454 cx.insert(i.id, NodeForeignItem(i,
456 i.vis, // Wrong but OK
459 IIMethod(impl_did, is_provided, m) => {
460 let entry = if is_provided {
461 NodeTraitMethod(@Provided(m), impl_did, @path)
463 NodeMethod(m, impl_did, @path)
465 cx.insert(m.id, entry);
472 pub fn node_id_to_str(map: Map, id: NodeId, itr: @IdentInterner) -> ~str {
475 format!("unknown node (id={})", id)
477 Some(NodeItem(item, path)) => {
478 let path_str = path_ident_to_str(path, item.ident, itr);
479 let item_str = match item.node {
480 ItemStatic(..) => ~"static",
482 ItemMod(..) => ~"mod",
483 ItemForeignMod(..) => ~"foreign mod",
485 ItemEnum(..) => ~"enum",
486 ItemStruct(..) => ~"struct",
487 ItemTrait(..) => ~"trait",
488 ItemImpl(..) => ~"impl",
489 ItemMac(..) => ~"macro"
491 format!("{} {} (id={})", item_str, path_str, id)
493 Some(NodeForeignItem(item, abi, _, path)) => {
494 format!("foreign item {} with abi {:?} (id={})",
495 path_ident_to_str(path, item.ident, itr), abi, id)
497 Some(NodeMethod(m, _, path)) => {
498 let name = itr.get(m.ident.name);
499 format!("method {} in {} (id={})",
500 name.as_slice(), path_to_str(*path, itr), id)
502 Some(NodeTraitMethod(ref tm, _, path)) => {
503 let m = ast_util::trait_method_to_ty_method(&**tm);
504 let name = itr.get(m.ident.name);
505 format!("method {} in {} (id={})",
506 name.as_slice(), path_to_str(*path, itr), id)
508 Some(NodeVariant(ref variant, _, path)) => {
509 let name = itr.get(variant.node.name.name);
510 format!("variant {} in {} (id={})",
512 path_to_str(*path, itr), id)
514 Some(NodeExpr(expr)) => {
515 format!("expr {} (id={})", pprust::expr_to_str(expr, itr), id)
517 Some(NodeCalleeScope(expr)) => {
518 format!("callee_scope {} (id={})", pprust::expr_to_str(expr, itr), id)
520 Some(NodeStmt(stmt)) => {
521 format!("stmt {} (id={})",
522 pprust::stmt_to_str(stmt, itr), id)
524 Some(NodeArg(pat)) => {
525 format!("arg {} (id={})", pprust::pat_to_str(pat, itr), id)
527 Some(NodeLocal(pat)) => {
528 format!("local {} (id={})", pprust::pat_to_str(pat, itr), id)
530 Some(NodeBlock(block)) => {
531 format!("block {} (id={})", pprust::block_to_str(block, itr), id)
533 Some(NodeStructCtor(_, _, path)) => {
534 format!("struct_ctor {} (id={})", path_to_str(*path, itr), id)
539 pub fn node_item_query<Result>(items: Map, id: NodeId, query: |@Item| -> Result, error_msg: ~str)
541 match items.find(id) {
542 Some(NodeItem(it, _)) => query(it),
543 _ => fail!("{}", error_msg)
547 pub fn node_span(items: Map, id: ast::NodeId) -> Span {
548 match items.find(id) {
549 Some(NodeItem(item, _)) => item.span,
550 Some(NodeForeignItem(foreign_item, _, _, _)) => foreign_item.span,
551 Some(NodeTraitMethod(trait_method, _, _)) => {
552 match *trait_method {
553 Required(ref type_method) => type_method.span,
554 Provided(ref method) => method.span,
557 Some(NodeMethod(method, _, _)) => method.span,
558 Some(NodeVariant(variant, _, _)) => variant.span,
559 Some(NodeExpr(expr)) => expr.span,
560 Some(NodeStmt(stmt)) => stmt.span,
561 Some(NodeArg(pat)) | Some(NodeLocal(pat)) => pat.span,
562 Some(NodeBlock(block)) => block.span,
563 Some(NodeStructCtor(_, item, _)) => item.span,
564 Some(NodeCalleeScope(expr)) => expr.span,
565 None => fail!("node_span: could not find id {}", id),