auto merge of #8539 : pnkfelix/rust/fsk-visitor-vpar-defaults-step2, r=graydon,nikomatsakis
r? @nikomatsakis Follow up to #8527 (which was step 1 of 5). See that for overall description Part of #7081
This commit is contained in:
commit
d597f54fc2
@ -17,7 +17,30 @@ use util::ppaux;
|
||||
|
||||
use syntax::ast::*;
|
||||
use syntax::codemap;
|
||||
use syntax::{oldvisit, ast_util, ast_map};
|
||||
use syntax::{ast_util, ast_map};
|
||||
use syntax::visit::Visitor;
|
||||
use syntax::visit;
|
||||
|
||||
struct CheckCrateVisitor {
|
||||
sess: Session,
|
||||
ast_map: ast_map::map,
|
||||
def_map: resolve::DefMap,
|
||||
method_map: typeck::method_map,
|
||||
tcx: ty::ctxt,
|
||||
}
|
||||
|
||||
impl Visitor<bool> for CheckCrateVisitor {
|
||||
fn visit_item(&mut self, i:@item, env:bool) {
|
||||
check_item(self, self.sess, self.ast_map, self.def_map, i, env);
|
||||
}
|
||||
fn visit_pat(&mut self, p:@pat, env:bool) {
|
||||
check_pat(self, p, env);
|
||||
}
|
||||
fn visit_expr(&mut self, ex:@expr, env:bool) {
|
||||
check_expr(self, self.sess, self.def_map, self.method_map,
|
||||
self.tcx, ex, env);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_crate(sess: Session,
|
||||
crate: &Crate,
|
||||
@ -25,39 +48,40 @@ pub fn check_crate(sess: Session,
|
||||
def_map: resolve::DefMap,
|
||||
method_map: typeck::method_map,
|
||||
tcx: ty::ctxt) {
|
||||
oldvisit::visit_crate(crate, (false, oldvisit::mk_vt(@oldvisit::Visitor {
|
||||
visit_item: |a,b| check_item(sess, ast_map, def_map, a, b),
|
||||
visit_pat: check_pat,
|
||||
visit_expr: |a,b|
|
||||
check_expr(sess, def_map, method_map, tcx, a, b),
|
||||
.. *oldvisit::default_visitor()
|
||||
})));
|
||||
let mut v = CheckCrateVisitor {
|
||||
sess: sess,
|
||||
ast_map: ast_map,
|
||||
def_map: def_map,
|
||||
method_map: method_map,
|
||||
tcx: tcx,
|
||||
};
|
||||
visit::walk_crate(&mut v, crate, false);
|
||||
sess.abort_if_errors();
|
||||
}
|
||||
|
||||
pub fn check_item(sess: Session,
|
||||
pub fn check_item(v: &mut CheckCrateVisitor,
|
||||
sess: Session,
|
||||
ast_map: ast_map::map,
|
||||
def_map: resolve::DefMap,
|
||||
it: @item,
|
||||
(_is_const, v): (bool,
|
||||
oldvisit::vt<bool>)) {
|
||||
_is_const: bool) {
|
||||
match it.node {
|
||||
item_static(_, _, ex) => {
|
||||
(v.visit_expr)(ex, (true, v));
|
||||
v.visit_expr(ex, true);
|
||||
check_item_recursion(sess, ast_map, def_map, it);
|
||||
}
|
||||
item_enum(ref enum_definition, _) => {
|
||||
for var in (*enum_definition).variants.iter() {
|
||||
for ex in var.node.disr_expr.iter() {
|
||||
(v.visit_expr)(*ex, (true, v));
|
||||
v.visit_expr(*ex, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => oldvisit::visit_item(it, (false, v))
|
||||
_ => visit::walk_item(v, it, false)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_pat(p: @pat, (_is_const, v): (bool, oldvisit::vt<bool>)) {
|
||||
pub fn check_pat(v: &mut CheckCrateVisitor, p: @pat, _is_const: bool) {
|
||||
fn is_str(e: @expr) -> bool {
|
||||
match e.node {
|
||||
expr_vstore(
|
||||
@ -72,22 +96,22 @@ pub fn check_pat(p: @pat, (_is_const, v): (bool, oldvisit::vt<bool>)) {
|
||||
}
|
||||
match p.node {
|
||||
// Let through plain ~-string literals here
|
||||
pat_lit(a) => if !is_str(a) { (v.visit_expr)(a, (true, v)); },
|
||||
pat_lit(a) => if !is_str(a) { v.visit_expr(a, true); },
|
||||
pat_range(a, b) => {
|
||||
if !is_str(a) { (v.visit_expr)(a, (true, v)); }
|
||||
if !is_str(b) { (v.visit_expr)(b, (true, v)); }
|
||||
if !is_str(a) { v.visit_expr(a, true); }
|
||||
if !is_str(b) { v.visit_expr(b, true); }
|
||||
}
|
||||
_ => oldvisit::visit_pat(p, (false, v))
|
||||
_ => visit::walk_pat(v, p, false)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_expr(sess: Session,
|
||||
pub fn check_expr(v: &mut CheckCrateVisitor,
|
||||
sess: Session,
|
||||
def_map: resolve::DefMap,
|
||||
method_map: typeck::method_map,
|
||||
tcx: ty::ctxt,
|
||||
e: @expr,
|
||||
(is_const, v): (bool,
|
||||
oldvisit::vt<bool>)) {
|
||||
is_const: bool) {
|
||||
if is_const {
|
||||
match e.node {
|
||||
expr_unary(_, deref, _) => { }
|
||||
@ -152,8 +176,8 @@ pub fn check_expr(sess: Session,
|
||||
}
|
||||
}
|
||||
}
|
||||
expr_paren(e) => { check_expr(sess, def_map, method_map,
|
||||
tcx, e, (is_const, v)); }
|
||||
expr_paren(e) => { check_expr(v, sess, def_map, method_map,
|
||||
tcx, e, is_const); }
|
||||
expr_vstore(_, expr_vstore_slice) |
|
||||
expr_vec(_, m_imm) |
|
||||
expr_addr_of(m_imm, _) |
|
||||
@ -192,7 +216,7 @@ pub fn check_expr(sess: Session,
|
||||
}
|
||||
_ => ()
|
||||
}
|
||||
oldvisit::visit_expr(e, (is_const, v));
|
||||
visit::walk_expr(v, e, is_const);
|
||||
}
|
||||
|
||||
#[deriving(Clone)]
|
||||
@ -204,6 +228,8 @@ struct env {
|
||||
idstack: @mut ~[NodeId]
|
||||
}
|
||||
|
||||
struct CheckItemRecursionVisitor;
|
||||
|
||||
// Make sure a const item doesn't recursively refer to itself
|
||||
// FIXME: Should use the dependency graph when it's available (#1356)
|
||||
pub fn check_item_recursion(sess: Session,
|
||||
@ -218,29 +244,27 @@ pub fn check_item_recursion(sess: Session,
|
||||
idstack: @mut ~[]
|
||||
};
|
||||
|
||||
let visitor = oldvisit::mk_vt(@oldvisit::Visitor {
|
||||
visit_item: visit_item,
|
||||
visit_expr: visit_expr,
|
||||
.. *oldvisit::default_visitor()
|
||||
});
|
||||
(visitor.visit_item)(it, (env, visitor));
|
||||
let mut visitor = CheckItemRecursionVisitor;
|
||||
visitor.visit_item(it, env);
|
||||
}
|
||||
|
||||
fn visit_item(it: @item, (env, v): (env, oldvisit::vt<env>)) {
|
||||
impl Visitor<env> for CheckItemRecursionVisitor {
|
||||
fn visit_item(&mut self, it: @item, env: env) {
|
||||
if env.idstack.iter().any(|x| x == &(it.id)) {
|
||||
env.sess.span_fatal(env.root_it.span, "recursive constant");
|
||||
}
|
||||
env.idstack.push(it.id);
|
||||
oldvisit::visit_item(it, (env, v));
|
||||
visit::walk_item(self, it, env);
|
||||
env.idstack.pop();
|
||||
}
|
||||
|
||||
fn visit_expr(e: @expr, (env, v): (env, oldvisit::vt<env>)) {
|
||||
fn visit_expr(&mut self, e: @expr, env: env) {
|
||||
match e.node {
|
||||
expr_path(*) => match env.def_map.find(&e.id) {
|
||||
Some(&def_static(def_id, _)) if ast_util::is_local(def_id) =>
|
||||
match env.ast_map.get_copy(&def_id.node) {
|
||||
ast_map::node_item(it, _) => {
|
||||
(v.visit_item)(it, (env, v));
|
||||
self.visit_item(it, env);
|
||||
}
|
||||
_ => fail!("const not bound to an item")
|
||||
},
|
||||
@ -248,6 +272,6 @@ pub fn check_item_recursion(sess: Session,
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
oldvisit::visit_expr(e, (env, v));
|
||||
visit::walk_expr(self, e, env);
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,8 @@
|
||||
use middle::ty;
|
||||
|
||||
use syntax::ast::*;
|
||||
use syntax::oldvisit;
|
||||
use syntax::visit;
|
||||
use syntax::visit::Visitor;
|
||||
|
||||
#[deriving(Clone)]
|
||||
pub struct Context {
|
||||
@ -20,50 +21,55 @@ pub struct Context {
|
||||
can_ret: bool
|
||||
}
|
||||
|
||||
struct CheckLoopVisitor {
|
||||
tcx: ty::ctxt,
|
||||
}
|
||||
|
||||
pub fn check_crate(tcx: ty::ctxt, crate: &Crate) {
|
||||
oldvisit::visit_crate(crate,
|
||||
(Context { in_loop: false, can_ret: true },
|
||||
oldvisit::mk_vt(@oldvisit::Visitor {
|
||||
visit_item: |i, (_cx, v)| {
|
||||
oldvisit::visit_item(i, (Context {
|
||||
visit::walk_crate(&mut CheckLoopVisitor { tcx: tcx },
|
||||
crate,
|
||||
Context { in_loop: false, can_ret: true });
|
||||
}
|
||||
|
||||
impl Visitor<Context> for CheckLoopVisitor {
|
||||
fn visit_item(&mut self, i:@item, _cx:Context) {
|
||||
visit::walk_item(self, i, Context {
|
||||
in_loop: false,
|
||||
can_ret: true
|
||||
}, v));
|
||||
},
|
||||
visit_expr: |e: @expr, (cx, v): (Context, oldvisit::vt<Context>)| {
|
||||
});
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, e:@expr, cx:Context) {
|
||||
|
||||
match e.node {
|
||||
expr_while(e, ref b) => {
|
||||
(v.visit_expr)(e, (cx, v));
|
||||
(v.visit_block)(b, (Context { in_loop: true,.. cx }, v));
|
||||
self.visit_expr(e, cx);
|
||||
self.visit_block(b, Context { in_loop: true,.. cx });
|
||||
}
|
||||
expr_loop(ref b, _) => {
|
||||
(v.visit_block)(b, (Context { in_loop: true,.. cx }, v));
|
||||
self.visit_block(b, Context { in_loop: true,.. cx });
|
||||
}
|
||||
expr_fn_block(_, ref b) => {
|
||||
(v.visit_block)(b, (Context {
|
||||
in_loop: false,
|
||||
can_ret: false
|
||||
}, v));
|
||||
self.visit_block(b, Context { in_loop: false, can_ret: false });
|
||||
}
|
||||
expr_break(_) => {
|
||||
if !cx.in_loop {
|
||||
tcx.sess.span_err(e.span, "`break` outside of loop");
|
||||
self.tcx.sess.span_err(e.span, "`break` outside of loop");
|
||||
}
|
||||
}
|
||||
expr_again(_) => {
|
||||
if !cx.in_loop {
|
||||
tcx.sess.span_err(e.span, "`loop` outside of loop");
|
||||
self.tcx.sess.span_err(e.span, "`loop` outside of loop");
|
||||
}
|
||||
}
|
||||
expr_ret(oe) => {
|
||||
if !cx.can_ret {
|
||||
tcx.sess.span_err(e.span, "`return` in block function");
|
||||
self.tcx.sess.span_err(e.span, "`return` in block function");
|
||||
}
|
||||
oldvisit::visit_expr_opt(oe, (cx, v));
|
||||
visit::walk_expr_opt(self, oe, cx);
|
||||
}
|
||||
_ => oldvisit::visit_expr(e, (cx, v))
|
||||
_ => visit::walk_expr(self, e, cx)
|
||||
}
|
||||
},
|
||||
.. *oldvisit::default_visitor()
|
||||
})));
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,8 @@ use extra::sort;
|
||||
use syntax::ast::*;
|
||||
use syntax::ast_util::{unguarded_pat, walk_pat};
|
||||
use syntax::codemap::{span, dummy_sp, spanned};
|
||||
use syntax::oldvisit;
|
||||
use syntax::visit;
|
||||
use syntax::visit::{Visitor,fn_kind};
|
||||
|
||||
pub struct MatchCheckCtxt {
|
||||
tcx: ty::ctxt,
|
||||
@ -33,6 +34,22 @@ pub struct MatchCheckCtxt {
|
||||
moves_map: moves::MovesMap
|
||||
}
|
||||
|
||||
struct CheckMatchVisitor {
|
||||
cx: @MatchCheckCtxt
|
||||
}
|
||||
|
||||
impl Visitor<()> for CheckMatchVisitor {
|
||||
fn visit_expr(&mut self, ex:@expr, e:()) {
|
||||
check_expr(self, self.cx, ex, e);
|
||||
}
|
||||
fn visit_local(&mut self, l:@Local, e:()) {
|
||||
check_local(self, self.cx, l, e);
|
||||
}
|
||||
fn visit_fn(&mut self, fk:&fn_kind, fd:&fn_decl, b:&Block, s:span, n:NodeId, e:()) {
|
||||
check_fn(self, self.cx, fk, fd, b, s, n, e);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_crate(tcx: ty::ctxt,
|
||||
method_map: method_map,
|
||||
moves_map: moves::MovesMap,
|
||||
@ -40,20 +57,18 @@ pub fn check_crate(tcx: ty::ctxt,
|
||||
let cx = @MatchCheckCtxt {tcx: tcx,
|
||||
method_map: method_map,
|
||||
moves_map: moves_map};
|
||||
oldvisit::visit_crate(crate, ((), oldvisit::mk_vt(@oldvisit::Visitor {
|
||||
visit_expr: |a,b| check_expr(cx, a, b),
|
||||
visit_local: |a,b| check_local(cx, a, b),
|
||||
visit_fn: |kind, decl, body, sp, id, (e, v)|
|
||||
check_fn(cx, kind, decl, body, sp, id, (e, v)),
|
||||
.. *oldvisit::default_visitor::<()>()
|
||||
})));
|
||||
let mut v = CheckMatchVisitor { cx: cx };
|
||||
|
||||
visit::walk_crate(&mut v, crate, ());
|
||||
|
||||
tcx.sess.abort_if_errors();
|
||||
}
|
||||
|
||||
pub fn check_expr(cx: @MatchCheckCtxt,
|
||||
pub fn check_expr(v: &mut CheckMatchVisitor,
|
||||
cx: @MatchCheckCtxt,
|
||||
ex: @expr,
|
||||
(s, v): ((), oldvisit::vt<()>)) {
|
||||
oldvisit::visit_expr(ex, (s, v));
|
||||
s: ()) {
|
||||
visit::walk_expr(v, ex, s);
|
||||
match ex.node {
|
||||
expr_match(scrut, ref arms) => {
|
||||
// First, check legality of move bindings.
|
||||
@ -787,10 +802,11 @@ pub fn default(cx: &MatchCheckCtxt, r: &[@pat]) -> Option<~[@pat]> {
|
||||
else { None }
|
||||
}
|
||||
|
||||
pub fn check_local(cx: &MatchCheckCtxt,
|
||||
pub fn check_local(v: &mut CheckMatchVisitor,
|
||||
cx: &MatchCheckCtxt,
|
||||
loc: @Local,
|
||||
(s, v): ((), oldvisit::vt<()>)) {
|
||||
oldvisit::visit_local(loc, (s, v));
|
||||
s: ()) {
|
||||
visit::walk_local(v, loc, s);
|
||||
if is_refutable(cx, loc.pat) {
|
||||
cx.tcx.sess.span_err(loc.pat.span,
|
||||
"refutable pattern in local binding");
|
||||
@ -800,15 +816,15 @@ pub fn check_local(cx: &MatchCheckCtxt,
|
||||
check_legality_of_move_bindings(cx, false, [ loc.pat ]);
|
||||
}
|
||||
|
||||
pub fn check_fn(cx: &MatchCheckCtxt,
|
||||
kind: &oldvisit::fn_kind,
|
||||
pub fn check_fn(v: &mut CheckMatchVisitor,
|
||||
cx: &MatchCheckCtxt,
|
||||
kind: &visit::fn_kind,
|
||||
decl: &fn_decl,
|
||||
body: &Block,
|
||||
sp: span,
|
||||
id: NodeId,
|
||||
(s, v): ((),
|
||||
oldvisit::vt<()>)) {
|
||||
oldvisit::visit_fn(kind, decl, body, sp, id, (s, v));
|
||||
s: ()) {
|
||||
visit::walk_fn(v, kind, decl, body, sp, id, s);
|
||||
for input in decl.inputs.iter() {
|
||||
if is_refutable(cx, input.pat) {
|
||||
cx.tcx.sess.span_err(input.pat.span,
|
||||
|
@ -14,7 +14,9 @@ use middle::astencode;
|
||||
use middle::ty;
|
||||
use middle;
|
||||
|
||||
use syntax::{ast, ast_map, ast_util, oldvisit};
|
||||
use syntax::{ast, ast_map, ast_util};
|
||||
use syntax::visit;
|
||||
use syntax::visit::Visitor;
|
||||
use syntax::ast::*;
|
||||
|
||||
use std::float;
|
||||
@ -267,13 +269,18 @@ pub fn lookup_constness(tcx: ty::ctxt, e: &expr) -> constness {
|
||||
}
|
||||
}
|
||||
|
||||
struct ConstEvalVisitor { tcx: ty::ctxt }
|
||||
|
||||
impl Visitor<()> for ConstEvalVisitor {
|
||||
fn visit_expr_post(&mut self, e:@expr, _:()) {
|
||||
classify(e, self.tcx);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process_crate(crate: &ast::Crate,
|
||||
tcx: ty::ctxt) {
|
||||
let v = oldvisit::mk_simple_visitor(@oldvisit::SimpleVisitor {
|
||||
visit_expr_post: |e| { classify(e, tcx); },
|
||||
.. *oldvisit::default_simple_visitor()
|
||||
});
|
||||
oldvisit::visit_crate(crate, ((), v));
|
||||
let mut v = ConstEvalVisitor { tcx: tcx };
|
||||
visit::walk_crate(&mut v, crate, ());
|
||||
tcx.sess.abort_if_errors();
|
||||
}
|
||||
|
||||
|
@ -20,8 +20,10 @@ use syntax::ast::{deref, expr_call, expr_inline_asm, expr_method_call};
|
||||
use syntax::ast::{expr_unary, unsafe_fn, expr_path};
|
||||
use syntax::ast;
|
||||
use syntax::codemap::span;
|
||||
use syntax::oldvisit::{fk_item_fn, fk_method};
|
||||
use syntax::oldvisit;
|
||||
use syntax::visit::{fk_item_fn, fk_method};
|
||||
use syntax::visit;
|
||||
use syntax::visit::{Visitor,fn_kind};
|
||||
use syntax::ast::{fn_decl,Block,NodeId,expr};
|
||||
|
||||
#[deriving(Eq)]
|
||||
enum UnsafeContext {
|
||||
@ -45,6 +47,121 @@ fn type_is_unsafe_function(ty: ty::t) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
struct EffectCheckVisitor {
|
||||
tcx: ty::ctxt,
|
||||
context: @mut Context,
|
||||
}
|
||||
|
||||
impl EffectCheckVisitor {
|
||||
fn require_unsafe(&mut self, span: span, description: &str) {
|
||||
match self.context.unsafe_context {
|
||||
SafeContext => {
|
||||
// Report an error.
|
||||
self.tcx.sess.span_err(span,
|
||||
fmt!("%s requires unsafe function or block",
|
||||
description))
|
||||
}
|
||||
UnsafeBlock(block_id) => {
|
||||
// OK, but record this.
|
||||
debug!("effect: recording unsafe block as used: %?", block_id);
|
||||
let _ = self.tcx.used_unsafe.insert(block_id);
|
||||
}
|
||||
UnsafeFn => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Visitor<()> for EffectCheckVisitor {
|
||||
fn visit_fn(&mut self, fn_kind:&fn_kind, fn_decl:&fn_decl,
|
||||
block:&Block, span:span, node_id:NodeId, _:()) {
|
||||
|
||||
let (is_item_fn, is_unsafe_fn) = match *fn_kind {
|
||||
fk_item_fn(_, _, purity, _) => (true, purity == unsafe_fn),
|
||||
fk_method(_, _, method) => (true, method.purity == unsafe_fn),
|
||||
_ => (false, false),
|
||||
};
|
||||
|
||||
let old_unsafe_context = self.context.unsafe_context;
|
||||
if is_unsafe_fn {
|
||||
self.context.unsafe_context = UnsafeFn
|
||||
} else if is_item_fn {
|
||||
self.context.unsafe_context = SafeContext
|
||||
}
|
||||
|
||||
visit::walk_fn(self,
|
||||
fn_kind,
|
||||
fn_decl,
|
||||
block,
|
||||
span,
|
||||
node_id,
|
||||
());
|
||||
|
||||
self.context.unsafe_context = old_unsafe_context
|
||||
}
|
||||
|
||||
fn visit_block(&mut self, block:&Block, _:()) {
|
||||
|
||||
let old_unsafe_context = self.context.unsafe_context;
|
||||
if block.rules == ast::UnsafeBlock &&
|
||||
self.context.unsafe_context == SafeContext {
|
||||
self.context.unsafe_context = UnsafeBlock(block.id)
|
||||
}
|
||||
|
||||
visit::walk_block(self, block, ());
|
||||
|
||||
self.context.unsafe_context = old_unsafe_context
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, expr:@expr, _:()) {
|
||||
|
||||
match expr.node {
|
||||
expr_method_call(callee_id, _, _, _, _, _) => {
|
||||
let base_type = ty::node_id_to_type(self.tcx, callee_id);
|
||||
debug!("effect: method call case, base type is %s",
|
||||
ppaux::ty_to_str(self.tcx, base_type));
|
||||
if type_is_unsafe_function(base_type) {
|
||||
self.require_unsafe(expr.span,
|
||||
"invocation of unsafe method")
|
||||
}
|
||||
}
|
||||
expr_call(base, _, _) => {
|
||||
let base_type = ty::node_id_to_type(self.tcx, base.id);
|
||||
debug!("effect: call case, base type is %s",
|
||||
ppaux::ty_to_str(self.tcx, base_type));
|
||||
if type_is_unsafe_function(base_type) {
|
||||
self.require_unsafe(expr.span, "call to unsafe function")
|
||||
}
|
||||
}
|
||||
expr_unary(_, deref, base) => {
|
||||
let base_type = ty::node_id_to_type(self.tcx, base.id);
|
||||
debug!("effect: unary case, base type is %s",
|
||||
ppaux::ty_to_str(self.tcx, base_type));
|
||||
match ty::get(base_type).sty {
|
||||
ty_ptr(_) => {
|
||||
self.require_unsafe(expr.span,
|
||||
"dereference of unsafe pointer")
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
expr_inline_asm(*) => {
|
||||
self.require_unsafe(expr.span, "use of inline assembly")
|
||||
}
|
||||
expr_path(*) => {
|
||||
match ty::resolve_expr(self.tcx, expr) {
|
||||
ast::def_static(_, true) => {
|
||||
self.require_unsafe(expr.span, "use of mutable static")
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
visit::walk_expr(self, expr, ());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_crate(tcx: ty::ctxt,
|
||||
method_map: method_map,
|
||||
crate: &ast::Crate) {
|
||||
@ -53,112 +170,10 @@ pub fn check_crate(tcx: ty::ctxt,
|
||||
unsafe_context: SafeContext,
|
||||
};
|
||||
|
||||
let require_unsafe: @fn(span: span,
|
||||
description: &str) = |span, description| {
|
||||
match context.unsafe_context {
|
||||
SafeContext => {
|
||||
// Report an error.
|
||||
tcx.sess.span_err(span,
|
||||
fmt!("%s requires unsafe function or block",
|
||||
description))
|
||||
}
|
||||
UnsafeBlock(block_id) => {
|
||||
// OK, but record this.
|
||||
debug!("effect: recording unsafe block as used: %?", block_id);
|
||||
let _ = tcx.used_unsafe.insert(block_id);
|
||||
}
|
||||
UnsafeFn => {}
|
||||
}
|
||||
let mut visitor = EffectCheckVisitor {
|
||||
tcx: tcx,
|
||||
context: context,
|
||||
};
|
||||
|
||||
let visitor = oldvisit::mk_vt(@oldvisit::Visitor {
|
||||
visit_fn: |fn_kind, fn_decl, block, span, node_id, (_, visitor)| {
|
||||
let (is_item_fn, is_unsafe_fn) = match *fn_kind {
|
||||
fk_item_fn(_, _, purity, _) => (true, purity == unsafe_fn),
|
||||
fk_method(_, _, method) => (true, method.purity == unsafe_fn),
|
||||
_ => (false, false),
|
||||
};
|
||||
|
||||
let old_unsafe_context = context.unsafe_context;
|
||||
if is_unsafe_fn {
|
||||
context.unsafe_context = UnsafeFn
|
||||
} else if is_item_fn {
|
||||
context.unsafe_context = SafeContext
|
||||
}
|
||||
|
||||
oldvisit::visit_fn(fn_kind,
|
||||
fn_decl,
|
||||
block,
|
||||
span,
|
||||
node_id,
|
||||
((),
|
||||
visitor));
|
||||
|
||||
context.unsafe_context = old_unsafe_context
|
||||
},
|
||||
|
||||
visit_block: |block, (_, visitor)| {
|
||||
let old_unsafe_context = context.unsafe_context;
|
||||
if block.rules == ast::UnsafeBlock &&
|
||||
context.unsafe_context == SafeContext {
|
||||
context.unsafe_context = UnsafeBlock(block.id)
|
||||
}
|
||||
|
||||
oldvisit::visit_block(block, ((), visitor));
|
||||
|
||||
context.unsafe_context = old_unsafe_context
|
||||
},
|
||||
|
||||
visit_expr: |expr, (_, visitor)| {
|
||||
match expr.node {
|
||||
expr_method_call(callee_id, _, _, _, _, _) => {
|
||||
let base_type = ty::node_id_to_type(tcx, callee_id);
|
||||
debug!("effect: method call case, base type is %s",
|
||||
ppaux::ty_to_str(tcx, base_type));
|
||||
if type_is_unsafe_function(base_type) {
|
||||
require_unsafe(expr.span,
|
||||
"invocation of unsafe method")
|
||||
}
|
||||
}
|
||||
expr_call(base, _, _) => {
|
||||
let base_type = ty::node_id_to_type(tcx, base.id);
|
||||
debug!("effect: call case, base type is %s",
|
||||
ppaux::ty_to_str(tcx, base_type));
|
||||
if type_is_unsafe_function(base_type) {
|
||||
require_unsafe(expr.span, "call to unsafe function")
|
||||
}
|
||||
}
|
||||
expr_unary(_, deref, base) => {
|
||||
let base_type = ty::node_id_to_type(tcx, base.id);
|
||||
debug!("effect: unary case, base type is %s",
|
||||
ppaux::ty_to_str(tcx, base_type));
|
||||
match ty::get(base_type).sty {
|
||||
ty_ptr(_) => {
|
||||
require_unsafe(expr.span,
|
||||
"dereference of unsafe pointer")
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
expr_inline_asm(*) => {
|
||||
require_unsafe(expr.span, "use of inline assembly")
|
||||
}
|
||||
expr_path(*) => {
|
||||
match ty::resolve_expr(tcx, expr) {
|
||||
ast::def_static(_, true) => {
|
||||
require_unsafe(expr.span, "use of mutable static")
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
oldvisit::visit_expr(expr, ((), visitor))
|
||||
},
|
||||
|
||||
.. *oldvisit::default_visitor()
|
||||
});
|
||||
|
||||
oldvisit::visit_crate(crate, ((), visitor))
|
||||
visit::walk_crate(&mut visitor, crate, ());
|
||||
}
|
||||
|
@ -34,54 +34,57 @@ use syntax::ast_util::{variant_visibility_to_privacy, visibility_to_privacy};
|
||||
use syntax::attr;
|
||||
use syntax::codemap::span;
|
||||
use syntax::parse::token;
|
||||
use syntax::oldvisit;
|
||||
use syntax::visit;
|
||||
use syntax::visit::Visitor;
|
||||
use syntax::ast::{_mod,expr,item,Block,pat};
|
||||
|
||||
pub fn check_crate<'mm>(tcx: ty::ctxt,
|
||||
method_map: &'mm method_map,
|
||||
crate: &ast::Crate) {
|
||||
let privileged_items = @mut ~[];
|
||||
struct PrivacyVisitor {
|
||||
tcx: ty::ctxt,
|
||||
privileged_items: @mut ~[NodeId],
|
||||
}
|
||||
|
||||
impl PrivacyVisitor {
|
||||
// Adds an item to its scope.
|
||||
let add_privileged_item: @fn(@ast::item, &mut uint) = |item, count| {
|
||||
fn add_privileged_item(&mut self, item: @ast::item, count: &mut uint) {
|
||||
match item.node {
|
||||
item_struct(*) | item_trait(*) | item_enum(*) |
|
||||
item_fn(*) => {
|
||||
privileged_items.push(item.id);
|
||||
self.privileged_items.push(item.id);
|
||||
*count += 1;
|
||||
}
|
||||
item_impl(_, _, _, ref methods) => {
|
||||
for method in methods.iter() {
|
||||
privileged_items.push(method.id);
|
||||
self.privileged_items.push(method.id);
|
||||
*count += 1;
|
||||
}
|
||||
privileged_items.push(item.id);
|
||||
self.privileged_items.push(item.id);
|
||||
*count += 1;
|
||||
}
|
||||
item_foreign_mod(ref foreign_mod) => {
|
||||
for foreign_item in foreign_mod.items.iter() {
|
||||
privileged_items.push(foreign_item.id);
|
||||
self.privileged_items.push(foreign_item.id);
|
||||
*count += 1;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Adds items that are privileged to this scope.
|
||||
let add_privileged_items: @fn(&[@ast::item]) -> uint = |items| {
|
||||
fn add_privileged_items(&mut self, items: &[@ast::item]) -> uint {
|
||||
let mut count = 0;
|
||||
for &item in items.iter() {
|
||||
add_privileged_item(item, &mut count);
|
||||
self.add_privileged_item(item, &mut count);
|
||||
}
|
||||
count
|
||||
};
|
||||
}
|
||||
|
||||
// Checks that an enum variant is in scope
|
||||
let check_variant: @fn(span: span, enum_id: ast::def_id) =
|
||||
|span, enum_id| {
|
||||
let variant_info = ty::enum_variants(tcx, enum_id)[0];
|
||||
fn check_variant(&mut self, span: span, enum_id: ast::def_id) {
|
||||
let variant_info = ty::enum_variants(self.tcx, enum_id)[0];
|
||||
let parental_privacy = if is_local(enum_id) {
|
||||
let parent_vis = ast_map::node_item_query(tcx.items, enum_id.node,
|
||||
let parent_vis = ast_map::node_item_query(self.tcx.items,
|
||||
enum_id.node,
|
||||
|it| { it.vis },
|
||||
~"unbound enum parent when checking \
|
||||
dereference of enum type");
|
||||
@ -99,15 +102,14 @@ pub fn check_crate<'mm>(tcx: ty::ctxt,
|
||||
if variant_visibility_to_privacy(variant_info.vis,
|
||||
parental_privacy == Public)
|
||||
== Private {
|
||||
tcx.sess.span_err(span,
|
||||
self.tcx.sess.span_err(span,
|
||||
"can only dereference enums \
|
||||
with a single, public variant");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Returns true if a crate-local method is private and false otherwise.
|
||||
let method_is_private: @fn(span: span, method_id: NodeId) -> bool =
|
||||
|span, method_id| {
|
||||
fn method_is_private(&mut self, span: span, method_id: NodeId) -> bool {
|
||||
let check = |vis: visibility, container_id: def_id| {
|
||||
let mut is_private = false;
|
||||
if vis == private {
|
||||
@ -117,12 +119,12 @@ pub fn check_crate<'mm>(tcx: ty::ctxt,
|
||||
} else {
|
||||
// Look up the enclosing impl.
|
||||
if container_id.crate != LOCAL_CRATE {
|
||||
tcx.sess.span_bug(span,
|
||||
self.tcx.sess.span_bug(span,
|
||||
"local method isn't in local \
|
||||
impl?!");
|
||||
}
|
||||
|
||||
match tcx.items.find(&container_id.node) {
|
||||
match self.tcx.items.find(&container_id.node) {
|
||||
Some(&node_item(item, _)) => {
|
||||
match item.node {
|
||||
item_impl(_, None, _, _)
|
||||
@ -133,10 +135,10 @@ pub fn check_crate<'mm>(tcx: ty::ctxt,
|
||||
}
|
||||
}
|
||||
Some(_) => {
|
||||
tcx.sess.span_bug(span, "impl wasn't an item?!");
|
||||
self.tcx.sess.span_bug(span, "impl wasn't an item?!");
|
||||
}
|
||||
None => {
|
||||
tcx.sess.span_bug(span, "impl wasn't in AST map?!");
|
||||
self.tcx.sess.span_bug(span, "impl wasn't in AST map?!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -144,7 +146,7 @@ pub fn check_crate<'mm>(tcx: ty::ctxt,
|
||||
is_private
|
||||
};
|
||||
|
||||
match tcx.items.find(&method_id) {
|
||||
match self.tcx.items.find(&method_id) {
|
||||
Some(&node_method(method, impl_id, _)) => {
|
||||
check(method.vis, impl_id)
|
||||
}
|
||||
@ -155,26 +157,25 @@ pub fn check_crate<'mm>(tcx: ty::ctxt,
|
||||
}
|
||||
}
|
||||
Some(_) => {
|
||||
tcx.sess.span_bug(span,
|
||||
self.tcx.sess.span_bug(span,
|
||||
fmt!("method_is_private: method was a %s?!",
|
||||
ast_map::node_id_to_str(
|
||||
tcx.items,
|
||||
self.tcx.items,
|
||||
method_id,
|
||||
token::get_ident_interner())));
|
||||
}
|
||||
None => {
|
||||
tcx.sess.span_bug(span, "method not found in \
|
||||
self.tcx.sess.span_bug(span, "method not found in \
|
||||
AST map?!");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Returns true if the given local item is private and false otherwise.
|
||||
let local_item_is_private: @fn(span: span, item_id: NodeId) -> bool =
|
||||
|span, item_id| {
|
||||
fn local_item_is_private(&mut self, span: span, item_id: NodeId) -> bool {
|
||||
let mut f: &fn(NodeId) -> bool = |_| false;
|
||||
f = |item_id| {
|
||||
match tcx.items.find(&item_id) {
|
||||
match self.tcx.items.find(&item_id) {
|
||||
Some(&node_item(item, _)) => item.vis != public,
|
||||
Some(&node_foreign_item(*)) => false,
|
||||
Some(&node_method(method, impl_did, _)) => {
|
||||
@ -186,104 +187,96 @@ pub fn check_crate<'mm>(tcx: ty::ctxt,
|
||||
}
|
||||
Some(&node_trait_method(_, trait_did, _)) => f(trait_did.node),
|
||||
Some(_) => {
|
||||
tcx.sess.span_bug(span,
|
||||
self.tcx.sess.span_bug(span,
|
||||
fmt!("local_item_is_private: item was \
|
||||
a %s?!",
|
||||
ast_map::node_id_to_str(
|
||||
tcx.items,
|
||||
self.tcx.items,
|
||||
item_id,
|
||||
token::get_ident_interner())));
|
||||
}
|
||||
None => {
|
||||
tcx.sess.span_bug(span, "item not found in AST map?!");
|
||||
self.tcx.sess.span_bug(span, "item not found in AST map?!");
|
||||
}
|
||||
}
|
||||
};
|
||||
f(item_id)
|
||||
};
|
||||
}
|
||||
|
||||
// Checks that a private field is in scope.
|
||||
let check_field: @fn(span: span, id: ast::def_id, ident: ast::ident) =
|
||||
|span, id, ident| {
|
||||
let fields = ty::lookup_struct_fields(tcx, id);
|
||||
fn check_field(&mut self, span: span, id: ast::def_id, ident: ast::ident) {
|
||||
let fields = ty::lookup_struct_fields(self.tcx, id);
|
||||
for field in fields.iter() {
|
||||
if field.ident != ident { loop; }
|
||||
if field.vis == private {
|
||||
tcx.sess.span_err(span, fmt!("field `%s` is private",
|
||||
self.tcx.sess.span_err(span, fmt!("field `%s` is private",
|
||||
token::ident_to_str(&ident)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Given the ID of a method, checks to ensure it's in scope.
|
||||
let check_method_common: @fn(span: span,
|
||||
method_id: def_id,
|
||||
name: &ident) =
|
||||
|span, method_id, name| {
|
||||
fn check_method_common(&mut self, span: span, method_id: def_id, name: &ident) {
|
||||
// If the method is a default method, we need to use the def_id of
|
||||
// the default implementation.
|
||||
// Having to do this this is really unfortunate.
|
||||
let method_id = ty::method(tcx, method_id).provided_source
|
||||
let method_id = ty::method(self.tcx, method_id).provided_source
|
||||
.unwrap_or_default(method_id);
|
||||
|
||||
if method_id.crate == LOCAL_CRATE {
|
||||
let is_private = method_is_private(span, method_id.node);
|
||||
let container_id = ty::method(tcx, method_id).container_id;
|
||||
let is_private = self.method_is_private(span, method_id.node);
|
||||
let container_id = ty::method(self.tcx, method_id).container_id;
|
||||
if is_private &&
|
||||
(container_id.crate != LOCAL_CRATE ||
|
||||
!privileged_items.iter().any(|x| x == &(container_id.node))) {
|
||||
tcx.sess.span_err(span,
|
||||
!self.privileged_items.iter().any(|x| x == &(container_id.node))) {
|
||||
self.tcx.sess.span_err(span,
|
||||
fmt!("method `%s` is private",
|
||||
token::ident_to_str(name)));
|
||||
}
|
||||
} else {
|
||||
let visibility =
|
||||
csearch::get_item_visibility(tcx.sess.cstore, method_id);
|
||||
csearch::get_item_visibility(self.tcx.sess.cstore, method_id);
|
||||
if visibility != public {
|
||||
tcx.sess.span_err(span,
|
||||
self.tcx.sess.span_err(span,
|
||||
fmt!("method `%s` is private",
|
||||
token::ident_to_str(name)));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Checks that a private path is in scope.
|
||||
let check_path: @fn(span: span, def: def, path: &Path) =
|
||||
|span, def, path| {
|
||||
fn check_path(&mut self, span: span, def: def, path: &Path) {
|
||||
debug!("checking path");
|
||||
match def {
|
||||
def_static_method(method_id, _, _) => {
|
||||
debug!("found static method def, checking it");
|
||||
check_method_common(span, method_id, path.idents.last())
|
||||
self.check_method_common(span, method_id, path.idents.last())
|
||||
}
|
||||
def_fn(def_id, _) => {
|
||||
if def_id.crate == LOCAL_CRATE {
|
||||
if local_item_is_private(span, def_id.node) &&
|
||||
!privileged_items.iter().any(|x| x == &def_id.node) {
|
||||
tcx.sess.span_err(span,
|
||||
if self.local_item_is_private(span, def_id.node) &&
|
||||
!self.privileged_items.iter().any(|x| x == &def_id.node) {
|
||||
self.tcx.sess.span_err(span,
|
||||
fmt!("function `%s` is private",
|
||||
token::ident_to_str(path.idents.last())));
|
||||
}
|
||||
} else if csearch::get_item_visibility(tcx.sess.cstore,
|
||||
} else if csearch::get_item_visibility(self.tcx.sess.cstore,
|
||||
def_id) != public {
|
||||
tcx.sess.span_err(span,
|
||||
self.tcx.sess.span_err(span,
|
||||
fmt!("function `%s` is private",
|
||||
token::ident_to_str(path.idents.last())));
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Checks that a private method is in scope.
|
||||
let check_method: @fn(span: span,
|
||||
origin: &method_origin,
|
||||
ident: ast::ident) =
|
||||
|span, origin, ident| {
|
||||
fn check_method(&mut self, span: span, origin: &method_origin, ident: ast::ident) {
|
||||
match *origin {
|
||||
method_static(method_id) => {
|
||||
check_method_common(span, method_id, &ident)
|
||||
self.check_method_common(span, method_id, &ident)
|
||||
}
|
||||
method_param(method_param {
|
||||
trait_id: trait_id,
|
||||
@ -292,19 +285,20 @@ pub fn check_crate<'mm>(tcx: ty::ctxt,
|
||||
}) |
|
||||
method_trait(trait_id, method_num) => {
|
||||
if trait_id.crate == LOCAL_CRATE {
|
||||
match tcx.items.find(&trait_id.node) {
|
||||
match self.tcx.items.find(&trait_id.node) {
|
||||
Some(&node_item(item, _)) => {
|
||||
match item.node {
|
||||
item_trait(_, _, ref methods) => {
|
||||
if method_num >= (*methods).len() {
|
||||
tcx.sess.span_bug(span, "method number out of range?!");
|
||||
self.tcx.sess.span_bug(span,
|
||||
"method number out of range?!");
|
||||
}
|
||||
match (*methods)[method_num] {
|
||||
provided(method)
|
||||
if method.vis == private &&
|
||||
!privileged_items.iter()
|
||||
!self.privileged_items.iter()
|
||||
.any(|x| x == &(trait_id.node)) => {
|
||||
tcx.sess.span_err(span,
|
||||
self.tcx.sess.span_err(span,
|
||||
fmt!("method `%s` is private",
|
||||
token::ident_to_str(&method
|
||||
.ident)));
|
||||
@ -316,15 +310,16 @@ pub fn check_crate<'mm>(tcx: ty::ctxt,
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
tcx.sess.span_bug(span, "trait wasn't actually a trait?!");
|
||||
self.tcx.sess.span_bug(span, "trait wasn't actually a trait?!");
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(_) => {
|
||||
tcx.sess.span_bug(span, "trait wasn't an item?!");
|
||||
self.tcx.sess.span_bug(span, "trait wasn't an item?!");
|
||||
}
|
||||
None => {
|
||||
tcx.sess.span_bug(span, "trait item wasn't found in the AST map?!");
|
||||
self.tcx.sess.span_bug(span,
|
||||
"trait item wasn't found in the AST map?!");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -332,30 +327,35 @@ pub fn check_crate<'mm>(tcx: ty::ctxt,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let visitor = oldvisit::mk_vt(@oldvisit::Visitor {
|
||||
visit_mod: |the_module, span, node_id, (method_map, visitor)| {
|
||||
let n_added = add_privileged_items(the_module.items);
|
||||
impl<'self> Visitor<&'self method_map> for PrivacyVisitor {
|
||||
|
||||
oldvisit::visit_mod(the_module,
|
||||
span,
|
||||
node_id,
|
||||
(method_map, visitor));
|
||||
fn visit_mod<'mm>(&mut self, the_module:&_mod, _:span, _:NodeId,
|
||||
method_map:&'mm method_map) {
|
||||
|
||||
let n_added = self.add_privileged_items(the_module.items);
|
||||
|
||||
visit::walk_mod(self, the_module, method_map);
|
||||
|
||||
do n_added.times {
|
||||
ignore(privileged_items.pop());
|
||||
ignore(self.privileged_items.pop());
|
||||
}
|
||||
},
|
||||
visit_item: |item, (method_map, visitor)| {
|
||||
}
|
||||
|
||||
fn visit_item<'mm>(&mut self, item:@item, method_map:&'mm method_map) {
|
||||
|
||||
// Do not check privacy inside items with the resolve_unexported
|
||||
// attribute. This is used for the test runner.
|
||||
if !attr::contains_name(item.attrs, "!resolve_unexported") {
|
||||
check_sane_privacy(tcx, item);
|
||||
oldvisit::visit_item(item, (method_map, visitor));
|
||||
check_sane_privacy(self.tcx, item);
|
||||
visit::walk_item(self, item, method_map);
|
||||
}
|
||||
},
|
||||
visit_block: |block, (method_map, visitor)| {
|
||||
}
|
||||
|
||||
fn visit_block<'mm>(&mut self, block:&Block, method_map:&'mm method_map) {
|
||||
|
||||
// Gather up all the privileged items.
|
||||
let mut n_added = 0;
|
||||
for stmt in block.stmts.iter() {
|
||||
@ -363,7 +363,7 @@ pub fn check_crate<'mm>(tcx: ty::ctxt,
|
||||
stmt_decl(decl, _) => {
|
||||
match decl.node {
|
||||
decl_item(item) => {
|
||||
add_privileged_item(item, &mut n_added);
|
||||
self.add_privileged_item(item, &mut n_added);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@ -372,15 +372,16 @@ pub fn check_crate<'mm>(tcx: ty::ctxt,
|
||||
}
|
||||
}
|
||||
|
||||
oldvisit::visit_block(block, (method_map, visitor));
|
||||
visit::walk_block(self, block, method_map);
|
||||
|
||||
do n_added.times {
|
||||
ignore(privileged_items.pop());
|
||||
ignore(self.privileged_items.pop());
|
||||
}
|
||||
},
|
||||
visit_expr: |expr,
|
||||
(method_map, visitor):
|
||||
(&'mm method_map, oldvisit::vt<&'mm method_map>)| {
|
||||
|
||||
}
|
||||
|
||||
fn visit_expr<'mm>(&mut self, expr:@expr, method_map:&'mm method_map) {
|
||||
|
||||
match expr.node {
|
||||
expr_field(base, ident, _) => {
|
||||
// Method calls are now a special syntactic form,
|
||||
@ -389,35 +390,35 @@ pub fn check_crate<'mm>(tcx: ty::ctxt,
|
||||
|
||||
// With type_autoderef, make sure we don't
|
||||
// allow pointers to violate privacy
|
||||
match ty::get(ty::type_autoderef(tcx, ty::expr_ty(tcx,
|
||||
match ty::get(ty::type_autoderef(self.tcx, ty::expr_ty(self.tcx,
|
||||
base))).sty {
|
||||
ty_struct(id, _)
|
||||
if id.crate != LOCAL_CRATE || !privileged_items.iter()
|
||||
if id.crate != LOCAL_CRATE || !self.privileged_items.iter()
|
||||
.any(|x| x == &(id.node)) => {
|
||||
debug!("(privacy checking) checking field access");
|
||||
check_field(expr.span, id, ident);
|
||||
self.check_field(expr.span, id, ident);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
expr_method_call(_, base, ident, _, _, _) => {
|
||||
// Ditto
|
||||
match ty::get(ty::type_autoderef(tcx, ty::expr_ty(tcx,
|
||||
match ty::get(ty::type_autoderef(self.tcx, ty::expr_ty(self.tcx,
|
||||
base))).sty {
|
||||
ty_enum(id, _) |
|
||||
ty_struct(id, _)
|
||||
if id.crate != LOCAL_CRATE ||
|
||||
!privileged_items.iter().any(|x| x == &(id.node)) => {
|
||||
!self.privileged_items.iter().any(|x| x == &(id.node)) => {
|
||||
match method_map.find(&expr.id) {
|
||||
None => {
|
||||
tcx.sess.span_bug(expr.span,
|
||||
self.tcx.sess.span_bug(expr.span,
|
||||
"method call not in \
|
||||
method map");
|
||||
}
|
||||
Some(ref entry) => {
|
||||
debug!("(privacy checking) checking \
|
||||
impl method");
|
||||
check_method(expr.span, &entry.origin, ident);
|
||||
self.check_method(expr.span, &entry.origin, ident);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -425,35 +426,35 @@ pub fn check_crate<'mm>(tcx: ty::ctxt,
|
||||
}
|
||||
}
|
||||
expr_path(ref path) => {
|
||||
check_path(expr.span, tcx.def_map.get_copy(&expr.id), path);
|
||||
self.check_path(expr.span, self.tcx.def_map.get_copy(&expr.id), path);
|
||||
}
|
||||
expr_struct(_, ref fields, _) => {
|
||||
match ty::get(ty::expr_ty(tcx, expr)).sty {
|
||||
match ty::get(ty::expr_ty(self.tcx, expr)).sty {
|
||||
ty_struct(id, _) => {
|
||||
if id.crate != LOCAL_CRATE ||
|
||||
!privileged_items.iter().any(|x| x == &(id.node)) {
|
||||
!self.privileged_items.iter().any(|x| x == &(id.node)) {
|
||||
for field in (*fields).iter() {
|
||||
debug!("(privacy checking) checking \
|
||||
field in struct literal");
|
||||
check_field(expr.span, id, field.ident);
|
||||
self.check_field(expr.span, id, field.ident);
|
||||
}
|
||||
}
|
||||
}
|
||||
ty_enum(id, _) => {
|
||||
if id.crate != LOCAL_CRATE ||
|
||||
!privileged_items.iter().any(|x| x == &(id.node)) {
|
||||
match tcx.def_map.get_copy(&expr.id) {
|
||||
!self.privileged_items.iter().any(|x| x == &(id.node)) {
|
||||
match self.tcx.def_map.get_copy(&expr.id) {
|
||||
def_variant(_, variant_id) => {
|
||||
for field in (*fields).iter() {
|
||||
debug!("(privacy checking) \
|
||||
checking field in \
|
||||
struct variant \
|
||||
literal");
|
||||
check_field(expr.span, variant_id, field.ident);
|
||||
self.check_field(expr.span, variant_id, field.ident);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
tcx.sess.span_bug(expr.span,
|
||||
self.tcx.sess.span_bug(expr.span,
|
||||
"resolve didn't \
|
||||
map enum struct \
|
||||
constructor to a \
|
||||
@ -463,7 +464,7 @@ pub fn check_crate<'mm>(tcx: ty::ctxt,
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
tcx.sess.span_bug(expr.span, "struct expr \
|
||||
self.tcx.sess.span_bug(expr.span, "struct expr \
|
||||
didn't have \
|
||||
struct type?!");
|
||||
}
|
||||
@ -474,11 +475,11 @@ pub fn check_crate<'mm>(tcx: ty::ctxt,
|
||||
// enum type t, then t's first variant is public or
|
||||
// privileged. (We can assume it has only one variant
|
||||
// since typeck already happened.)
|
||||
match ty::get(ty::expr_ty(tcx, operand)).sty {
|
||||
match ty::get(ty::expr_ty(self.tcx, operand)).sty {
|
||||
ty_enum(id, _) => {
|
||||
if id.crate != LOCAL_CRATE ||
|
||||
!privileged_items.iter().any(|x| x == &(id.node)) {
|
||||
check_variant(expr.span, id);
|
||||
!self.privileged_items.iter().any(|x| x == &(id.node)) {
|
||||
self.check_variant(expr.span, id);
|
||||
}
|
||||
}
|
||||
_ => { /* No check needed */ }
|
||||
@ -487,36 +488,39 @@ pub fn check_crate<'mm>(tcx: ty::ctxt,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
oldvisit::visit_expr(expr, (method_map, visitor));
|
||||
},
|
||||
visit_pat: |pattern, (method_map, visitor)| {
|
||||
visit::walk_expr(self, expr, method_map);
|
||||
|
||||
}
|
||||
|
||||
fn visit_pat<'mm>(&mut self, pattern:@pat, method_map:&'mm method_map) {
|
||||
|
||||
match pattern.node {
|
||||
pat_struct(_, ref fields, _) => {
|
||||
match ty::get(ty::pat_ty(tcx, pattern)).sty {
|
||||
match ty::get(ty::pat_ty(self.tcx, pattern)).sty {
|
||||
ty_struct(id, _) => {
|
||||
if id.crate != LOCAL_CRATE ||
|
||||
!privileged_items.iter().any(|x| x == &(id.node)) {
|
||||
!self.privileged_items.iter().any(|x| x == &(id.node)) {
|
||||
for field in fields.iter() {
|
||||
debug!("(privacy checking) checking \
|
||||
struct pattern");
|
||||
check_field(pattern.span, id, field.ident);
|
||||
self.check_field(pattern.span, id, field.ident);
|
||||
}
|
||||
}
|
||||
}
|
||||
ty_enum(enum_id, _) => {
|
||||
if enum_id.crate != LOCAL_CRATE ||
|
||||
!privileged_items.iter().any(|x| x == &enum_id.node) {
|
||||
match tcx.def_map.find(&pattern.id) {
|
||||
!self.privileged_items.iter().any(|x| x == &enum_id.node) {
|
||||
match self.tcx.def_map.find(&pattern.id) {
|
||||
Some(&def_variant(_, variant_id)) => {
|
||||
for field in fields.iter() {
|
||||
debug!("(privacy checking) \
|
||||
checking field in \
|
||||
struct variant pattern");
|
||||
check_field(pattern.span, variant_id, field.ident);
|
||||
self.check_field(pattern.span, variant_id, field.ident);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
tcx.sess.span_bug(pattern.span,
|
||||
self.tcx.sess.span_bug(pattern.span,
|
||||
"resolve didn't \
|
||||
map enum struct \
|
||||
pattern to a \
|
||||
@ -526,7 +530,7 @@ pub fn check_crate<'mm>(tcx: ty::ctxt,
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
tcx.sess.span_bug(pattern.span,
|
||||
self.tcx.sess.span_bug(pattern.span,
|
||||
"struct pattern didn't have \
|
||||
struct type?!");
|
||||
}
|
||||
@ -535,11 +539,20 @@ pub fn check_crate<'mm>(tcx: ty::ctxt,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
oldvisit::visit_pat(pattern, (method_map, visitor));
|
||||
},
|
||||
.. *oldvisit::default_visitor()
|
||||
});
|
||||
oldvisit::visit_crate(crate, (method_map, visitor));
|
||||
visit::walk_pat(self, pattern, method_map);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_crate<'mm>(tcx: ty::ctxt,
|
||||
method_map: &'mm method_map,
|
||||
crate: &ast::Crate) {
|
||||
let privileged_items = @mut ~[];
|
||||
|
||||
let mut visitor = PrivacyVisitor {
|
||||
tcx: tcx,
|
||||
privileged_items: privileged_items,
|
||||
};
|
||||
visit::walk_crate(&mut visitor, crate, method_map);
|
||||
}
|
||||
|
||||
/// Validates all of the visibility qualifers placed on the item given. This
|
||||
|
@ -24,8 +24,8 @@ use syntax::ast_map;
|
||||
use syntax::ast_util::def_id_of_def;
|
||||
use syntax::attr;
|
||||
use syntax::parse::token;
|
||||
use syntax::oldvisit::Visitor;
|
||||
use syntax::oldvisit;
|
||||
use syntax::visit::Visitor;
|
||||
use syntax::visit;
|
||||
|
||||
// Returns true if the given set of attributes contains the `#[inline]`
|
||||
// attribute.
|
||||
@ -94,40 +94,29 @@ struct ReachableContext {
|
||||
worklist: @mut ~[NodeId],
|
||||
}
|
||||
|
||||
impl ReachableContext {
|
||||
// Creates a new reachability computation context.
|
||||
fn new(tcx: ty::ctxt, method_map: typeck::method_map)
|
||||
-> ReachableContext {
|
||||
ReachableContext {
|
||||
tcx: tcx,
|
||||
method_map: method_map,
|
||||
reachable_symbols: @mut HashSet::new(),
|
||||
worklist: @mut ~[],
|
||||
}
|
||||
}
|
||||
struct ReachableVisitor {
|
||||
reachable_symbols: @mut HashSet<NodeId>,
|
||||
worklist: @mut ~[NodeId],
|
||||
}
|
||||
|
||||
impl Visitor<PrivacyContext> for ReachableVisitor {
|
||||
|
||||
fn visit_item(&mut self, item:@item, privacy_context:PrivacyContext) {
|
||||
|
||||
// Step 1: Mark all public symbols, and add all public symbols that might
|
||||
// be inlined to a worklist.
|
||||
fn mark_public_symbols(&self, crate: @Crate) {
|
||||
let reachable_symbols = self.reachable_symbols;
|
||||
let worklist = self.worklist;
|
||||
let visitor = oldvisit::mk_vt(@Visitor {
|
||||
visit_item: |item, (privacy_context, visitor):
|
||||
(PrivacyContext, oldvisit::vt<PrivacyContext>)| {
|
||||
match item.node {
|
||||
item_fn(*) => {
|
||||
if privacy_context == PublicContext {
|
||||
reachable_symbols.insert(item.id);
|
||||
self.reachable_symbols.insert(item.id);
|
||||
}
|
||||
if item_might_be_inlined(item) {
|
||||
worklist.push(item.id)
|
||||
self.worklist.push(item.id)
|
||||
}
|
||||
}
|
||||
item_struct(ref struct_def, _) => {
|
||||
match struct_def.ctor_id {
|
||||
Some(ctor_id) if
|
||||
privacy_context == PublicContext => {
|
||||
reachable_symbols.insert(ctor_id);
|
||||
self.reachable_symbols.insert(ctor_id);
|
||||
}
|
||||
Some(_) | None => {}
|
||||
}
|
||||
@ -135,7 +124,7 @@ impl ReachableContext {
|
||||
item_enum(ref enum_def, _) => {
|
||||
if privacy_context == PublicContext {
|
||||
for variant in enum_def.variants.iter() {
|
||||
reachable_symbols.insert(variant.node.id);
|
||||
self.reachable_symbols.insert(variant.node.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -155,7 +144,7 @@ impl ReachableContext {
|
||||
// Mark all public methods as reachable.
|
||||
for &method in methods.iter() {
|
||||
if should_be_considered_public(method) {
|
||||
reachable_symbols.insert(method.id);
|
||||
self.reachable_symbols.insert(method.id);
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,7 +153,7 @@ impl ReachableContext {
|
||||
// symbols to the worklist.
|
||||
for &method in methods.iter() {
|
||||
if should_be_considered_public(method) {
|
||||
worklist.push(method.id)
|
||||
self.worklist.push(method.id)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -176,7 +165,7 @@ impl ReachableContext {
|
||||
if generics_require_inlining(generics) ||
|
||||
attributes_specify_inlining(*attrs) ||
|
||||
should_be_considered_public(*method) {
|
||||
worklist.push(method.id)
|
||||
self.worklist.push(method.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -187,8 +176,8 @@ impl ReachableContext {
|
||||
for trait_method in trait_methods.iter() {
|
||||
match *trait_method {
|
||||
provided(method) => {
|
||||
reachable_symbols.insert(method.id);
|
||||
worklist.push(method.id)
|
||||
self.reachable_symbols.insert(method.id);
|
||||
self.worklist.push(method.id)
|
||||
}
|
||||
required(_) => {}
|
||||
}
|
||||
@ -199,15 +188,97 @@ impl ReachableContext {
|
||||
}
|
||||
|
||||
if item.vis == public && privacy_context == PublicContext {
|
||||
oldvisit::visit_item(item, (PublicContext, visitor))
|
||||
visit::walk_item(self, item, PublicContext)
|
||||
} else {
|
||||
oldvisit::visit_item(item, (PrivateContext, visitor))
|
||||
visit::walk_item(self, item, PrivateContext)
|
||||
}
|
||||
},
|
||||
.. *oldvisit::default_visitor()
|
||||
});
|
||||
}
|
||||
|
||||
oldvisit::visit_crate(crate, (PublicContext, visitor))
|
||||
}
|
||||
|
||||
struct MarkSymbolVisitor {
|
||||
worklist: @mut ~[NodeId],
|
||||
method_map: typeck::method_map,
|
||||
tcx: ty::ctxt,
|
||||
reachable_symbols: @mut HashSet<NodeId>,
|
||||
}
|
||||
|
||||
impl Visitor<()> for MarkSymbolVisitor {
|
||||
|
||||
fn visit_expr(&mut self, expr:@expr, _:()) {
|
||||
|
||||
match expr.node {
|
||||
expr_path(_) => {
|
||||
let def = match self.tcx.def_map.find(&expr.id) {
|
||||
Some(&def) => def,
|
||||
None => {
|
||||
self.tcx.sess.span_bug(expr.span,
|
||||
"def ID not in def map?!")
|
||||
}
|
||||
};
|
||||
|
||||
let def_id = def_id_of_def(def);
|
||||
if ReachableContext::
|
||||
def_id_represents_local_inlined_item(self.tcx,
|
||||
def_id) {
|
||||
self.worklist.push(def_id.node)
|
||||
}
|
||||
self.reachable_symbols.insert(def_id.node);
|
||||
}
|
||||
expr_method_call(*) => {
|
||||
match self.method_map.find(&expr.id) {
|
||||
Some(&typeck::method_map_entry {
|
||||
origin: typeck::method_static(def_id),
|
||||
_
|
||||
}) => {
|
||||
if ReachableContext::
|
||||
def_id_represents_local_inlined_item(
|
||||
self.tcx,
|
||||
def_id) {
|
||||
self.worklist.push(def_id.node)
|
||||
}
|
||||
self.reachable_symbols.insert(def_id.node);
|
||||
}
|
||||
Some(_) => {}
|
||||
None => {
|
||||
self.tcx.sess.span_bug(expr.span,
|
||||
"method call expression \
|
||||
not in method map?!")
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
visit::walk_expr(self, expr, ())
|
||||
}
|
||||
}
|
||||
|
||||
impl ReachableContext {
|
||||
// Creates a new reachability computation context.
|
||||
fn new(tcx: ty::ctxt, method_map: typeck::method_map)
|
||||
-> ReachableContext {
|
||||
ReachableContext {
|
||||
tcx: tcx,
|
||||
method_map: method_map,
|
||||
reachable_symbols: @mut HashSet::new(),
|
||||
worklist: @mut ~[],
|
||||
}
|
||||
}
|
||||
|
||||
// Step 1: Mark all public symbols, and add all public symbols that might
|
||||
// be inlined to a worklist.
|
||||
fn mark_public_symbols(&self, crate: @Crate) {
|
||||
let reachable_symbols = self.reachable_symbols;
|
||||
let worklist = self.worklist;
|
||||
|
||||
let mut visitor = ReachableVisitor {
|
||||
reachable_symbols: reachable_symbols,
|
||||
worklist: worklist,
|
||||
};
|
||||
|
||||
|
||||
visit::walk_crate(&mut visitor, crate, PublicContext);
|
||||
}
|
||||
|
||||
// Returns true if the given def ID represents a local item that is
|
||||
@ -269,63 +340,21 @@ impl ReachableContext {
|
||||
}
|
||||
|
||||
// Helper function to set up a visitor for `propagate()` below.
|
||||
fn init_visitor(&self) -> oldvisit::vt<()> {
|
||||
fn init_visitor(&self) -> MarkSymbolVisitor {
|
||||
let (worklist, method_map) = (self.worklist, self.method_map);
|
||||
let (tcx, reachable_symbols) = (self.tcx, self.reachable_symbols);
|
||||
oldvisit::mk_vt(@oldvisit::Visitor {
|
||||
visit_expr: |expr, (_, visitor)| {
|
||||
match expr.node {
|
||||
expr_path(_) => {
|
||||
let def = match tcx.def_map.find(&expr.id) {
|
||||
Some(&def) => def,
|
||||
None => {
|
||||
tcx.sess.span_bug(expr.span,
|
||||
"def ID not in def map?!")
|
||||
}
|
||||
};
|
||||
|
||||
let def_id = def_id_of_def(def);
|
||||
if ReachableContext::
|
||||
def_id_represents_local_inlined_item(tcx,
|
||||
def_id) {
|
||||
worklist.push(def_id.node)
|
||||
}
|
||||
reachable_symbols.insert(def_id.node);
|
||||
}
|
||||
expr_method_call(*) => {
|
||||
match method_map.find(&expr.id) {
|
||||
Some(&typeck::method_map_entry {
|
||||
origin: typeck::method_static(def_id),
|
||||
_
|
||||
}) => {
|
||||
if ReachableContext::
|
||||
def_id_represents_local_inlined_item(
|
||||
tcx,
|
||||
def_id) {
|
||||
worklist.push(def_id.node)
|
||||
}
|
||||
reachable_symbols.insert(def_id.node);
|
||||
}
|
||||
Some(_) => {}
|
||||
None => {
|
||||
tcx.sess.span_bug(expr.span,
|
||||
"method call expression \
|
||||
not in method map?!")
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
oldvisit::visit_expr(expr, ((), visitor))
|
||||
},
|
||||
..*oldvisit::default_visitor()
|
||||
})
|
||||
MarkSymbolVisitor {
|
||||
worklist: worklist,
|
||||
method_map: method_map,
|
||||
tcx: tcx,
|
||||
reachable_symbols: reachable_symbols,
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: Mark all symbols that the symbols on the worklist touch.
|
||||
fn propagate(&self) {
|
||||
let visitor = self.init_visitor();
|
||||
let mut visitor = self.init_visitor();
|
||||
let mut scanned = HashSet::new();
|
||||
while self.worklist.len() > 0 {
|
||||
let search_item = self.worklist.pop();
|
||||
@ -342,7 +371,7 @@ impl ReachableContext {
|
||||
Some(&ast_map::node_item(item, _)) => {
|
||||
match item.node {
|
||||
item_fn(_, _, _, _, ref search_block) => {
|
||||
oldvisit::visit_block(search_block, ((), visitor))
|
||||
visit::walk_block(&mut visitor, search_block, ())
|
||||
}
|
||||
_ => {
|
||||
self.tcx.sess.span_bug(item.span,
|
||||
@ -359,12 +388,12 @@ impl ReachableContext {
|
||||
worklist?!")
|
||||
}
|
||||
provided(ref method) => {
|
||||
oldvisit::visit_block(&method.body, ((), visitor))
|
||||
visit::walk_block(&mut visitor, &method.body, ())
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(&ast_map::node_method(ref method, _, _)) => {
|
||||
oldvisit::visit_block(&method.body, ((), visitor))
|
||||
visit::walk_block(&mut visitor, &method.body, ())
|
||||
}
|
||||
Some(_) => {
|
||||
let ident_interner = token::get_ident_interner();
|
||||
|
@ -34,7 +34,10 @@ use syntax::codemap::span;
|
||||
use syntax::print::pprust;
|
||||
use syntax::parse::token;
|
||||
use syntax::parse::token::special_idents;
|
||||
use syntax::{ast, oldvisit};
|
||||
use syntax::{ast, visit};
|
||||
use syntax::visit::{Visitor,fn_kind};
|
||||
use syntax::ast::{Block,item,fn_decl,NodeId,arm,pat,stmt,expr,Local};
|
||||
use syntax::ast::{Ty,TypeMethod,struct_field};
|
||||
|
||||
/**
|
||||
The region maps encode information about region relationships.
|
||||
@ -323,8 +326,9 @@ fn parent_to_expr(cx: Context, child_id: ast::NodeId, sp: span) {
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_block(blk: &ast::Block,
|
||||
(cx, visitor): (Context, oldvisit::vt<Context>)) {
|
||||
fn resolve_block(visitor: &mut RegionResolutionVisitor,
|
||||
blk: &ast::Block,
|
||||
cx: Context) {
|
||||
// Record the parent of this block.
|
||||
parent_to_expr(cx, blk.id, blk.span);
|
||||
|
||||
@ -332,39 +336,43 @@ fn resolve_block(blk: &ast::Block,
|
||||
let new_cx = Context {var_parent: Some(blk.id),
|
||||
parent: Some(blk.id),
|
||||
..cx};
|
||||
oldvisit::visit_block(blk, (new_cx, visitor));
|
||||
visit::walk_block(visitor, blk, new_cx);
|
||||
}
|
||||
|
||||
fn resolve_arm(arm: &ast::arm,
|
||||
(cx, visitor): (Context, oldvisit::vt<Context>)) {
|
||||
oldvisit::visit_arm(arm, (cx, visitor));
|
||||
fn resolve_arm(visitor: &mut RegionResolutionVisitor,
|
||||
arm: &ast::arm,
|
||||
cx: Context) {
|
||||
visit::walk_arm(visitor, arm, cx);
|
||||
}
|
||||
|
||||
fn resolve_pat(pat: @ast::pat,
|
||||
(cx, visitor): (Context, oldvisit::vt<Context>)) {
|
||||
fn resolve_pat(visitor: &mut RegionResolutionVisitor,
|
||||
pat: @ast::pat,
|
||||
cx: Context) {
|
||||
assert_eq!(cx.var_parent, cx.parent);
|
||||
parent_to_expr(cx, pat.id, pat.span);
|
||||
oldvisit::visit_pat(pat, (cx, visitor));
|
||||
visit::walk_pat(visitor, pat, cx);
|
||||
}
|
||||
|
||||
fn resolve_stmt(stmt: @ast::stmt,
|
||||
(cx, visitor): (Context, oldvisit::vt<Context>)) {
|
||||
fn resolve_stmt(visitor: &mut RegionResolutionVisitor,
|
||||
stmt: @ast::stmt,
|
||||
cx: Context) {
|
||||
match stmt.node {
|
||||
ast::stmt_decl(*) => {
|
||||
oldvisit::visit_stmt(stmt, (cx, visitor));
|
||||
visit::walk_stmt(visitor, stmt, cx);
|
||||
}
|
||||
ast::stmt_expr(_, stmt_id) |
|
||||
ast::stmt_semi(_, stmt_id) => {
|
||||
parent_to_expr(cx, stmt_id, stmt.span);
|
||||
let expr_cx = Context {parent: Some(stmt_id), ..cx};
|
||||
oldvisit::visit_stmt(stmt, (expr_cx, visitor));
|
||||
visit::walk_stmt(visitor, stmt, expr_cx);
|
||||
}
|
||||
ast::stmt_mac(*) => cx.sess.bug("unexpanded macro")
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_expr(expr: @ast::expr,
|
||||
(cx, visitor): (Context, oldvisit::vt<Context>)) {
|
||||
fn resolve_expr(visitor: &mut RegionResolutionVisitor,
|
||||
expr: @ast::expr,
|
||||
cx: Context) {
|
||||
parent_to_expr(cx, expr.id, expr.span);
|
||||
|
||||
let mut new_cx = cx;
|
||||
@ -400,30 +408,32 @@ fn resolve_expr(expr: @ast::expr,
|
||||
};
|
||||
|
||||
|
||||
oldvisit::visit_expr(expr, (new_cx, visitor));
|
||||
visit::walk_expr(visitor, expr, new_cx);
|
||||
}
|
||||
|
||||
fn resolve_local(local: @ast::Local,
|
||||
(cx, visitor): (Context, oldvisit::vt<Context>)) {
|
||||
fn resolve_local(visitor: &mut RegionResolutionVisitor,
|
||||
local: @ast::Local,
|
||||
cx: Context) {
|
||||
assert_eq!(cx.var_parent, cx.parent);
|
||||
parent_to_expr(cx, local.id, local.span);
|
||||
oldvisit::visit_local(local, (cx, visitor));
|
||||
visit::walk_local(visitor, local, cx);
|
||||
}
|
||||
|
||||
fn resolve_item(item: @ast::item,
|
||||
(cx, visitor): (Context, oldvisit::vt<Context>)) {
|
||||
fn resolve_item(visitor: &mut RegionResolutionVisitor,
|
||||
item: @ast::item,
|
||||
cx: Context) {
|
||||
// Items create a new outer block scope as far as we're concerned.
|
||||
let new_cx = Context {var_parent: None, parent: None, ..cx};
|
||||
oldvisit::visit_item(item, (new_cx, visitor));
|
||||
visit::walk_item(visitor, item, new_cx);
|
||||
}
|
||||
|
||||
fn resolve_fn(fk: &oldvisit::fn_kind,
|
||||
fn resolve_fn(visitor: &mut RegionResolutionVisitor,
|
||||
fk: &visit::fn_kind,
|
||||
decl: &ast::fn_decl,
|
||||
body: &ast::Block,
|
||||
sp: span,
|
||||
id: ast::NodeId,
|
||||
(cx, visitor): (Context,
|
||||
oldvisit::vt<Context>)) {
|
||||
cx: Context) {
|
||||
debug!("region::resolve_fn(id=%?, \
|
||||
span=%?, \
|
||||
body.id=%?, \
|
||||
@ -438,26 +448,58 @@ fn resolve_fn(fk: &oldvisit::fn_kind,
|
||||
var_parent: Some(body.id),
|
||||
..cx};
|
||||
match *fk {
|
||||
oldvisit::fk_method(_, _, method) => {
|
||||
visit::fk_method(_, _, method) => {
|
||||
cx.region_maps.record_parent(method.self_id, body.id);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
oldvisit::visit_fn_decl(decl, (decl_cx, visitor));
|
||||
visit::walk_fn_decl(visitor, decl, decl_cx);
|
||||
|
||||
// The body of the fn itself is either a root scope (top-level fn)
|
||||
// or it continues with the inherited scope (closures).
|
||||
let body_cx = match *fk {
|
||||
oldvisit::fk_item_fn(*) |
|
||||
oldvisit::fk_method(*) => {
|
||||
visit::fk_item_fn(*) |
|
||||
visit::fk_method(*) => {
|
||||
Context {parent: None, var_parent: None, ..cx}
|
||||
}
|
||||
oldvisit::fk_anon(*) |
|
||||
oldvisit::fk_fn_block(*) => {
|
||||
visit::fk_anon(*) |
|
||||
visit::fk_fn_block(*) => {
|
||||
cx
|
||||
}
|
||||
};
|
||||
(visitor.visit_block)(body, (body_cx, visitor));
|
||||
visitor.visit_block(body, body_cx);
|
||||
}
|
||||
|
||||
struct RegionResolutionVisitor;
|
||||
|
||||
impl Visitor<Context> for RegionResolutionVisitor {
|
||||
|
||||
fn visit_block(&mut self, b:&Block, cx:Context) {
|
||||
resolve_block(self, b, cx);
|
||||
}
|
||||
|
||||
fn visit_item(&mut self, i:@item, cx:Context) {
|
||||
resolve_item(self, i, cx);
|
||||
}
|
||||
|
||||
fn visit_fn(&mut self, fk:&fn_kind, fd:&fn_decl, b:&Block, s:span, n:NodeId, cx:Context) {
|
||||
resolve_fn(self, fk, fd, b, s, n, cx);
|
||||
}
|
||||
fn visit_arm(&mut self, a:&arm, cx:Context) {
|
||||
resolve_arm(self, a, cx);
|
||||
}
|
||||
fn visit_pat(&mut self, p:@pat, cx:Context) {
|
||||
resolve_pat(self, p, cx);
|
||||
}
|
||||
fn visit_stmt(&mut self, s:@stmt, cx:Context) {
|
||||
resolve_stmt(self, s, cx);
|
||||
}
|
||||
fn visit_expr(&mut self, ex:@expr, cx:Context) {
|
||||
resolve_expr(self, ex, cx);
|
||||
}
|
||||
fn visit_local(&mut self, l:@Local, cx:Context) {
|
||||
resolve_local(self, l, cx);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_crate(sess: Session,
|
||||
@ -474,18 +516,8 @@ pub fn resolve_crate(sess: Session,
|
||||
region_maps: region_maps,
|
||||
parent: None,
|
||||
var_parent: None};
|
||||
let visitor = oldvisit::mk_vt(@oldvisit::Visitor {
|
||||
visit_block: resolve_block,
|
||||
visit_item: resolve_item,
|
||||
visit_fn: resolve_fn,
|
||||
visit_arm: resolve_arm,
|
||||
visit_pat: resolve_pat,
|
||||
visit_stmt: resolve_stmt,
|
||||
visit_expr: resolve_expr,
|
||||
visit_local: resolve_local,
|
||||
.. *oldvisit::default_visitor()
|
||||
});
|
||||
oldvisit::visit_crate(crate, (cx, visitor));
|
||||
let mut visitor = RegionResolutionVisitor;
|
||||
visit::walk_crate(&mut visitor, crate, cx);
|
||||
return region_maps;
|
||||
}
|
||||
|
||||
@ -700,46 +732,45 @@ impl DetermineRpCtxt {
|
||||
}
|
||||
}
|
||||
|
||||
fn determine_rp_in_item(item: @ast::item,
|
||||
(cx, visitor): (@mut DetermineRpCtxt,
|
||||
oldvisit::vt<@mut DetermineRpCtxt>)) {
|
||||
fn determine_rp_in_item(visitor: &mut DetermineRpVisitor,
|
||||
item: @ast::item,
|
||||
cx: @mut DetermineRpCtxt) {
|
||||
do cx.with(item.id, true) {
|
||||
oldvisit::visit_item(item, (cx, visitor));
|
||||
visit::walk_item(visitor, item, cx);
|
||||
}
|
||||
}
|
||||
|
||||
fn determine_rp_in_fn(fk: &oldvisit::fn_kind,
|
||||
fn determine_rp_in_fn(visitor: &mut DetermineRpVisitor,
|
||||
fk: &visit::fn_kind,
|
||||
decl: &ast::fn_decl,
|
||||
body: &ast::Block,
|
||||
_: span,
|
||||
_: ast::NodeId,
|
||||
(cx, visitor): (@mut DetermineRpCtxt,
|
||||
oldvisit::vt<@mut DetermineRpCtxt>)) {
|
||||
cx: @mut DetermineRpCtxt) {
|
||||
do cx.with(cx.item_id, false) {
|
||||
do cx.with_ambient_variance(rv_contravariant) {
|
||||
for a in decl.inputs.iter() {
|
||||
(visitor.visit_ty)(&a.ty, (cx, visitor));
|
||||
visitor.visit_ty(&a.ty, cx);
|
||||
}
|
||||
}
|
||||
(visitor.visit_ty)(&decl.output, (cx, visitor));
|
||||
let generics = oldvisit::generics_of_fn(fk);
|
||||
(visitor.visit_generics)(&generics, (cx, visitor));
|
||||
(visitor.visit_block)(body, (cx, visitor));
|
||||
visitor.visit_ty(&decl.output, cx);
|
||||
let generics = visit::generics_of_fn(fk);
|
||||
visitor.visit_generics(&generics, cx);
|
||||
visitor.visit_block(body, cx);
|
||||
}
|
||||
}
|
||||
|
||||
fn determine_rp_in_ty_method(ty_m: &ast::TypeMethod,
|
||||
(cx, visitor):
|
||||
(@mut DetermineRpCtxt,
|
||||
oldvisit::vt<@mut DetermineRpCtxt>)) {
|
||||
fn determine_rp_in_ty_method(visitor: &mut DetermineRpVisitor,
|
||||
ty_m: &ast::TypeMethod,
|
||||
cx: @mut DetermineRpCtxt) {
|
||||
do cx.with(cx.item_id, false) {
|
||||
oldvisit::visit_ty_method(ty_m, (cx, visitor));
|
||||
visit::walk_ty_method(visitor, ty_m, cx);
|
||||
}
|
||||
}
|
||||
|
||||
fn determine_rp_in_ty(ty: &ast::Ty,
|
||||
(cx, visitor): (@mut DetermineRpCtxt,
|
||||
oldvisit::vt<@mut DetermineRpCtxt>)) {
|
||||
fn determine_rp_in_ty(visitor: &mut DetermineRpVisitor,
|
||||
ty: &ast::Ty,
|
||||
cx: @mut DetermineRpCtxt) {
|
||||
// we are only interested in types that will require an item to
|
||||
// be region-parameterized. if cx.item_id is zero, then this type
|
||||
// is not a member of a type defn nor is it a constitutent of an
|
||||
@ -823,14 +854,14 @@ fn determine_rp_in_ty(ty: &ast::Ty,
|
||||
match ty.node {
|
||||
ast::ty_box(ref mt) | ast::ty_uniq(ref mt) | ast::ty_vec(ref mt) |
|
||||
ast::ty_rptr(_, ref mt) | ast::ty_ptr(ref mt) => {
|
||||
visit_mt(mt, (cx, visitor));
|
||||
visit_mt(visitor, mt, cx);
|
||||
}
|
||||
|
||||
ast::ty_path(ref path, _, _) => {
|
||||
// type parameters are---for now, anyway---always invariant
|
||||
do cx.with_ambient_variance(rv_invariant) {
|
||||
for tp in path.types.iter() {
|
||||
(visitor.visit_ty)(tp, (cx, visitor));
|
||||
visitor.visit_ty(tp, cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -843,37 +874,59 @@ fn determine_rp_in_ty(ty: &ast::Ty,
|
||||
// parameters are contravariant
|
||||
do cx.with_ambient_variance(rv_contravariant) {
|
||||
for a in decl.inputs.iter() {
|
||||
(visitor.visit_ty)(&a.ty, (cx, visitor));
|
||||
visitor.visit_ty(&a.ty, cx);
|
||||
}
|
||||
}
|
||||
(visitor.visit_ty)(&decl.output, (cx, visitor));
|
||||
visitor.visit_ty(&decl.output, cx);
|
||||
}
|
||||
}
|
||||
|
||||
_ => {
|
||||
oldvisit::visit_ty(ty, (cx, visitor));
|
||||
visit::walk_ty(visitor, ty, cx);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mt(mt: &ast::mt,
|
||||
(cx, visitor): (@mut DetermineRpCtxt,
|
||||
oldvisit::vt<@mut DetermineRpCtxt>)) {
|
||||
fn visit_mt(visitor: &mut DetermineRpVisitor,
|
||||
mt: &ast::mt,
|
||||
cx: @mut DetermineRpCtxt) {
|
||||
// mutability is invariant
|
||||
if mt.mutbl == ast::m_mutbl {
|
||||
do cx.with_ambient_variance(rv_invariant) {
|
||||
(visitor.visit_ty)(mt.ty, (cx, visitor));
|
||||
visitor.visit_ty(mt.ty, cx);
|
||||
}
|
||||
} else {
|
||||
(visitor.visit_ty)(mt.ty, (cx, visitor));
|
||||
visitor.visit_ty(mt.ty, cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn determine_rp_in_struct_field(
|
||||
cm: @ast::struct_field,
|
||||
(cx, visitor): (@mut DetermineRpCtxt,
|
||||
oldvisit::vt<@mut DetermineRpCtxt>)) {
|
||||
oldvisit::visit_struct_field(cm, (cx, visitor));
|
||||
fn determine_rp_in_struct_field(visitor: &mut DetermineRpVisitor,
|
||||
cm: @ast::struct_field,
|
||||
cx: @mut DetermineRpCtxt) {
|
||||
visit::walk_struct_field(visitor, cm, cx);
|
||||
}
|
||||
|
||||
struct DetermineRpVisitor;
|
||||
|
||||
impl Visitor<@mut DetermineRpCtxt> for DetermineRpVisitor {
|
||||
|
||||
fn visit_fn(&mut self, fk:&fn_kind, fd:&fn_decl,
|
||||
b:&Block, s:span, n:NodeId, e:@mut DetermineRpCtxt) {
|
||||
determine_rp_in_fn(self, fk, fd, b, s, n, e);
|
||||
}
|
||||
fn visit_item(&mut self, i:@item, e:@mut DetermineRpCtxt) {
|
||||
determine_rp_in_item(self, i, e);
|
||||
}
|
||||
fn visit_ty(&mut self, t:&Ty, e:@mut DetermineRpCtxt) {
|
||||
determine_rp_in_ty(self, t, e);
|
||||
}
|
||||
fn visit_ty_method(&mut self, t:&TypeMethod, e:@mut DetermineRpCtxt) {
|
||||
determine_rp_in_ty_method(self, t, e);
|
||||
}
|
||||
fn visit_struct_field(&mut self, s:@struct_field, e:@mut DetermineRpCtxt) {
|
||||
determine_rp_in_struct_field(self, s, e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn determine_rp_in_crate(sess: Session,
|
||||
@ -894,15 +947,8 @@ pub fn determine_rp_in_crate(sess: Session,
|
||||
};
|
||||
|
||||
// Gather up the base set, worklist and dep_map
|
||||
let visitor = oldvisit::mk_vt(@oldvisit::Visitor {
|
||||
visit_fn: determine_rp_in_fn,
|
||||
visit_item: determine_rp_in_item,
|
||||
visit_ty: determine_rp_in_ty,
|
||||
visit_ty_method: determine_rp_in_ty_method,
|
||||
visit_struct_field: determine_rp_in_struct_field,
|
||||
.. *oldvisit::default_visitor()
|
||||
});
|
||||
oldvisit::visit_crate(crate, (cx, visitor));
|
||||
let mut visitor = DetermineRpVisitor;
|
||||
visit::walk_crate(&mut visitor, crate, cx);
|
||||
|
||||
// Propagate indirect dependencies
|
||||
//
|
||||
|
Loading…
Reference in New Issue
Block a user