librustc: Enforce cross-crate method privacy
This commit is contained in:
parent
09a2b4e599
commit
2859c1ac6d
9 changed files with 112 additions and 27 deletions
|
@ -2304,11 +2304,10 @@ mod farm {
|
||||||
farmer: Human
|
farmer: Human
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note - visibility modifiers on impls currently have no effect
|
|
||||||
impl Farm {
|
impl Farm {
|
||||||
priv fn feed_chickens(&self) { ... }
|
priv fn feed_chickens(&self) { ... }
|
||||||
priv fn feed_cows(&self) { ... }
|
priv fn feed_cows(&self) { ... }
|
||||||
fn add_chicken(&self, c: Chicken) { ... }
|
pub fn add_chicken(&self, c: Chicken) { ... }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn feed_animals(farm: &Farm) {
|
pub fn feed_animals(farm: &Farm) {
|
||||||
|
|
|
@ -155,6 +155,7 @@ pub const tag_lang_items_item_node_id: uint = 0x75;
|
||||||
|
|
||||||
pub const tag_item_unnamed_field: uint = 0x76;
|
pub const tag_item_unnamed_field: uint = 0x76;
|
||||||
pub const tag_items_data_item_struct_ctor: uint = 0x77;
|
pub const tag_items_data_item_struct_ctor: uint = 0x77;
|
||||||
|
pub const tag_items_data_item_visibility: uint = 0x78;
|
||||||
|
|
||||||
pub struct LinkMeta {
|
pub struct LinkMeta {
|
||||||
name: @str,
|
name: @str,
|
||||||
|
|
|
@ -234,6 +234,14 @@ pub fn struct_dtor(cstore: @mut cstore::CStore, def: ast::def_id)
|
||||||
let cdata = cstore::get_crate_data(cstore, def.crate);
|
let cdata = cstore::get_crate_data(cstore, def.crate);
|
||||||
decoder::struct_dtor(cdata, def.node)
|
decoder::struct_dtor(cdata, def.node)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_method_visibility(cstore: @mut cstore::CStore,
|
||||||
|
def_id: ast::def_id)
|
||||||
|
-> ast::visibility {
|
||||||
|
let cdata = cstore::get_crate_data(cstore, def_id.crate);
|
||||||
|
decoder::get_method_visibility(cdata, def_id.node)
|
||||||
|
}
|
||||||
|
|
||||||
// Local Variables:
|
// Local Variables:
|
||||||
// mode: rust
|
// mode: rust
|
||||||
// fill-column: 78;
|
// fill-column: 78;
|
||||||
|
|
|
@ -151,6 +151,16 @@ fn item_family(item: ebml::Doc) -> Family {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn item_visibility(item: ebml::Doc) -> ast::visibility {
|
||||||
|
let visibility = reader::get_doc(item, tag_items_data_item_visibility);
|
||||||
|
match reader::doc_as_u8(visibility) as char {
|
||||||
|
'y' => ast::public,
|
||||||
|
'n' => ast::private,
|
||||||
|
'i' => ast::inherited,
|
||||||
|
_ => fail!(~"unknown visibility character"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn item_method_sort(item: ebml::Doc) -> char {
|
fn item_method_sort(item: ebml::Doc) -> char {
|
||||||
for reader::tagged_docs(item, tag_item_trait_method_sort) |doc| {
|
for reader::tagged_docs(item, tag_item_trait_method_sort) |doc| {
|
||||||
return str::from_bytes(reader::doc_data(doc))[0] as char;
|
return str::from_bytes(reader::doc_data(doc))[0] as char;
|
||||||
|
@ -860,7 +870,7 @@ pub fn get_item_attrs(cdata: cmd,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pure fn family_to_visibility(family: Family) -> ast::visibility {
|
pure fn struct_field_family_to_visibility(family: Family) -> ast::visibility {
|
||||||
match family {
|
match family {
|
||||||
PublicField => ast::public,
|
PublicField => ast::public,
|
||||||
PrivateField => ast::private,
|
PrivateField => ast::private,
|
||||||
|
@ -883,7 +893,7 @@ pub fn get_struct_fields(intr: @ident_interner, cdata: cmd, id: ast::node_id)
|
||||||
result.push(ty::field_ty {
|
result.push(ty::field_ty {
|
||||||
ident: name,
|
ident: name,
|
||||||
id: did, vis:
|
id: did, vis:
|
||||||
family_to_visibility(f),
|
struct_field_family_to_visibility(f),
|
||||||
mutability: mt,
|
mutability: mt,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -900,6 +910,11 @@ pub fn get_struct_fields(intr: @ident_interner, cdata: cmd, id: ast::node_id)
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_method_visibility(cdata: cmd, id: ast::node_id)
|
||||||
|
-> ast::visibility {
|
||||||
|
item_visibility(lookup_item(id, cdata.data))
|
||||||
|
}
|
||||||
|
|
||||||
fn family_has_type_params(fam: Family) -> bool {
|
fn family_has_type_params(fam: Family) -> bool {
|
||||||
match fam {
|
match fam {
|
||||||
Const | ForeignType | Mod | ForeignMod | PublicField | PrivateField
|
Const | ForeignType | Mod | ForeignMod | PublicField | PrivateField
|
||||||
|
|
|
@ -383,7 +383,8 @@ fn encode_info_for_mod(ecx: @EncodeContext, ebml_w: writer::Encoder,
|
||||||
ebml_w.end_tag();
|
ebml_w.end_tag();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encode_visibility(ebml_w: writer::Encoder, visibility: visibility) {
|
fn encode_struct_field_family(ebml_w: writer::Encoder,
|
||||||
|
visibility: visibility) {
|
||||||
encode_family(ebml_w, match visibility {
|
encode_family(ebml_w, match visibility {
|
||||||
public => 'g',
|
public => 'g',
|
||||||
private => 'j',
|
private => 'j',
|
||||||
|
@ -391,6 +392,17 @@ fn encode_visibility(ebml_w: writer::Encoder, visibility: visibility) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn encode_visibility(ebml_w: writer::Encoder, visibility: visibility) {
|
||||||
|
ebml_w.start_tag(tag_items_data_item_visibility);
|
||||||
|
let ch = match visibility {
|
||||||
|
public => 'y',
|
||||||
|
private => 'n',
|
||||||
|
inherited => 'i',
|
||||||
|
};
|
||||||
|
ebml_w.wr_str(str::from_char(ch));
|
||||||
|
ebml_w.end_tag();
|
||||||
|
}
|
||||||
|
|
||||||
fn encode_self_type(ebml_w: writer::Encoder, self_type: ast::self_ty_) {
|
fn encode_self_type(ebml_w: writer::Encoder, self_type: ast::self_ty_) {
|
||||||
ebml_w.start_tag(tag_item_trait_method_self_ty);
|
ebml_w.start_tag(tag_item_trait_method_self_ty);
|
||||||
|
|
||||||
|
@ -456,7 +468,7 @@ fn encode_info_for_struct(ecx: @EncodeContext, ebml_w: writer::Encoder,
|
||||||
ebml_w.start_tag(tag_items_data_item);
|
ebml_w.start_tag(tag_items_data_item);
|
||||||
debug!("encode_info_for_struct: doing %s %d",
|
debug!("encode_info_for_struct: doing %s %d",
|
||||||
*tcx.sess.str_of(nm), id);
|
*tcx.sess.str_of(nm), id);
|
||||||
encode_visibility(ebml_w, vis);
|
encode_struct_field_family(ebml_w, vis);
|
||||||
encode_name(ecx, ebml_w, nm);
|
encode_name(ecx, ebml_w, nm);
|
||||||
encode_path(ecx, ebml_w, path, ast_map::path_name(nm));
|
encode_path(ecx, ebml_w, path, ast_map::path_name(nm));
|
||||||
encode_type(ecx, ebml_w, node_id_to_type(tcx, id));
|
encode_type(ecx, ebml_w, node_id_to_type(tcx, id));
|
||||||
|
@ -525,6 +537,7 @@ fn encode_info_for_method(ecx: @EncodeContext,
|
||||||
should_inline: bool,
|
should_inline: bool,
|
||||||
parent_id: node_id,
|
parent_id: node_id,
|
||||||
m: @method,
|
m: @method,
|
||||||
|
parent_visibility: ast::visibility,
|
||||||
owner_generics: &ast::Generics,
|
owner_generics: &ast::Generics,
|
||||||
method_generics: &ast::Generics) {
|
method_generics: &ast::Generics) {
|
||||||
debug!("encode_info_for_method: %d %s %u %u", m.id,
|
debug!("encode_info_for_method: %d %s %u %u", m.id,
|
||||||
|
@ -533,6 +546,7 @@ fn encode_info_for_method(ecx: @EncodeContext,
|
||||||
method_generics.ty_params.len());
|
method_generics.ty_params.len());
|
||||||
ebml_w.start_tag(tag_items_data_item);
|
ebml_w.start_tag(tag_items_data_item);
|
||||||
encode_def_id(ebml_w, local_def(m.id));
|
encode_def_id(ebml_w, local_def(m.id));
|
||||||
|
|
||||||
match m.self_ty.node {
|
match m.self_ty.node {
|
||||||
ast::sty_static => {
|
ast::sty_static => {
|
||||||
encode_family(ebml_w, purity_static_method_family(m.purity));
|
encode_family(ebml_w, purity_static_method_family(m.purity));
|
||||||
|
@ -550,6 +564,14 @@ fn encode_info_for_method(ecx: @EncodeContext,
|
||||||
encode_name(ecx, ebml_w, m.ident);
|
encode_name(ecx, ebml_w, m.ident);
|
||||||
encode_path(ecx, ebml_w, impl_path, ast_map::path_name(m.ident));
|
encode_path(ecx, ebml_w, impl_path, ast_map::path_name(m.ident));
|
||||||
encode_self_type(ebml_w, m.self_ty.node);
|
encode_self_type(ebml_w, m.self_ty.node);
|
||||||
|
|
||||||
|
// Combine parent visibility and this visibility.
|
||||||
|
let visibility = match m.vis {
|
||||||
|
ast::inherited => parent_visibility,
|
||||||
|
vis => vis,
|
||||||
|
};
|
||||||
|
encode_visibility(ebml_w, visibility);
|
||||||
|
|
||||||
if len > 0u || should_inline {
|
if len > 0u || should_inline {
|
||||||
(ecx.encode_inlined_item)(
|
(ecx.encode_inlined_item)(
|
||||||
ecx, ebml_w, impl_path,
|
ecx, ebml_w, impl_path,
|
||||||
|
@ -568,6 +590,7 @@ fn purity_fn_family(p: purity) -> char {
|
||||||
extern_fn => 'e'
|
extern_fn => 'e'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn purity_static_method_family(p: purity) -> char {
|
fn purity_static_method_family(p: purity) -> char {
|
||||||
match p {
|
match p {
|
||||||
unsafe_fn => 'U',
|
unsafe_fn => 'U',
|
||||||
|
@ -757,7 +780,7 @@ fn encode_info_for_item(ecx: @EncodeContext, ebml_w: writer::Encoder,
|
||||||
match f.node.kind {
|
match f.node.kind {
|
||||||
named_field(ident, _, vis) => {
|
named_field(ident, _, vis) => {
|
||||||
ebml_w.start_tag(tag_item_field);
|
ebml_w.start_tag(tag_item_field);
|
||||||
encode_visibility(ebml_w, vis);
|
encode_struct_field_family(ebml_w, vis);
|
||||||
encode_name(ecx, ebml_w, ident);
|
encode_name(ecx, ebml_w, ident);
|
||||||
encode_def_id(ebml_w, local_def(f.node.id));
|
encode_def_id(ebml_w, local_def(f.node.id));
|
||||||
ebml_w.end_tag();
|
ebml_w.end_tag();
|
||||||
|
@ -808,12 +831,28 @@ fn encode_info_for_item(ecx: @EncodeContext, ebml_w: writer::Encoder,
|
||||||
let mut impl_path = vec::append(~[], path);
|
let mut impl_path = vec::append(~[], path);
|
||||||
impl_path += ~[ast_map::path_name(item.ident)];
|
impl_path += ~[ast_map::path_name(item.ident)];
|
||||||
|
|
||||||
|
// If there is a trait reference, treat the methods as always public.
|
||||||
|
// This is to work around some incorrect behavior in privacy checking:
|
||||||
|
// when the method belongs to a trait, it should acquire the privacy
|
||||||
|
// from the trait, not the impl. Forcing the visibility to be public
|
||||||
|
// makes things sorta work.
|
||||||
|
let parent_visibility = if opt_trait.is_some() {
|
||||||
|
ast::public
|
||||||
|
} else {
|
||||||
|
item.vis
|
||||||
|
};
|
||||||
|
|
||||||
for methods.each |m| {
|
for methods.each |m| {
|
||||||
index.push(entry {val: m.id, pos: ebml_w.writer.tell()});
|
index.push(entry {val: m.id, pos: ebml_w.writer.tell()});
|
||||||
encode_info_for_method(ecx, ebml_w, impl_path,
|
encode_info_for_method(ecx,
|
||||||
|
ebml_w,
|
||||||
|
impl_path,
|
||||||
should_inline(m.attrs),
|
should_inline(m.attrs),
|
||||||
item.id, *m,
|
item.id,
|
||||||
generics, &m.generics);
|
*m,
|
||||||
|
parent_visibility,
|
||||||
|
generics,
|
||||||
|
&m.generics);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
item_trait(ref generics, ref traits, ref ms) => {
|
item_trait(ref generics, ref traits, ref ms) => {
|
||||||
|
@ -902,9 +941,15 @@ fn encode_info_for_item(ecx: @EncodeContext, ebml_w: writer::Encoder,
|
||||||
// of provided methods. I am not sure why this is. -ndm
|
// of provided methods. I am not sure why this is. -ndm
|
||||||
let owner_generics = ast_util::empty_generics();
|
let owner_generics = ast_util::empty_generics();
|
||||||
|
|
||||||
encode_info_for_method(ecx, ebml_w, /*bad*/copy path,
|
encode_info_for_method(ecx,
|
||||||
true, item.id, *m,
|
ebml_w,
|
||||||
&owner_generics, &m.generics);
|
/*bad*/copy path,
|
||||||
|
true,
|
||||||
|
item.id,
|
||||||
|
*m,
|
||||||
|
item.vis,
|
||||||
|
&owner_generics,
|
||||||
|
&m.generics);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
item_mac(*) => fail!(~"item macros unimplemented")
|
item_mac(*) => fail!(~"item macros unimplemented")
|
||||||
|
|
|
@ -14,10 +14,11 @@
|
||||||
|
|
||||||
use core::prelude::*;
|
use core::prelude::*;
|
||||||
|
|
||||||
|
use metadata::csearch;
|
||||||
use middle::ty::{ty_struct, ty_enum};
|
use middle::ty::{ty_struct, ty_enum};
|
||||||
use middle::ty;
|
use middle::ty;
|
||||||
use middle::typeck::{method_map, method_origin, method_param, method_self,
|
use middle::typeck::{method_map, method_origin, method_param, method_self};
|
||||||
method_super};
|
use middle::typeck::{method_super};
|
||||||
use middle::typeck::{method_static, method_trait};
|
use middle::typeck::{method_static, method_trait};
|
||||||
|
|
||||||
use core::dvec::DVec;
|
use core::dvec::DVec;
|
||||||
|
@ -100,8 +101,10 @@ pub fn check_crate(tcx: ty::ctxt,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Checks that a private method is in scope.
|
// Checks that a private method is in scope.
|
||||||
let check_method: @fn(span: span, origin: &method_origin) =
|
let check_method: @fn(span: span,
|
||||||
|span, origin| {
|
origin: &method_origin,
|
||||||
|
ident: ast::ident) =
|
||||||
|
|span, origin, ident| {
|
||||||
match *origin {
|
match *origin {
|
||||||
method_static(method_id) => {
|
method_static(method_id) => {
|
||||||
if method_id.crate == local_crate {
|
if method_id.crate == local_crate {
|
||||||
|
@ -110,6 +113,8 @@ pub fn check_crate(tcx: ty::ctxt,
|
||||||
let mut is_private = false;
|
let mut is_private = false;
|
||||||
if method.vis == private {
|
if method.vis == private {
|
||||||
is_private = true;
|
is_private = true;
|
||||||
|
} else if method.vis == public {
|
||||||
|
is_private = false;
|
||||||
} else {
|
} else {
|
||||||
// Look up the enclosing impl.
|
// Look up the enclosing impl.
|
||||||
if impl_id.crate != local_crate {
|
if impl_id.crate != local_crate {
|
||||||
|
@ -121,7 +126,7 @@ pub fn check_crate(tcx: ty::ctxt,
|
||||||
match tcx.items.find(&impl_id.node) {
|
match tcx.items.find(&impl_id.node) {
|
||||||
Some(node_item(item, _)) => {
|
Some(node_item(item, _)) => {
|
||||||
match item.node {
|
match item.node {
|
||||||
item_impl(_, None, _, _)
|
item_impl(_, None, _, _)
|
||||||
if item.vis != public => {
|
if item.vis != public => {
|
||||||
is_private = true;
|
is_private = true;
|
||||||
}
|
}
|
||||||
|
@ -165,7 +170,15 @@ pub fn check_crate(tcx: ty::ctxt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// FIXME #4732: External crates.
|
let visibility =
|
||||||
|
csearch::get_method_visibility(tcx.sess.cstore,
|
||||||
|
method_id);
|
||||||
|
if visibility != public {
|
||||||
|
tcx.sess.span_err(span,
|
||||||
|
fmt!("method `%s` is private",
|
||||||
|
*tcx.sess.parse_sess.interner
|
||||||
|
.get(ident)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
method_param(method_param {
|
method_param(method_param {
|
||||||
|
@ -264,14 +277,16 @@ pub fn check_crate(tcx: ty::ctxt,
|
||||||
Some(ref entry) => {
|
Some(ref entry) => {
|
||||||
debug!("(privacy checking) checking \
|
debug!("(privacy checking) checking \
|
||||||
impl method");
|
impl method");
|
||||||
check_method(expr.span, &(*entry).origin);
|
check_method(expr.span,
|
||||||
|
&entry.origin,
|
||||||
|
ident);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
expr_method_call(base, _, _, _, _) => {
|
expr_method_call(base, ident, _, _, _) => {
|
||||||
// Ditto
|
// Ditto
|
||||||
match ty::get(ty::type_autoderef(tcx, ty::expr_ty(tcx,
|
match ty::get(ty::type_autoderef(tcx, ty::expr_ty(tcx,
|
||||||
base))).sty {
|
base))).sty {
|
||||||
|
@ -287,7 +302,9 @@ pub fn check_crate(tcx: ty::ctxt,
|
||||||
Some(ref entry) => {
|
Some(ref entry) => {
|
||||||
debug!("(privacy checking) checking \
|
debug!("(privacy checking) checking \
|
||||||
impl method");
|
impl method");
|
||||||
check_method(expr.span, &(*entry).origin);
|
check_method(expr.span,
|
||||||
|
&entry.origin,
|
||||||
|
ident);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,7 +134,9 @@ pub mod chained {
|
||||||
}
|
}
|
||||||
self.chains = new_chains;
|
self.chains = new_chains;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub impl<K:Eq + IterBytes + Hash,V> T<K, V> {
|
||||||
pure fn each_entry(blk: fn(@Entry<K,V>) -> bool) {
|
pure fn each_entry(blk: fn(@Entry<K,V>) -> bool) {
|
||||||
// n.b. we can't use vec::iter() here because self.chains
|
// n.b. we can't use vec::iter() here because self.chains
|
||||||
// is stored in a mutable location.
|
// is stored in a mutable location.
|
||||||
|
|
|
@ -10,12 +10,12 @@
|
||||||
|
|
||||||
pub mod kitties {
|
pub mod kitties {
|
||||||
pub struct cat {
|
pub struct cat {
|
||||||
priv mut meows : uint,
|
priv meows : uint,
|
||||||
how_hungry : int,
|
how_hungry : int,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub impl cat {
|
pub impl cat {
|
||||||
priv fn nap() { for uint::range(1, 10000u) |_i|{}}
|
priv fn nap(&self) { for uint::range(1, 10000u) |_i|{}}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cat(in_x : uint, in_y : int) -> cat {
|
pub fn cat(in_x : uint, in_y : int) -> cat {
|
||||||
|
|
|
@ -8,8 +8,6 @@
|
||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
// error-pattern:attempted access of field `nap` on type
|
|
||||||
// xfail-test Cross-crate impl method privacy doesn't work
|
|
||||||
// xfail-fast
|
// xfail-fast
|
||||||
// aux-build:cci_class_5.rs
|
// aux-build:cci_class_5.rs
|
||||||
extern mod cci_class_5;
|
extern mod cci_class_5;
|
||||||
|
@ -17,5 +15,5 @@ use cci_class_5::kitties::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let nyan : cat = cat(52, 99);
|
let nyan : cat = cat(52, 99);
|
||||||
nyan.nap();
|
nyan.nap(); //~ ERROR method `nap` is private
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue