1298 lines
44 KiB
Rust
1298 lines
44 KiB
Rust
import front::ast;
|
|
import front::ast::ident;
|
|
import front::ast::def;
|
|
import front::ast::def_id;
|
|
import front::ast::ann;
|
|
import front::creader;
|
|
import driver::session::session;
|
|
import util::common::new_def_hash;
|
|
import util::common::new_int_hash;
|
|
import util::common::new_uint_hash;
|
|
import util::common::new_str_hash;
|
|
import util::common::span;
|
|
import middle::tstate::ann::ts_ann;
|
|
import std::map::hashmap;
|
|
import std::list;
|
|
import std::list::list;
|
|
import std::list::nil;
|
|
import std::list::cons;
|
|
import std::option;
|
|
import std::option::some;
|
|
import std::option::none;
|
|
import std::str;
|
|
import std::vec;
|
|
|
|
// Resolving happens in two passes. The first pass collects defids of all
|
|
// (internal) imports and modules, so that they can be looked up when needed,
|
|
// and then uses this information to resolve the imports. The second pass
|
|
// locates all names (in expressions, types, and alt patterns) and resolves
|
|
// them, storing the resulting def in the AST nodes.
|
|
|
|
// This module internally uses -1 as a def_id for the top_level module in a
|
|
// crate. The parser doesn't assign a def_id to this module.
|
|
// (FIXME See https://github.com/graydon/rust/issues/358 for the reason this
|
|
// isn't a const.)
|
|
|
|
tag scope {
|
|
scope_crate(@ast::crate);
|
|
scope_item(@ast::item);
|
|
scope_native_item(@ast::native_item);
|
|
scope_loop(@ast::decl); // there's only 1 decl per loop.
|
|
scope_block(ast::block);
|
|
scope_arm(ast::arm);
|
|
}
|
|
|
|
tag import_state {
|
|
todo(@ast::view_item, list[scope]); // only used for explicit imports
|
|
resolving(span);
|
|
resolved(option::t[def] /* value */,
|
|
option::t[def] /* type */,
|
|
option::t[def] /* module */);
|
|
}
|
|
|
|
type ext_hash = hashmap[tup(def_id,str,namespace),def];
|
|
fn new_ext_hash() -> ext_hash {
|
|
fn hash(&tup(def_id,str,namespace) v) -> uint {
|
|
ret str::hash(v._1) + util::common::hash_def(v._0) + (alt (v._2) {
|
|
case (ns_value) { 1u }
|
|
case (ns_type) { 2u }
|
|
case (ns_module) { 3u }
|
|
});
|
|
}
|
|
fn eq(&tup(def_id,str,namespace) v1,
|
|
&tup(def_id,str,namespace) v2) -> bool {
|
|
ret util::common::def_eq(v1._0, v2._0) &&
|
|
str::eq(v1._1, v2._1) &&
|
|
v1._2 == v2._2;
|
|
}
|
|
ret std::map::mk_hashmap[tup(def_id,str,namespace),def](hash, eq);
|
|
}
|
|
|
|
tag mod_index_entry {
|
|
mie_view_item(@ast::view_item);
|
|
mie_item(@ast::item);
|
|
mie_native_item(@ast::native_item);
|
|
mie_tag_variant(@ast::item /* tag item */, uint /* variant index */);
|
|
}
|
|
type mod_index = hashmap[ident,list[mod_index_entry]];
|
|
|
|
type indexed_mod = rec(option::t[ast::_mod] m,
|
|
mod_index index, vec[def] glob_imports,
|
|
hashmap[str,import_state] glob_imported_names);
|
|
/* native modules can't contain tags, and we don't store their ASTs because we
|
|
only need to look at them to determine exports, which they can't control.*/
|
|
// It should be safe to use index to memoize lookups of globbed names.
|
|
|
|
type crate_map = hashmap[uint,ast::crate_num];
|
|
|
|
type def_map = hashmap[uint,def];
|
|
|
|
type env = rec(crate_map crate_map,
|
|
def_map def_map,
|
|
hashmap[def_id,@ast::item] ast_map,
|
|
hashmap[ast::def_num,import_state] imports,
|
|
hashmap[ast::def_num,@indexed_mod] mod_map,
|
|
hashmap[def_id,vec[ident]] ext_map,
|
|
ext_hash ext_cache,
|
|
session sess);
|
|
|
|
// Used to distinguish between lookups from outside and from inside modules,
|
|
// since export restrictions should only be applied for the former.
|
|
tag dir { inside; outside; }
|
|
|
|
tag namespace {
|
|
ns_value;
|
|
ns_type;
|
|
ns_module;
|
|
}
|
|
|
|
fn resolve_crate(session sess, @ast::crate crate) -> def_map {
|
|
auto e = @rec(crate_map = new_uint_hash[ast::crate_num](),
|
|
def_map = new_uint_hash[def](),
|
|
ast_map = new_def_hash[@ast::item](),
|
|
imports = new_int_hash[import_state](),
|
|
mod_map = new_int_hash[@indexed_mod](),
|
|
ext_map = new_def_hash[vec[ident]](),
|
|
ext_cache = new_ext_hash(),
|
|
sess = sess);
|
|
creader::read_crates(sess, e.crate_map, *crate);
|
|
map_crate(e, *crate);
|
|
resolve_imports(*e);
|
|
check_for_collisions(e, *crate);
|
|
resolve_names(e, *crate);
|
|
ret e.def_map;
|
|
}
|
|
|
|
|
|
// Locate all modules and imports and index them, so that the next passes can
|
|
// resolve through them.
|
|
|
|
fn map_crate(&@env e, &ast::crate c) {
|
|
|
|
// First, find all the modules, and index the names that they contain
|
|
|
|
auto cell = @mutable nil[scope];
|
|
auto index_names =
|
|
rec(visit_crate_pre = bind push_env_for_crate(cell, _),
|
|
visit_crate_post = bind pop_env_for_crate(cell, _),
|
|
visit_view_item_pre = bind index_vi(e, cell, _),
|
|
visit_item_pre = bind index_i(e, cell, _),
|
|
visit_item_post = bind pop_env_for_item(cell, _)
|
|
with walk::default_visitor());
|
|
// Register the top-level mod
|
|
e.mod_map.insert(-1, @rec(m=some(c.node.module),
|
|
index=index_mod(c.node.module),
|
|
glob_imports=vec::empty[def](),
|
|
glob_imported_names
|
|
=new_str_hash[import_state]()));
|
|
walk::walk_crate(index_names, c);
|
|
|
|
fn index_vi(@env e, @mutable list[scope] sc, &@ast::view_item i) {
|
|
alt (i.node) {
|
|
case (ast::view_item_import(_, ?ids, ?defid)) {
|
|
e.imports.insert(defid._1, todo(i, *sc));
|
|
}
|
|
case (_) {}
|
|
}
|
|
}
|
|
fn index_i(@env e, @mutable list[scope] sc, &@ast::item i) {
|
|
push_env_for_item(sc, i);
|
|
alt (i.node) {
|
|
case (ast::item_mod(_, ?md, ?defid)) {
|
|
e.mod_map.insert(defid._1,
|
|
@rec(m=some(md), index=index_mod(md),
|
|
glob_imports=vec::empty[def](),
|
|
glob_imported_names
|
|
=new_str_hash[import_state]()));
|
|
e.ast_map.insert(defid, i);
|
|
}
|
|
case (ast::item_native_mod(_, ?nmd, ?defid)) {
|
|
e.mod_map.insert(defid._1,
|
|
@rec(m=none[ast::_mod],
|
|
index=index_nmod(nmd),
|
|
glob_imports=vec::empty[def](),
|
|
glob_imported_names
|
|
=new_str_hash[import_state]()));
|
|
e.ast_map.insert(defid, i);
|
|
}
|
|
case (ast::item_const(_, _, _, ?defid, _)) {
|
|
e.ast_map.insert(defid, i);
|
|
}
|
|
case (ast::item_fn(_, _, _, ?defid, _)) {
|
|
e.ast_map.insert(defid, i);
|
|
}
|
|
case (ast::item_ty(_, _, _, ?defid, _)) {
|
|
e.ast_map.insert(defid, i);
|
|
}
|
|
case (ast::item_tag(_, _, _, ?defid, _)) {
|
|
e.ast_map.insert(defid, i);
|
|
}
|
|
case (ast::item_obj(_, _, _, ?obj_def_ids, _)) {
|
|
e.ast_map.insert(obj_def_ids.ty, i);
|
|
e.ast_map.insert(obj_def_ids.ctor, i);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Next, assemble the links for globbed imports.
|
|
cell = @mutable nil[scope];
|
|
auto link_globs =
|
|
rec(visit_crate_pre = bind push_env_for_crate(cell, _),
|
|
visit_crate_post = bind pop_env_for_crate(cell, _),
|
|
visit_view_item_pre = bind link_glob(e, cell, _),
|
|
visit_item_pre = bind push_env_for_item(cell, _),
|
|
visit_item_post = bind pop_env_for_item(cell, _)
|
|
with walk::default_visitor());
|
|
walk::walk_crate(link_globs, c);
|
|
|
|
fn link_glob(@env e, @mutable list[scope] sc, &@ast::view_item vi) {
|
|
fn find_mod(@env e, list[scope] sc) -> @indexed_mod {
|
|
alt (sc) {
|
|
case (cons(scope_item(?i), ?tl)) {
|
|
alt(i.node) {
|
|
case (ast::item_mod(_, _, ?defid)) {
|
|
ret e.mod_map.get(defid._1);
|
|
}
|
|
case (ast::item_native_mod(_, _, ?defid)) {
|
|
ret e.mod_map.get(defid._1);
|
|
}
|
|
case (_) {
|
|
be find_mod(e, *tl);
|
|
}
|
|
}
|
|
}
|
|
case (_) {
|
|
ret e.mod_map.get(-1); //top-level
|
|
}
|
|
}
|
|
}
|
|
|
|
alt (vi.node) {
|
|
//if it really is a glob import, that is
|
|
case (ast::view_item_import_glob(?path, _)) {
|
|
find_mod(e, *sc).glob_imports
|
|
+= [follow_import(*e, *sc, path, vi.span)];
|
|
}
|
|
case (_) {}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fn resolve_imports(&env e) {
|
|
for each (@tup(ast::def_num, import_state) it in e.imports.items()) {
|
|
alt (it._1) {
|
|
case (todo(?item, ?sc)) {
|
|
resolve_import(e, item, sc);
|
|
}
|
|
case (resolved(_, _, _)) {}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn resolve_names(&@env e, &ast::crate c) {
|
|
auto cell = @mutable nil[scope];
|
|
auto v = rec(visit_crate_pre = bind push_env_for_crate(cell, _),
|
|
visit_crate_post = bind pop_env_for_crate(cell, _),
|
|
visit_item_pre = bind push_env_for_item(cell, _),
|
|
visit_item_post = bind pop_env_for_item(cell, _),
|
|
visit_method_pre = bind push_env_for_method(cell, _),
|
|
visit_method_post = bind pop_env_for_method(cell, _),
|
|
visit_native_item_pre = bind push_env_for_n_item(cell, _),
|
|
visit_native_item_post = bind pop_env_for_n_item(cell, _),
|
|
visit_block_pre = bind push_env_for_block(cell, _),
|
|
visit_block_post = bind pop_env_for_block(cell, _),
|
|
visit_arm_pre = bind walk_arm(e, cell, _),
|
|
visit_arm_post = bind pop_env_for_arm(cell, _),
|
|
visit_expr_pre = bind walk_expr(e, cell, _),
|
|
visit_expr_post = bind pop_env_for_expr(cell, _),
|
|
visit_ty_pre = bind walk_ty(e, cell, _)
|
|
with walk::default_visitor());
|
|
walk::walk_crate(v, c);
|
|
|
|
fn walk_expr(@env e, @mutable list[scope] sc, &@ast::expr exp) {
|
|
push_env_for_expr(sc, exp);
|
|
alt (exp.node) {
|
|
case (ast::expr_path(?p, ?a)) {
|
|
auto df = lookup_path_strict(*e, *sc, exp.span, p.node.idents,
|
|
ns_value);
|
|
e.def_map.insert(a.id, df);
|
|
}
|
|
case (_) {}
|
|
}
|
|
}
|
|
fn walk_ty(@env e, @mutable list[scope] sc, &@ast::ty t) {
|
|
alt (t.node) {
|
|
case (ast::ty_path(?p, ?a)) {
|
|
auto new_def = lookup_path_strict(*e, *sc, t.span,
|
|
p.node.idents, ns_type);
|
|
e.def_map.insert(a.id, new_def);
|
|
}
|
|
case (_) {}
|
|
}
|
|
}
|
|
fn walk_arm(@env e, @mutable list[scope] sc, &ast::arm a) {
|
|
walk_pat(*e, *sc, a.pat);
|
|
push_env_for_arm(sc, a);
|
|
}
|
|
fn walk_pat(&env e, &list[scope] sc, &@ast::pat pat) {
|
|
alt (pat.node) {
|
|
case (ast::pat_tag(?p, ?children, ?a)) {
|
|
auto fnd = lookup_path_strict(e, sc, p.span, p.node.idents,
|
|
ns_value);
|
|
alt (fnd) {
|
|
case (ast::def_variant(?did, ?vid)) {
|
|
e.def_map.insert(a.id, fnd);
|
|
}
|
|
case (_) {
|
|
e.sess.span_err(p.span, "not a tag variant: " +
|
|
str::connect(p.node.idents, "::"));
|
|
fail;
|
|
}
|
|
}
|
|
for (@ast::pat child in children) {
|
|
walk_pat(e, sc, child);
|
|
}
|
|
}
|
|
case (_) {}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Helpers for tracking scope during a walk
|
|
|
|
fn push_env_for_crate(@mutable list[scope] sc, &ast::crate c) {
|
|
*sc = cons[scope](scope_crate(@c), @*sc);
|
|
}
|
|
fn pop_env_for_crate(@mutable list[scope] sc, &ast::crate c) {
|
|
*sc = std::list::cdr(*sc);
|
|
}
|
|
|
|
fn push_env_for_item(@mutable list[scope] sc, &@ast::item i) {
|
|
*sc = cons[scope](scope_item(i), @*sc);
|
|
}
|
|
fn pop_env_for_item(@mutable list[scope] sc, &@ast::item i) {
|
|
*sc = std::list::cdr(*sc);
|
|
}
|
|
|
|
fn push_env_for_method(@mutable list[scope] sc, &@ast::method m) {
|
|
let vec[ast::ty_param] tp = [];
|
|
let @ast::item i = @rec(node=ast::item_fn(m.node.ident,
|
|
m.node.meth,
|
|
tp,
|
|
m.node.id,
|
|
m.node.ann),
|
|
span=m.span);
|
|
*sc = cons[scope](scope_item(i), @*sc);
|
|
}
|
|
fn pop_env_for_method(@mutable list[scope] sc, &@ast::method m) {
|
|
*sc = std::list::cdr(*sc);
|
|
}
|
|
|
|
fn push_env_for_n_item(@mutable list[scope] sc, &@ast::native_item i) {
|
|
*sc = cons[scope](scope_native_item(i), @*sc);
|
|
}
|
|
fn pop_env_for_n_item(@mutable list[scope] sc, &@ast::native_item i) {
|
|
*sc = std::list::cdr(*sc);
|
|
}
|
|
|
|
fn push_env_for_block(@mutable list[scope] sc, &ast::block b) {
|
|
*sc = cons[scope](scope_block(b), @*sc);
|
|
}
|
|
fn pop_env_for_block(@mutable list[scope] sc, &ast::block b) {
|
|
*sc = std::list::cdr(*sc);
|
|
}
|
|
|
|
fn push_env_for_expr(@mutable list[scope] sc, &@ast::expr x) {
|
|
alt (x.node) {
|
|
case (ast::expr_for(?d, _, _, _)) {
|
|
*sc = cons[scope](scope_loop(d), @*sc);
|
|
}
|
|
case (ast::expr_for_each(?d, _, _, _)) {
|
|
*sc = cons[scope](scope_loop(d), @*sc);
|
|
}
|
|
case (_) {}
|
|
}
|
|
}
|
|
fn pop_env_for_expr(@mutable list[scope] sc, &@ast::expr x) {
|
|
alt (x.node) {
|
|
case (ast::expr_for(?d, _, _, _)) {
|
|
*sc = std::list::cdr(*sc);
|
|
}
|
|
case (ast::expr_for_each(?d, _, _, _)) {
|
|
*sc = std::list::cdr(*sc);
|
|
}
|
|
case (_) {}
|
|
}
|
|
}
|
|
|
|
fn push_env_for_arm(@mutable list[scope] sc, &ast::arm p) {
|
|
*sc = cons[scope](scope_arm(p), @*sc);
|
|
}
|
|
fn pop_env_for_arm(@mutable list[scope] sc, &ast::arm p) {
|
|
*sc = std::list::cdr(*sc);
|
|
}
|
|
|
|
fn follow_import(&env e, &list[scope] sc, vec[ident] path, &span sp)
|
|
-> def {
|
|
auto path_len = vec::len(path);
|
|
auto dcur = lookup_in_scope_strict(e, sc, sp, path.(0), ns_module);
|
|
auto i = 1u;
|
|
while (true) {
|
|
if (i == path_len) { break; }
|
|
dcur = lookup_in_mod_strict(e, dcur, sp, path.(i),
|
|
ns_module, outside);
|
|
i += 1u;
|
|
}
|
|
|
|
alt (dcur) {
|
|
case (ast::def_mod(?def_id)) { ret dcur; }
|
|
case (ast::def_native_mod(?def_id)) { ret dcur; }
|
|
case (_) {
|
|
e.sess.span_err(sp, str::connect(path, "::")
|
|
+ " does not name a module.");
|
|
fail;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
// Import resolution
|
|
|
|
fn resolve_import(&env e, &@ast::view_item it, &list[scope] sc) {
|
|
auto defid; auto ids;
|
|
alt (it.node) {
|
|
case (ast::view_item_import(_, ?_ids, ?_defid)) {
|
|
defid = _defid; ids = _ids;
|
|
}
|
|
}
|
|
e.imports.insert(defid._1, resolving(it.span));
|
|
|
|
auto n_idents = vec::len(ids);
|
|
auto end_id = ids.(n_idents - 1u);
|
|
|
|
if (n_idents == 1u) {
|
|
auto next_sc = std::list::cdr(sc);
|
|
register(e, defid, it.span, end_id,
|
|
lookup_in_scope(e, next_sc, it.span, end_id, ns_value),
|
|
lookup_in_scope(e, next_sc, it.span, end_id, ns_type),
|
|
lookup_in_scope(e, next_sc, it.span, end_id, ns_module));
|
|
} else {
|
|
auto dcur = lookup_in_scope_strict(e, sc, it.span, ids.(0),
|
|
ns_module);
|
|
auto i = 1u;
|
|
while (true) {
|
|
if (i == n_idents - 1u) {
|
|
register(e, defid, it.span, end_id,
|
|
lookup_in_mod(e, dcur, it.span, end_id, ns_value,
|
|
outside),
|
|
lookup_in_mod(e, dcur, it.span, end_id, ns_type,
|
|
outside),
|
|
lookup_in_mod(e, dcur, it.span, end_id, ns_module,
|
|
outside));
|
|
break;
|
|
} else {
|
|
dcur = lookup_in_mod_strict(e, dcur, it.span, ids.(i),
|
|
ns_module, outside);
|
|
i += 1u;
|
|
}
|
|
}
|
|
}
|
|
|
|
fn register(&env e, def_id defid, &span sp, &ident id,
|
|
&option::t[def] val, &option::t[def] typ,
|
|
&option::t[def] md) {
|
|
if (option::is_none(val) && option::is_none(typ) &&
|
|
option::is_none(md)) {
|
|
unresolved(e, sp, id, "import");
|
|
}
|
|
e.imports.insert(defid._1, resolved(val, typ, md));
|
|
}
|
|
}
|
|
|
|
// Utilities
|
|
|
|
fn ns_name(namespace ns) -> str {
|
|
alt (ns) {
|
|
case (ns_type) { ret "typename"; }
|
|
case (ns_value) { ret "name"; }
|
|
case (ns_module) { ret "modulename"; }
|
|
}
|
|
}
|
|
|
|
fn unresolved(&env e, &span sp, &ident id, &str kind) {
|
|
e.sess.span_err(sp, "unresolved " + kind + ": " + id);
|
|
}
|
|
|
|
// Lookup helpers
|
|
|
|
fn lookup_path_strict(&env e, &list[scope] sc, &span sp, vec[ident] idents,
|
|
namespace ns) -> def {
|
|
auto n_idents = vec::len(idents);
|
|
auto headns = if (n_idents == 1u) { ns } else { ns_module };
|
|
auto dcur = lookup_in_scope_strict(e, sc, sp, idents.(0), headns);
|
|
auto i = 1u;
|
|
while (i < n_idents) {
|
|
auto curns = if (n_idents == i + 1u) { ns } else { ns_module };
|
|
dcur = lookup_in_mod_strict(e, dcur, sp, idents.(i), curns, outside);
|
|
i += 1u;
|
|
}
|
|
ret dcur;
|
|
}
|
|
|
|
fn lookup_in_scope_strict(&env e, list[scope] sc, &span sp, &ident id,
|
|
namespace ns) -> def {
|
|
alt (lookup_in_scope(e, sc, sp, id, ns)) {
|
|
case (none) {
|
|
unresolved(e, sp, id, ns_name(ns));
|
|
fail;
|
|
}
|
|
case (some(?d)) {
|
|
ret d;
|
|
}
|
|
}
|
|
}
|
|
|
|
fn scope_is_fn(&scope sc) -> bool {
|
|
ret alt (sc) {
|
|
case (scope_item(?it)) {
|
|
alt (it.node) {
|
|
case (ast::item_fn(_, _, _, _, _)) { true }
|
|
case (_) { false }
|
|
}
|
|
}
|
|
case (scope_native_item(_)) { true }
|
|
case (_) { false }
|
|
};
|
|
}
|
|
|
|
fn def_is_local(&def d) -> bool {
|
|
ret alt (d) {
|
|
case (ast::def_arg(_)) { true }
|
|
case (ast::def_local(_)) { true }
|
|
case (ast::def_binding(_)) { true }
|
|
case (_) { false }
|
|
};
|
|
}
|
|
fn def_is_obj_field(&def d) -> bool {
|
|
ret alt (d) {
|
|
case (ast::def_obj_field(_)) { true }
|
|
case (_) { false }
|
|
};
|
|
}
|
|
|
|
fn lookup_in_scope(&env e, list[scope] sc, &span sp, &ident id, namespace ns)
|
|
-> option::t[def] {
|
|
fn in_scope(&env e, &span sp, &ident id, &scope s, namespace ns)
|
|
-> option::t[def] {
|
|
//not recursing through globs
|
|
|
|
alt (s) {
|
|
case (scope_crate(?c)) {
|
|
auto defid = tup(ast::local_crate, -1);
|
|
ret lookup_in_local_mod(e, defid, sp, id, ns, inside);
|
|
}
|
|
case (scope_item(?it)) {
|
|
alt (it.node) {
|
|
case (ast::item_fn(_, ?f, ?ty_params, _, _)) {
|
|
ret lookup_in_fn(id, f.decl, ty_params, ns);
|
|
}
|
|
case (ast::item_obj(_, ?ob, ?ty_params, _, _)) {
|
|
ret lookup_in_obj(id, ob, ty_params, ns);
|
|
}
|
|
case (ast::item_tag(_, _, ?ty_params, _, _)) {
|
|
if (ns == ns_type) {
|
|
ret lookup_in_ty_params(id, ty_params);
|
|
}
|
|
}
|
|
case (ast::item_mod(_, _, ?defid)) {
|
|
ret lookup_in_local_mod(e, defid, sp, id, ns, inside);
|
|
}
|
|
case (ast::item_native_mod(_, ?m, ?defid)) {
|
|
ret lookup_in_local_native_mod(e, defid, sp, id, ns);
|
|
}
|
|
case (ast::item_ty(_, _, ?ty_params, _, _)) {
|
|
if (ns == ns_type) {
|
|
ret lookup_in_ty_params(id, ty_params);
|
|
}
|
|
}
|
|
case (_) {}
|
|
}
|
|
}
|
|
case (scope_native_item(?it)) {
|
|
alt (it.node) {
|
|
case (ast::native_item_fn(_, _, ?decl, ?ty_params, _, _)){
|
|
ret lookup_in_fn(id, decl, ty_params, ns);
|
|
}
|
|
}
|
|
}
|
|
case (scope_loop(?d)) {
|
|
if (ns == ns_value) {
|
|
alt (d.node) {
|
|
case (ast::decl_local(?local)) {
|
|
if (str::eq(local.ident, id)) {
|
|
ret some(ast::def_local(local.id));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
case (scope_block(?b)) {
|
|
ret lookup_in_block(id, b.node, ns);
|
|
}
|
|
case (scope_arm(?a)) {
|
|
if (ns == ns_value) {
|
|
ret lookup_in_pat(id, *a.pat);
|
|
}
|
|
}
|
|
}
|
|
ret none[def];
|
|
}
|
|
|
|
auto left_fn = false;
|
|
// Used to determine whether obj fields are in scope
|
|
auto left_fn_level2 = false;
|
|
while (true) {
|
|
alt (sc) {
|
|
case (nil) {
|
|
ret none[def];
|
|
}
|
|
case (cons(?hd, ?tl)) {
|
|
auto fnd = in_scope(e, sp, id, hd, ns);
|
|
if (!option::is_none(fnd)) {
|
|
auto df = option::get(fnd);
|
|
if ((left_fn && def_is_local(df)) ||
|
|
(left_fn_level2 && def_is_obj_field(df))) {
|
|
e.sess.span_err
|
|
(sp, "attempted dynamic environment-capture");
|
|
}
|
|
ret fnd;
|
|
}
|
|
if (left_fn) { left_fn_level2 = true; }
|
|
if (ns == ns_value && !left_fn) {
|
|
left_fn = scope_is_fn(hd);
|
|
}
|
|
sc = *tl;
|
|
}
|
|
}
|
|
}
|
|
e.sess.bug("reached unreachable code in lookup_in_scope"); // sigh
|
|
fail;
|
|
}
|
|
|
|
fn lookup_in_ty_params(&ident id, &vec[ast::ty_param] ty_params)
|
|
-> option::t[def] {
|
|
auto i = 0u;
|
|
for (ast::ty_param tp in ty_params) {
|
|
if (str::eq(tp, id)) {
|
|
ret some(ast::def_ty_arg(i));
|
|
}
|
|
i += 1u;
|
|
}
|
|
ret none[def];
|
|
}
|
|
|
|
fn lookup_in_pat(&ident id, &ast::pat pat) -> option::t[def] {
|
|
alt (pat.node) {
|
|
case (ast::pat_bind(?name, ?defid, _)) {
|
|
if (str::eq(name, id)) { ret some(ast::def_binding(defid)); }
|
|
}
|
|
case (ast::pat_wild(_)) {}
|
|
case (ast::pat_lit(_, _)) {}
|
|
case (ast::pat_tag(_, ?pats, _)) {
|
|
for (@ast::pat p in pats) {
|
|
auto found = lookup_in_pat(id, *p);
|
|
if (!option::is_none(found)) { ret found; }
|
|
}
|
|
}
|
|
}
|
|
ret none[def];
|
|
}
|
|
|
|
|
|
fn lookup_in_fn(&ident id, &ast::fn_decl decl, &vec[ast::ty_param] ty_params,
|
|
namespace ns) -> option::t[def] {
|
|
alt (ns) {
|
|
case (ns_value) {
|
|
for (ast::arg a in decl.inputs) {
|
|
if (str::eq(a.ident, id)) {
|
|
ret some(ast::def_arg(a.id));
|
|
}
|
|
}
|
|
ret none[def];
|
|
}
|
|
case (ns_type) {
|
|
ret lookup_in_ty_params(id, ty_params);
|
|
}
|
|
case (_) { ret none[def]; }
|
|
}
|
|
}
|
|
|
|
fn lookup_in_obj(&ident id, &ast::_obj ob, &vec[ast::ty_param] ty_params,
|
|
namespace ns) -> option::t[def] {
|
|
alt (ns) {
|
|
case (ns_value) {
|
|
for (ast::obj_field f in ob.fields) {
|
|
if (str::eq(f.ident, id)) {
|
|
ret some(ast::def_obj_field(f.id));
|
|
}
|
|
}
|
|
ret none[def];
|
|
}
|
|
case (ns_type) {
|
|
ret lookup_in_ty_params(id, ty_params);
|
|
}
|
|
case (_) { ret none[def]; }
|
|
}
|
|
}
|
|
|
|
fn lookup_in_block(&ident id, &ast::block_ b, namespace ns)
|
|
-> option::t[def] {
|
|
for (@ast::stmt st in b.stmts) {
|
|
alt (st.node) {
|
|
case (ast::stmt_decl(?d,_)) {
|
|
alt (d.node) {
|
|
case (ast::decl_local(?loc)) {
|
|
if (ns == ns_value && str::eq(id, loc.ident)) {
|
|
ret some(ast::def_local(loc.id));
|
|
}
|
|
}
|
|
case (ast::decl_item(?it)) {
|
|
alt (it.node) {
|
|
case (ast::item_tag(?name, ?variants, _,
|
|
?defid, _)) {
|
|
if (ns == ns_type) {
|
|
if (str::eq(name, id)) {
|
|
ret some(ast::def_ty(defid));
|
|
}
|
|
} else if (ns == ns_value) {
|
|
for (ast::variant v in variants) {
|
|
if (str::eq(v.node.name, id)) {
|
|
ret some(ast::def_variant(
|
|
defid, v.node.id));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
case (_) {
|
|
if (str::eq(ast::item_ident(it), id)) {
|
|
auto found = found_def_item(it, ns);
|
|
if (!option::is_none(found)) {ret found;}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
case (_) {}
|
|
}
|
|
}
|
|
ret none[def];
|
|
}
|
|
|
|
fn found_def_item(&@ast::item i, namespace ns) -> option::t[def] {
|
|
alt (i.node) {
|
|
case (ast::item_const(_, _, _, ?defid, _)) {
|
|
if (ns == ns_value) { ret some(ast::def_const(defid)); }
|
|
}
|
|
case (ast::item_fn(_, _, _, ?defid, _)) {
|
|
if (ns == ns_value) { ret some(ast::def_fn(defid)); }
|
|
}
|
|
case (ast::item_mod(_, _, ?defid)) {
|
|
if (ns == ns_module) { ret some(ast::def_mod(defid)); }
|
|
}
|
|
case (ast::item_native_mod(_, _, ?defid)) {
|
|
if (ns == ns_module) { ret some(ast::def_native_mod(defid)); }
|
|
}
|
|
case (ast::item_ty(_, _, _, ?defid, _)) {
|
|
if (ns == ns_type) { ret some(ast::def_ty(defid)); }
|
|
}
|
|
case (ast::item_tag(_, _, _, ?defid, _)) {
|
|
if (ns == ns_type) { ret some(ast::def_ty(defid)); }
|
|
}
|
|
case (ast::item_obj(_, _, _, ?odid, _)) {
|
|
alt (ns) {
|
|
case (ns_value) { ret some(ast::def_obj(odid.ctor)); }
|
|
case (ns_type) { ret some(ast::def_obj(odid.ty)); }
|
|
case (_) { }
|
|
}
|
|
}
|
|
case (_) { }
|
|
}
|
|
ret none[def];
|
|
}
|
|
|
|
fn lookup_in_mod_strict(&env e, def m, &span sp, &ident id,
|
|
namespace ns, dir dr) -> def {
|
|
alt (lookup_in_mod(e, m, sp, id, ns, dr)) {
|
|
case (none) {
|
|
unresolved(e, sp, id, ns_name(ns));
|
|
fail;
|
|
}
|
|
case (some(?d)) {
|
|
ret d;
|
|
}
|
|
}
|
|
}
|
|
|
|
fn lookup_in_mod(&env e, def m, &span sp, &ident id, namespace ns, dir dr)
|
|
-> option::t[def] {
|
|
auto defid = ast::def_id_of_def(m);
|
|
if (defid._0 != ast::local_crate) {
|
|
// examining a module in an external crate
|
|
auto cached = e.ext_cache.find(tup(defid,id,ns));
|
|
if (!option::is_none(cached)) { ret cached; }
|
|
auto path = [id];
|
|
if (defid._1 != -1) {
|
|
path = e.ext_map.get(defid) + path;
|
|
}
|
|
auto fnd = lookup_external(e, defid._0, path, ns);
|
|
if (!option::is_none(fnd)) {
|
|
e.ext_cache.insert(tup(defid,id,ns), option::get(fnd));
|
|
}
|
|
ret fnd;
|
|
}
|
|
alt (m) {
|
|
case (ast::def_mod(?defid)) {
|
|
ret lookup_in_local_mod(e, defid, sp, id, ns, dr);
|
|
}
|
|
case (ast::def_native_mod(?defid)) {
|
|
ret lookup_in_local_native_mod(e, defid, sp, id, ns);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn found_view_item(&env e, @ast::view_item vi, namespace ns)
|
|
-> option::t[def] {
|
|
alt (vi.node) {
|
|
case (ast::view_item_use(_, _, _, ?ann)) {
|
|
ret some(ast::def_mod(tup(e.crate_map.get(ann.id), -1)));
|
|
}
|
|
case (ast::view_item_import(_, _, ?defid)) {
|
|
ret lookup_import(e, defid, ns);
|
|
}
|
|
case (ast::view_item_import_glob(_, ?defid)) {
|
|
ret none[def]; //will be handled in the fallback glob pass
|
|
}
|
|
}
|
|
}
|
|
|
|
fn lookup_import(&env e, def_id defid, namespace ns) -> option::t[def] {
|
|
alt (e.imports.get(defid._1)) {
|
|
case (todo(?item, ?sc)) {
|
|
resolve_import(e, item, sc);
|
|
ret lookup_import(e, defid, ns);
|
|
}
|
|
case (resolving(?sp)) {
|
|
e.sess.span_err(sp, "cyclic import");
|
|
}
|
|
case (resolved(?val, ?typ, ?md)) {
|
|
ret alt (ns) { case (ns_value) { val }
|
|
case (ns_type) { typ }
|
|
case (ns_module) { md } };
|
|
}
|
|
}
|
|
fail;
|
|
}
|
|
|
|
|
|
fn lookup_in_local_native_mod(&env e, def_id defid, &span sp,
|
|
&ident id, namespace ns) -> option::t[def] {
|
|
ret lookup_in_local_mod(e, defid, sp, id, ns, inside);
|
|
}
|
|
|
|
fn lookup_in_local_mod(&env e, def_id defid, &span sp,
|
|
&ident id, namespace ns, dir dr) -> option::t[def] {
|
|
auto info = e.mod_map.get(defid._1);
|
|
if (dr == outside && !ast::is_exported(id, option::get(info.m))) {
|
|
// if we're in a native mod, then dr==inside, so info.m is some _mod
|
|
ret none[def]; // name is not visible
|
|
}
|
|
alt(info.index.find(id)) {
|
|
case (none) { }
|
|
case (some(?lst)) {
|
|
while (true) {
|
|
alt (lst) {
|
|
case (nil) { break; }
|
|
case (cons(?hd, ?tl)) {
|
|
auto found = lookup_in_mie(e, hd, ns);
|
|
if (!option::is_none(found)) { ret found; }
|
|
lst = *tl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// not local or explicitly imported; try globs:
|
|
ret lookup_glob_in_mod(e, info, sp, id, ns, dr);
|
|
}
|
|
|
|
fn lookup_glob_in_mod(&env e, @indexed_mod info, &span sp,
|
|
&ident id, namespace wanted_ns, dir dr)
|
|
-> option::t[def] {
|
|
fn per_ns(&env e, @indexed_mod info, &span sp, &ident id,
|
|
namespace ns, dir dr) -> option::t[def] {
|
|
fn l_i_m_r(&env e, &def m, &span sp, &ident id,
|
|
namespace ns, dir dr) -> option::t[def] {
|
|
be lookup_in_mod(e, m, sp, id, ns, dr);
|
|
}
|
|
|
|
auto matches = vec::filter_map[def, def]
|
|
(bind l_i_m_r(e, _, sp, id, ns, dr),
|
|
info.glob_imports);
|
|
if (vec::len(matches) == 0u) {
|
|
ret none[def];
|
|
} else if (vec::len(matches) == 1u){
|
|
ret some[def](matches.(0));
|
|
} else {
|
|
for (def match in matches) {
|
|
e.sess.span_note(e.ast_map.get
|
|
(ast::def_id_of_def(match)).span,
|
|
"'" + id + "' is defined here.");
|
|
}
|
|
e.sess.span_err(sp, "'" + id + "' is glob-imported from" +
|
|
" multiple different modules.");
|
|
fail;
|
|
}
|
|
}
|
|
// since we don't know what names we have in advance,
|
|
// absence takes the place of todo()
|
|
if(!info.glob_imported_names.contains_key(id)) {
|
|
info.glob_imported_names.insert(id, resolving(sp));
|
|
auto val = per_ns(e, info, sp, id, ns_value, dr);
|
|
auto typ = per_ns(e, info, sp, id, ns_type, dr);
|
|
auto md = per_ns(e, info, sp, id, ns_module, dr);
|
|
info.glob_imported_names.insert(id, resolved(val, typ, md));
|
|
}
|
|
alt (info.glob_imported_names.get(id)) {
|
|
case (todo(_,_)) { e.sess.bug("Shouldn't've put a todo in."); }
|
|
case (resolving(?sp)) {
|
|
ret none[def]; //circularity is okay in import globs
|
|
}
|
|
case (resolved(?val, ?typ, ?md)) {
|
|
ret alt (wanted_ns) { case (ns_value) { val }
|
|
case (ns_type) { typ }
|
|
case (ns_module) { md } };
|
|
}
|
|
}
|
|
}
|
|
|
|
fn lookup_in_mie(&env e, &mod_index_entry mie, namespace ns)
|
|
-> option::t[def] {
|
|
alt (mie) {
|
|
case (mie_view_item(?view_item)) {
|
|
ret found_view_item(e, view_item, ns);
|
|
}
|
|
case (mie_item(?item)) {
|
|
ret found_def_item(item, ns);
|
|
}
|
|
case (mie_tag_variant(?item, ?variant_idx)) {
|
|
alt (item.node) {
|
|
case (ast::item_tag(_, ?variants, _, ?tid, _)) {
|
|
if (ns == ns_value) {
|
|
auto vid = variants.(variant_idx).node.id;
|
|
ret some(ast::def_variant(tid, vid));
|
|
} else {
|
|
ret none[def];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
case (mie_native_item(?native_item)) {
|
|
alt (native_item.node) {
|
|
case (ast::native_item_ty(_, ?id)) {
|
|
if (ns == ns_type) {
|
|
ret some(ast::def_native_ty(id));
|
|
}
|
|
}
|
|
case (ast::native_item_fn(_, _, _, _, ?id, _)) {
|
|
if (ns == ns_value) {
|
|
ret some(ast::def_native_fn(id));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
case (_) {}
|
|
}
|
|
ret none[def];
|
|
}
|
|
|
|
|
|
|
|
// Module indexing
|
|
|
|
fn add_to_index(&hashmap[ident,list[mod_index_entry]] index, &ident id,
|
|
&mod_index_entry ent) {
|
|
alt (index.find(id)) {
|
|
case (none) {
|
|
index.insert(id, cons(ent, @nil[mod_index_entry]));
|
|
}
|
|
case (some(?prev)) {
|
|
index.insert(id, cons(ent, @prev));
|
|
}
|
|
}
|
|
}
|
|
|
|
fn index_mod(&ast::_mod md) -> mod_index {
|
|
auto index = new_str_hash[list[mod_index_entry]]();
|
|
|
|
for (@ast::view_item it in md.view_items) {
|
|
alt (it.node) {
|
|
case(ast::view_item_use(?id, _, _, _)) {
|
|
add_to_index(index, id, mie_view_item(it));
|
|
}
|
|
case(ast::view_item_import(?def_ident,_,_)) {
|
|
add_to_index(index, def_ident, mie_view_item(it));
|
|
}
|
|
//globbed imports have to be resolved lazily.
|
|
case(ast::view_item_import_glob(_,_)) {}
|
|
case(ast::view_item_export(_)) {}
|
|
}
|
|
}
|
|
|
|
for (@ast::item it in md.items) {
|
|
alt (it.node) {
|
|
case (ast::item_const(?id, _, _, _, _)) {
|
|
add_to_index(index, id, mie_item(it));
|
|
}
|
|
case (ast::item_fn(?id, _, _, _, _)) {
|
|
add_to_index(index, id, mie_item(it));
|
|
}
|
|
case (ast::item_mod(?id, _, _)) {
|
|
add_to_index(index, id, mie_item(it));
|
|
}
|
|
case (ast::item_native_mod(?id, _, _)) {
|
|
add_to_index(index, id, mie_item(it));
|
|
}
|
|
case (ast::item_ty(?id, _, _, _, _)) {
|
|
add_to_index(index, id, mie_item(it));
|
|
}
|
|
case (ast::item_tag(?id, ?variants, _, _, _)) {
|
|
add_to_index(index, id, mie_item(it));
|
|
let uint variant_idx = 0u;
|
|
for (ast::variant v in variants) {
|
|
add_to_index(index, v.node.name,
|
|
mie_tag_variant(it, variant_idx));
|
|
variant_idx += 1u;
|
|
}
|
|
}
|
|
case (ast::item_obj(?id, _, _, _, _)) {
|
|
add_to_index(index, id, mie_item(it));
|
|
}
|
|
}
|
|
}
|
|
|
|
ret index;
|
|
}
|
|
|
|
fn index_nmod(&ast::native_mod md) -> mod_index {
|
|
auto index = new_str_hash[list[mod_index_entry]]();
|
|
|
|
for (@ast::view_item it in md.view_items) {
|
|
alt (it.node) {
|
|
case(ast::view_item_import(?def_ident,_,_)) {
|
|
add_to_index(index, def_ident, mie_view_item(it));
|
|
}
|
|
case(ast::view_item_import_glob(_,_)) {}
|
|
case(ast::view_item_export(_)) {}
|
|
}
|
|
}
|
|
|
|
for (@ast::native_item it in md.items) {
|
|
alt (it.node) {
|
|
case (ast::native_item_ty(?id, _)) {
|
|
add_to_index(index, id, mie_native_item(it));
|
|
}
|
|
case (ast::native_item_fn(?id, _, _, _, _, _)) {
|
|
add_to_index(index, id, mie_native_item(it));
|
|
}
|
|
}
|
|
}
|
|
|
|
ret index;
|
|
}
|
|
|
|
// External lookups
|
|
|
|
fn ns_for_def(def d) -> namespace {
|
|
ret alt (d) {
|
|
case (ast::def_fn(?id)) { ns_value }
|
|
case (ast::def_obj(?id)) { ns_value }
|
|
case (ast::def_obj_field(?id)) { ns_value }
|
|
case (ast::def_mod(?id)) { ns_module }
|
|
case (ast::def_native_mod(?id)) { ns_module }
|
|
case (ast::def_const(?id)) { ns_value }
|
|
case (ast::def_arg(?id)) { ns_value }
|
|
case (ast::def_local(?id)) { ns_value }
|
|
case (ast::def_variant(_, ?id)) { ns_value }
|
|
case (ast::def_ty(?id)) { ns_type }
|
|
case (ast::def_binding(?id)) { ns_type }
|
|
case (ast::def_use(?id)) { ns_module }
|
|
case (ast::def_native_ty(?id)) { ns_type }
|
|
case (ast::def_native_fn(?id)) { ns_value }
|
|
};
|
|
}
|
|
|
|
fn lookup_external(&env e, int cnum, vec[ident] ids, namespace ns)
|
|
-> option::t[def] {
|
|
for (def d in creader::lookup_defs(e.sess, cnum, ids)) {
|
|
e.ext_map.insert(ast::def_id_of_def(d), ids);
|
|
if (ns == ns_for_def(d)) { ret some(d); }
|
|
}
|
|
ret none[def];
|
|
}
|
|
|
|
// Collision detection
|
|
|
|
fn check_for_collisions(&@env e, &ast::crate c) {
|
|
// Module indices make checking those relatively simple -- just check each
|
|
// name for multiple entities in the same namespace.
|
|
for each (@tup(ast::def_num, @indexed_mod) m in e.mod_map.items()) {
|
|
for each (@tup(ident, list[mod_index_entry]) name in
|
|
m._1.index.items()) {
|
|
check_mod_name(*e, name._0, name._1);
|
|
}
|
|
}
|
|
|
|
// Other scopes have to be checked the hard way.
|
|
auto v = rec(visit_item_pre = bind check_item(e, _),
|
|
visit_block_pre = bind check_block(e, _),
|
|
visit_arm_pre = bind check_arm(e, _)
|
|
with walk::default_visitor());
|
|
walk::walk_crate(v, c);
|
|
}
|
|
|
|
fn check_mod_name(&env e, &ident name, &list[mod_index_entry] entries) {
|
|
auto saw_mod = false; auto saw_type = false; auto saw_value = false;
|
|
|
|
fn dup(&env e, &span sp, &str word, &ident name) {
|
|
e.sess.span_err(sp, "duplicate definition of " + word + name);
|
|
}
|
|
|
|
while (true) {
|
|
alt (entries) {
|
|
case (cons(?entry, ?rest)) {
|
|
if (!option::is_none(lookup_in_mie(e, entry, ns_value))) {
|
|
if (saw_value) { dup(e, mie_span(entry), "", name); }
|
|
else { saw_value = true; }
|
|
}
|
|
if (!option::is_none(lookup_in_mie(e, entry, ns_type))) {
|
|
if (saw_type) { dup(e, mie_span(entry), "type ", name); }
|
|
else { saw_type = true; }
|
|
}
|
|
if (!option::is_none(lookup_in_mie(e, entry, ns_module))) {
|
|
if (saw_mod) { dup(e, mie_span(entry), "module ", name); }
|
|
else { saw_mod = true; }
|
|
}
|
|
entries = *rest;
|
|
}
|
|
case (nil) { break; }
|
|
}
|
|
}
|
|
}
|
|
|
|
fn mie_span(&mod_index_entry mie) -> span {
|
|
alt (mie) {
|
|
case (mie_view_item(?item)) { ret item.span; }
|
|
case (mie_item(?item)) { ret item.span; }
|
|
case (mie_tag_variant(?item, _)) { ret item.span; }
|
|
case (mie_native_item(?item)) { ret item.span; }
|
|
}
|
|
}
|
|
|
|
|
|
fn check_item(@env e, &@ast::item i) {
|
|
alt (i.node) {
|
|
case (ast::item_fn(_, ?f, ?ty_params, _, _)) {
|
|
check_fn(*e, i.span, f);
|
|
ensure_unique(*e, i.span, ty_params, ident_id, "type parameter");
|
|
}
|
|
case (ast::item_obj(_, ?ob, ?ty_params, _, _)) {
|
|
fn field_name(&ast::obj_field field) -> ident {
|
|
ret field.ident;
|
|
}
|
|
ensure_unique(*e, i.span, ob.fields, field_name, "object field");
|
|
for (@ast::method m in ob.methods) {
|
|
check_fn(*e, m.span, m.node.meth);
|
|
}
|
|
ensure_unique(*e, i.span, ty_params, ident_id, "type parameter");
|
|
}
|
|
case (ast::item_tag(_, _, ?ty_params, _, _)) {
|
|
ensure_unique(*e, i.span, ty_params, ident_id, "type parameter");
|
|
}
|
|
case (_) {}
|
|
}
|
|
}
|
|
|
|
fn check_arm(@env e, &ast::arm a) {
|
|
fn walk_pat(checker ch, &@ast::pat p) {
|
|
alt (p.node) {
|
|
case (ast::pat_bind(?name, _, _)) {
|
|
add_name(ch, p.span, name);
|
|
}
|
|
case (ast::pat_tag(_, ?children, _)) {
|
|
for (@ast::pat child in children) {
|
|
walk_pat(ch, child);
|
|
}
|
|
}
|
|
case (_) {}
|
|
}
|
|
}
|
|
walk_pat(checker(*e, "binding"), a.pat);
|
|
}
|
|
|
|
fn check_block(@env e, &ast::block b) {
|
|
auto values = checker(*e, "value");
|
|
auto types = checker(*e, "type");
|
|
auto mods = checker(*e, "module");
|
|
|
|
for (@ast::stmt st in b.node.stmts) {
|
|
alt (st.node) {
|
|
case (ast::stmt_decl(?d,_)) {
|
|
alt (d.node) {
|
|
case (ast::decl_local(?loc)) {
|
|
add_name(values, d.span, loc.ident);
|
|
}
|
|
case (ast::decl_item(?it)) {
|
|
alt (it.node) {
|
|
case (ast::item_tag(?name, ?variants, _, _, _)) {
|
|
add_name(types, it.span, name);
|
|
for (ast::variant v in variants) {
|
|
add_name(values, v.span, v.node.name);
|
|
}
|
|
}
|
|
case (ast::item_const(?name, _, _, _, _)) {
|
|
add_name(values, it.span, name);
|
|
}
|
|
case (ast::item_fn(?name, _, _, _, _)) {
|
|
add_name(values, it.span, name);
|
|
}
|
|
case (ast::item_mod(?name, _, _)) {
|
|
add_name(mods, it.span, name);
|
|
}
|
|
case (ast::item_native_mod(?name, _, _)) {
|
|
add_name(mods, it.span, name);
|
|
}
|
|
case (ast::item_ty(?name, _, _, _, _)) {
|
|
add_name(types, it.span, name);
|
|
}
|
|
case (ast::item_obj(?name, _, _, _, _)) {
|
|
add_name(types, it.span, name);
|
|
add_name(values, it.span, name);
|
|
}
|
|
case (_) { }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
case (_) {}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn check_fn(&env e, &span sp, &ast::_fn f) {
|
|
fn arg_name(&ast::arg a) -> ident {
|
|
ret a.ident;
|
|
}
|
|
ensure_unique(e, sp, f.decl.inputs, arg_name, "argument");
|
|
}
|
|
|
|
type checker = @rec(mutable vec[ident] seen,
|
|
str kind,
|
|
session sess);
|
|
fn checker(&env e, str kind) -> checker {
|
|
let vec[ident] seen = [];
|
|
ret @rec(mutable seen=seen, kind=kind, sess=e.sess);
|
|
}
|
|
|
|
fn add_name(&checker ch, &span sp, &ident id) {
|
|
for (ident s in ch.seen) {
|
|
if (str::eq(s, id)) {
|
|
ch.sess.span_err(sp, "duplicate " + ch.kind + " name: " + id);
|
|
}
|
|
}
|
|
vec::push(ch.seen, id);
|
|
}
|
|
|
|
fn ident_id(&ident i) -> ident { ret i; }
|
|
|
|
fn ensure_unique[T](&env e, &span sp, &vec[T] elts, fn (&T) -> ident id,
|
|
&str kind) {
|
|
auto ch = checker(e, kind);
|
|
for (T elt in elts) {
|
|
add_name(ch, sp, id(elt));
|
|
}
|
|
}
|
|
|
|
// Local Variables:
|
|
// mode: rust
|
|
// fill-column: 78;
|
|
// indent-tabs-mode: nil
|
|
// c-basic-offset: 4
|
|
// buffer-file-coding-system: utf-8-unix
|
|
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
|
|
// End:
|