auto merge of #12245 : nick29581/rust/priv2, r=alexcrichton
closes #4110
This commit is contained in:
commit
a25f447835
@ -41,6 +41,11 @@ pub type ExportedItems = HashSet<ast::NodeId>;
|
||||
/// reexporting a public struct doesn't inline the doc).
|
||||
pub type PublicItems = HashSet<ast::NodeId>;
|
||||
|
||||
/// Result of a checking operation - None => no errors were found. Some => an
|
||||
/// error and contains the span and message for reporting that error and
|
||||
/// optionally the same for a note about the error.
|
||||
type CheckResult = Option<(Span, ~str, Option<(Span, ~str)>)>;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// The parent visitor, used to determine what's the parent of what (node-wise)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@ -510,40 +515,50 @@ impl<'a> PrivacyVisitor<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Guarantee that a particular definition is public, possibly emitting an
|
||||
/// error message if it's not.
|
||||
fn ensure_public(&self, span: Span, to_check: ast::DefId,
|
||||
source_did: Option<ast::DefId>, msg: &str) -> bool {
|
||||
match self.def_privacy(to_check) {
|
||||
ExternallyDenied => {
|
||||
self.tcx.sess.span_err(span, format!("{} is private", msg))
|
||||
}
|
||||
DisallowedBy(id) => {
|
||||
if id == source_did.unwrap_or(to_check).node {
|
||||
self.tcx.sess.span_err(span, format!("{} is private", msg));
|
||||
return false;
|
||||
} else {
|
||||
self.tcx.sess.span_err(span, format!("{} is inaccessible",
|
||||
msg));
|
||||
fn report_error(&self, result: CheckResult) -> bool {
|
||||
match result {
|
||||
None => true,
|
||||
Some((span, msg, note)) => {
|
||||
self.tcx.sess.span_err(span, msg);
|
||||
match note {
|
||||
Some((span, msg)) => self.tcx.sess.span_note(span, msg),
|
||||
None => {},
|
||||
}
|
||||
false
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Guarantee that a particular definition is public. Returns a CheckResult
|
||||
/// which contains any errors found. These can be reported using `report_error`.
|
||||
/// If the result is `None`, no errors were found.
|
||||
fn ensure_public(&self, span: Span, to_check: ast::DefId,
|
||||
source_did: Option<ast::DefId>, msg: &str) -> CheckResult {
|
||||
match self.def_privacy(to_check) {
|
||||
ExternallyDenied => Some((span, format!("{} is private", msg), None)),
|
||||
DisallowedBy(id) => {
|
||||
let (err_span, err_msg) = if id == source_did.unwrap_or(to_check).node {
|
||||
return Some((span, format!("{} is private", msg), None));
|
||||
} else {
|
||||
(span, format!("{} is inaccessible", msg))
|
||||
};
|
||||
match self.tcx.map.find(id) {
|
||||
Some(ast_map::NodeItem(item)) => {
|
||||
let desc = match item.node {
|
||||
ast::ItemMod(..) => "module",
|
||||
ast::ItemTrait(..) => "trait",
|
||||
_ => return false,
|
||||
_ => return Some((err_span, err_msg, None)),
|
||||
};
|
||||
let msg = format!("{} `{}` is private",
|
||||
desc,
|
||||
token::get_ident(item.ident));
|
||||
self.tcx.sess.span_note(span, msg);
|
||||
}
|
||||
Some(..) | None => {}
|
||||
Some((err_span, err_msg, Some((span, msg))))
|
||||
},
|
||||
_ => Some((err_span, err_msg, None)),
|
||||
}
|
||||
}
|
||||
Allowable => return true
|
||||
},
|
||||
Allowable => None,
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Checks that a field is in scope.
|
||||
@ -613,34 +628,99 @@ impl<'a> PrivacyVisitor<'a> {
|
||||
let method_id = ty::method(self.tcx, method_id).provided_source
|
||||
.unwrap_or(method_id);
|
||||
|
||||
self.ensure_public(span,
|
||||
method_id,
|
||||
None,
|
||||
format!("method `{}`", token::get_ident(name)));
|
||||
let string = token::get_ident(name);
|
||||
self.report_error(self.ensure_public(span,
|
||||
method_id,
|
||||
None,
|
||||
format!("method `{}`", string)));
|
||||
}
|
||||
|
||||
// Checks that a path is in scope.
|
||||
fn check_path(&mut self, span: Span, path_id: ast::NodeId, path: &ast::Path) {
|
||||
debug!("privacy - path {}", self.nodestr(path_id));
|
||||
let def_map = self.tcx.def_map.borrow();
|
||||
let def = def_map.get().get_copy(&path_id);
|
||||
let orig_def = def_map.get().get_copy(&path_id);
|
||||
let ck = |tyname: &str| {
|
||||
let origdid = def_id_of_def(def);
|
||||
let ck_public = |def: ast::DefId| {
|
||||
let name = token::get_ident(path.segments
|
||||
.last()
|
||||
.unwrap()
|
||||
.identifier);
|
||||
let origdid = def_id_of_def(orig_def);
|
||||
self.ensure_public(span,
|
||||
def,
|
||||
Some(origdid),
|
||||
format!("{} `{}`",
|
||||
tyname,
|
||||
name))
|
||||
};
|
||||
|
||||
match *self.last_private_map.get(&path_id) {
|
||||
resolve::AllPublic => {},
|
||||
resolve::DependsOn(def) => {
|
||||
let name = token::get_ident(path.segments
|
||||
.last()
|
||||
.unwrap()
|
||||
.identifier);
|
||||
self.ensure_public(span,
|
||||
def,
|
||||
Some(origdid),
|
||||
format!("{} `{}`",
|
||||
tyname, name));
|
||||
}
|
||||
resolve::LastMod(resolve::AllPublic) => {},
|
||||
resolve::LastMod(resolve::DependsOn(def)) => {
|
||||
self.report_error(ck_public(def));
|
||||
},
|
||||
resolve::LastImport{value_priv: value_priv,
|
||||
value_used: check_value,
|
||||
type_priv: type_priv,
|
||||
type_used: check_type} => {
|
||||
// This dance with found_error is because we don't want to report
|
||||
// a privacy error twice for the same directive.
|
||||
let found_error = match (type_priv, check_type) {
|
||||
(Some(resolve::DependsOn(def)), resolve::Used) => {
|
||||
!self.report_error(ck_public(def))
|
||||
},
|
||||
_ => false,
|
||||
};
|
||||
if !found_error {
|
||||
match (value_priv, check_value) {
|
||||
(Some(resolve::DependsOn(def)), resolve::Used) => {
|
||||
self.report_error(ck_public(def));
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
// If an import is not used in either namespace, we still want to check
|
||||
// that it could be legal. Therefore we check in both namespaces and only
|
||||
// report an error if both would be illegal. We only report one error,
|
||||
// even if it is illegal to import from both namespaces.
|
||||
match (value_priv, check_value, type_priv, check_type) {
|
||||
(Some(p), resolve::Unused, None, _) |
|
||||
(None, _, Some(p), resolve::Unused) => {
|
||||
let p = match p {
|
||||
resolve::AllPublic => None,
|
||||
resolve::DependsOn(def) => ck_public(def),
|
||||
};
|
||||
if p.is_some() {
|
||||
self.report_error(p);
|
||||
}
|
||||
},
|
||||
(Some(v), resolve::Unused, Some(t), resolve::Unused) => {
|
||||
let v = match v {
|
||||
resolve::AllPublic => None,
|
||||
resolve::DependsOn(def) => ck_public(def),
|
||||
};
|
||||
let t = match t {
|
||||
resolve::AllPublic => None,
|
||||
resolve::DependsOn(def) => ck_public(def),
|
||||
};
|
||||
match (v, t) {
|
||||
(Some(_), Some(t)) => {
|
||||
self.report_error(Some(t));
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
// FIXME(#12334) Imports can refer to definitions in both the type and
|
||||
// value namespaces. The privacy information is aware of this, but the
|
||||
// def map is not. Therefore the names we work out below will not always
|
||||
// be accurate and we can get slightly wonky error messages (but type
|
||||
// checking is always correct).
|
||||
let def_map = self.tcx.def_map.borrow();
|
||||
match def_map.get().get_copy(&path_id) {
|
||||
ast::DefStaticMethod(..) => ck("static method"),
|
||||
@ -668,7 +748,7 @@ impl<'a> PrivacyVisitor<'a> {
|
||||
// is whether the trait itself is accessible or not.
|
||||
method_param(method_param { trait_id: trait_id, .. }) |
|
||||
method_object(method_object { trait_id: trait_id, .. }) => {
|
||||
self.ensure_public(span, trait_id, None, "source trait");
|
||||
self.report_error(self.ensure_public(span, trait_id, None, "source trait"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,6 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
|
||||
use driver::session::Session;
|
||||
use metadata::csearch;
|
||||
use metadata::decoder::{DefLike, DlDef, DlField, DlImpl};
|
||||
@ -64,14 +63,34 @@ pub type ExternalExports = HashSet<DefId>;
|
||||
pub type LastPrivateMap = HashMap<NodeId, LastPrivate>;
|
||||
|
||||
pub enum LastPrivate {
|
||||
LastMod(PrivateDep),
|
||||
// `use` directives (imports) can refer to two separate definitions in the
|
||||
// type and value namespaces. We record here the last private node for each
|
||||
// and whether the import is in fact used for each.
|
||||
// If the Option<PrivateDep> fields are None, it means there is no defintion
|
||||
// in that namespace.
|
||||
LastImport{value_priv: Option<PrivateDep>,
|
||||
value_used: ImportUse,
|
||||
type_priv: Option<PrivateDep>,
|
||||
type_used: ImportUse},
|
||||
}
|
||||
|
||||
pub enum PrivateDep {
|
||||
AllPublic,
|
||||
DependsOn(DefId),
|
||||
}
|
||||
|
||||
// How an import is used.
|
||||
#[deriving(Eq)]
|
||||
pub enum ImportUse {
|
||||
Unused, // The import is not used.
|
||||
Used, // The import is used.
|
||||
}
|
||||
|
||||
impl LastPrivate {
|
||||
fn or(self, other: LastPrivate) -> LastPrivate {
|
||||
match (self, other) {
|
||||
(me, AllPublic) => me,
|
||||
(me, LastMod(AllPublic)) => me,
|
||||
(_, other) => other,
|
||||
}
|
||||
}
|
||||
@ -84,7 +103,7 @@ enum PatternBindingMode {
|
||||
ArgumentIrrefutableMode,
|
||||
}
|
||||
|
||||
#[deriving(Eq)]
|
||||
#[deriving(Eq, IterBytes)]
|
||||
enum Namespace {
|
||||
TypeNS,
|
||||
ValueNS
|
||||
@ -869,7 +888,7 @@ struct Resolver {
|
||||
// so as to avoid printing duplicate errors
|
||||
emit_errors: bool,
|
||||
|
||||
used_imports: HashSet<NodeId>,
|
||||
used_imports: HashSet<(NodeId, Namespace)>,
|
||||
}
|
||||
|
||||
struct BuildReducedGraphVisitor<'a> {
|
||||
@ -904,7 +923,7 @@ impl<'a> Visitor<ReducedGraphParent> for BuildReducedGraphVisitor<'a> {
|
||||
|
||||
}
|
||||
|
||||
struct UnusedImportCheckVisitor<'a> { resolver: &'a Resolver }
|
||||
struct UnusedImportCheckVisitor<'a> { resolver: &'a mut Resolver }
|
||||
|
||||
impl<'a> Visitor<()> for UnusedImportCheckVisitor<'a> {
|
||||
fn visit_view_item(&mut self, vi: &ViewItem, _: ()) {
|
||||
@ -2152,7 +2171,7 @@ impl Resolver {
|
||||
// First, resolve the module path for the directive, if necessary.
|
||||
let container = if module_path.len() == 0 {
|
||||
// Use the crate root.
|
||||
Some((self.graph_root.get_module(), AllPublic))
|
||||
Some((self.graph_root.get_module(), LastMod(AllPublic)))
|
||||
} else {
|
||||
match self.resolve_module_path(module_,
|
||||
*module_path,
|
||||
@ -2257,6 +2276,12 @@ impl Resolver {
|
||||
directive.id,
|
||||
lp);
|
||||
|
||||
let lp = match lp {
|
||||
LastMod(lp) => lp,
|
||||
LastImport{..} => self.session.span_bug(directive.span,
|
||||
"Not expecting Import here, must be LastMod"),
|
||||
};
|
||||
|
||||
// We need to resolve both namespaces for this to succeed.
|
||||
//
|
||||
|
||||
@ -2287,7 +2312,8 @@ impl Resolver {
|
||||
|
||||
// Unless we managed to find a result in both namespaces (unlikely),
|
||||
// search imports as well.
|
||||
let mut used_reexport = false;
|
||||
let mut value_used_reexport = false;
|
||||
let mut type_used_reexport = false;
|
||||
match (value_result, type_result) {
|
||||
(BoundResult(..), BoundResult(..)) => {} // Continue.
|
||||
_ => {
|
||||
@ -2342,7 +2368,7 @@ impl Resolver {
|
||||
}
|
||||
Some(target) => {
|
||||
let id = import_resolution.id(namespace);
|
||||
this.used_imports.insert(id);
|
||||
this.used_imports.insert((id, namespace));
|
||||
return BoundResult(target.target_module,
|
||||
target.bindings);
|
||||
}
|
||||
@ -2354,12 +2380,12 @@ impl Resolver {
|
||||
if value_result.is_unknown() {
|
||||
value_result = get_binding(self, *import_resolution,
|
||||
ValueNS);
|
||||
used_reexport = import_resolution.is_public.get();
|
||||
value_used_reexport = import_resolution.is_public.get();
|
||||
}
|
||||
if type_result.is_unknown() {
|
||||
type_result = get_binding(self, *import_resolution,
|
||||
TypeNS);
|
||||
used_reexport = import_resolution.is_public.get();
|
||||
type_used_reexport = import_resolution.is_public.get();
|
||||
}
|
||||
|
||||
}
|
||||
@ -2375,7 +2401,8 @@ impl Resolver {
|
||||
|
||||
// If we didn't find a result in the type namespace, search the
|
||||
// external modules.
|
||||
let mut used_public = false;
|
||||
let mut value_used_public = false;
|
||||
let mut type_used_public = false;
|
||||
match type_result {
|
||||
BoundResult(..) => {}
|
||||
_ => {
|
||||
@ -2393,7 +2420,7 @@ impl Resolver {
|
||||
module);
|
||||
type_result = BoundResult(containing_module,
|
||||
name_bindings);
|
||||
used_public = true;
|
||||
type_used_public = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2412,7 +2439,7 @@ impl Resolver {
|
||||
import_resolution.value_target.set(
|
||||
Some(Target::new(target_module, name_bindings)));
|
||||
import_resolution.value_id.set(directive.id);
|
||||
used_public = name_bindings.defined_in_public_namespace(ValueNS);
|
||||
value_used_public = name_bindings.defined_in_public_namespace(ValueNS);
|
||||
}
|
||||
UnboundResult => { /* Continue. */ }
|
||||
UnknownResult => {
|
||||
@ -2426,7 +2453,7 @@ impl Resolver {
|
||||
import_resolution.type_target.set(
|
||||
Some(Target::new(target_module, name_bindings)));
|
||||
import_resolution.type_id.set(directive.id);
|
||||
used_public = name_bindings.defined_in_public_namespace(TypeNS);
|
||||
type_used_public = name_bindings.defined_in_public_namespace(TypeNS);
|
||||
}
|
||||
UnboundResult => { /* Continue. */ }
|
||||
UnknownResult => {
|
||||
@ -2443,7 +2470,8 @@ impl Resolver {
|
||||
self.resolve_error(directive.span, msg);
|
||||
return Failed;
|
||||
}
|
||||
let used_public = used_reexport || used_public;
|
||||
let value_used_public = value_used_reexport || value_used_public;
|
||||
let type_used_public = type_used_reexport || type_used_public;
|
||||
|
||||
assert!(import_resolution.outstanding_references.get() >= 1);
|
||||
import_resolution.outstanding_references.set(
|
||||
@ -2452,28 +2480,33 @@ impl Resolver {
|
||||
// record what this import resolves to for later uses in documentation,
|
||||
// this may resolve to either a value or a type, but for documentation
|
||||
// purposes it's good enough to just favor one over the other.
|
||||
match import_resolution.value_target.get() {
|
||||
let value_private = match import_resolution.value_target.get() {
|
||||
Some(target) => {
|
||||
let def = target.bindings.def_for_namespace(ValueNS).unwrap();
|
||||
let mut def_map = self.def_map.borrow_mut();
|
||||
def_map.get().insert(directive.id, def);
|
||||
let did = def_id_of_def(def);
|
||||
self.last_private.insert(directive.id,
|
||||
if used_public {lp} else {DependsOn(did)});
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
match import_resolution.type_target.get() {
|
||||
if value_used_public {Some(lp)} else {Some(DependsOn(did))}
|
||||
},
|
||||
// AllPublic here and below is a dummy value, it should never be used because
|
||||
// _exists is false.
|
||||
None => None,
|
||||
};
|
||||
let type_private = match import_resolution.type_target.get() {
|
||||
Some(target) => {
|
||||
let def = target.bindings.def_for_namespace(TypeNS).unwrap();
|
||||
let mut def_map = self.def_map.borrow_mut();
|
||||
def_map.get().insert(directive.id, def);
|
||||
let did = def_id_of_def(def);
|
||||
self.last_private.insert(directive.id,
|
||||
if used_public {lp} else {DependsOn(did)});
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
if type_used_public {Some(lp)} else {Some(DependsOn(did))}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
|
||||
self.last_private.insert(directive.id, LastImport{value_priv: value_private,
|
||||
value_used: Used,
|
||||
type_priv: type_private,
|
||||
type_used: Used});
|
||||
|
||||
debug!("(resolving single import) successfully resolved import");
|
||||
return Success(());
|
||||
@ -2732,7 +2765,7 @@ impl Resolver {
|
||||
.get() {
|
||||
Some(did) => {
|
||||
closest_private =
|
||||
DependsOn(did);
|
||||
LastMod(DependsOn(did));
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
@ -2817,7 +2850,7 @@ impl Resolver {
|
||||
// resolution process at index zero.
|
||||
search_module = self.graph_root.get_module();
|
||||
start_index = 0;
|
||||
last_private = AllPublic;
|
||||
last_private = LastMod(AllPublic);
|
||||
}
|
||||
UseLexicalScope => {
|
||||
// This is not a crate-relative path. We resolve the
|
||||
@ -2839,7 +2872,7 @@ impl Resolver {
|
||||
Success(containing_module) => {
|
||||
search_module = containing_module;
|
||||
start_index = 1;
|
||||
last_private = AllPublic;
|
||||
last_private = LastMod(AllPublic);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2848,9 +2881,9 @@ impl Resolver {
|
||||
Success(PrefixFound(containing_module, index)) => {
|
||||
search_module = containing_module;
|
||||
start_index = index;
|
||||
last_private = DependsOn(containing_module.def_id
|
||||
.get()
|
||||
.unwrap());
|
||||
last_private = LastMod(DependsOn(containing_module.def_id
|
||||
.get()
|
||||
.unwrap()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -2914,7 +2947,7 @@ impl Resolver {
|
||||
Some(target) => {
|
||||
debug!("(resolving item in lexical scope) using \
|
||||
import resolution");
|
||||
self.used_imports.insert(import_resolution.id(namespace));
|
||||
self.used_imports.insert((import_resolution.id(namespace), namespace));
|
||||
return Success((target, false));
|
||||
}
|
||||
}
|
||||
@ -3199,7 +3232,7 @@ impl Resolver {
|
||||
Some(target) => {
|
||||
debug!("(resolving name in module) resolved to \
|
||||
import");
|
||||
self.used_imports.insert(import_resolution.id(namespace));
|
||||
self.used_imports.insert((import_resolution.id(namespace), namespace));
|
||||
return Success((target, true));
|
||||
}
|
||||
}
|
||||
@ -3808,7 +3841,7 @@ impl Resolver {
|
||||
// Associate this type parameter with
|
||||
// the item that bound it
|
||||
self.record_def(type_parameter.id,
|
||||
(DefTyParamBinder(node_id), AllPublic));
|
||||
(DefTyParamBinder(node_id), LastMod(AllPublic)));
|
||||
// plain insert (no renaming)
|
||||
let mut bindings = function_type_rib.bindings
|
||||
.borrow_mut();
|
||||
@ -4269,7 +4302,7 @@ impl Resolver {
|
||||
|
||||
Some(&primitive_type) => {
|
||||
result_def =
|
||||
Some((DefPrimTy(primitive_type), AllPublic));
|
||||
Some((DefPrimTy(primitive_type), LastMod(AllPublic)));
|
||||
|
||||
if path.segments
|
||||
.iter()
|
||||
@ -4438,7 +4471,7 @@ impl Resolver {
|
||||
// will be able to distinguish variants from
|
||||
// locals in patterns.
|
||||
|
||||
self.record_def(pattern.id, (def, AllPublic));
|
||||
self.record_def(pattern.id, (def, LastMod(AllPublic)));
|
||||
|
||||
// Add the binding to the local ribs, if it
|
||||
// doesn't already exist in the bindings list. (We
|
||||
@ -4632,10 +4665,10 @@ impl Resolver {
|
||||
// the lookup happened only within the current module.
|
||||
match def.def {
|
||||
def @ DefVariant(..) | def @ DefStruct(..) => {
|
||||
return FoundStructOrEnumVariant(def, AllPublic);
|
||||
return FoundStructOrEnumVariant(def, LastMod(AllPublic));
|
||||
}
|
||||
def @ DefStatic(_, false) => {
|
||||
return FoundConst(def, AllPublic);
|
||||
return FoundConst(def, LastMod(AllPublic));
|
||||
}
|
||||
_ => {
|
||||
return BareIdentifierPatternUnresolved;
|
||||
@ -4711,7 +4744,7 @@ impl Resolver {
|
||||
namespace,
|
||||
span) {
|
||||
Some(def) => {
|
||||
return Some((def, AllPublic));
|
||||
return Some((def, LastMod(AllPublic)));
|
||||
}
|
||||
None => {
|
||||
// Continue.
|
||||
@ -4741,8 +4774,8 @@ impl Resolver {
|
||||
// Found it. Stop the search here.
|
||||
let p = child_name_bindings.defined_in_public_namespace(
|
||||
namespace);
|
||||
let lp = if p {AllPublic} else {
|
||||
DependsOn(def_id_of_def(def))
|
||||
let lp = if p {LastMod(AllPublic)} else {
|
||||
LastMod(DependsOn(def_id_of_def(def)))
|
||||
};
|
||||
return ChildNameDefinition(def, lp);
|
||||
}
|
||||
@ -4764,8 +4797,8 @@ impl Resolver {
|
||||
Some(def) => {
|
||||
// Found it.
|
||||
let id = import_resolution.id(namespace);
|
||||
self.used_imports.insert(id);
|
||||
return ImportNameDefinition(def, AllPublic);
|
||||
self.used_imports.insert((id, namespace));
|
||||
return ImportNameDefinition(def, LastMod(AllPublic));
|
||||
}
|
||||
None => {
|
||||
// This can happen with external impls, due to
|
||||
@ -4792,8 +4825,8 @@ impl Resolver {
|
||||
match module.def_id.get() {
|
||||
None => {} // Continue.
|
||||
Some(def_id) => {
|
||||
let lp = if module.is_public {AllPublic} else {
|
||||
DependsOn(def_id)
|
||||
let lp = if module.is_public {LastMod(AllPublic)} else {
|
||||
LastMod(DependsOn(def_id))
|
||||
};
|
||||
return ChildNameDefinition(DefMod(def_id), lp);
|
||||
}
|
||||
@ -4887,7 +4920,7 @@ impl Resolver {
|
||||
0,
|
||||
path.span,
|
||||
PathSearch,
|
||||
AllPublic) {
|
||||
LastMod(AllPublic)) {
|
||||
Failed => {
|
||||
let msg = format!("use of undeclared module `::{}`",
|
||||
self.idents_to_str(module_path_idents));
|
||||
@ -4983,7 +5016,7 @@ impl Resolver {
|
||||
// This lookup is "all public" because it only searched
|
||||
// for one identifier in the current module (couldn't
|
||||
// have passed through reexports or anything like that.
|
||||
return Some((def, AllPublic));
|
||||
return Some((def, LastMod(AllPublic)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -5194,8 +5227,8 @@ impl Resolver {
|
||||
format!("use of undeclared label `{}`",
|
||||
token::get_name(label))),
|
||||
Some(DlDef(def @ DefLabel(_))) => {
|
||||
// FIXME: is AllPublic correct?
|
||||
self.record_def(expr.id, (def, AllPublic))
|
||||
// Since this def is a label, it is never read.
|
||||
self.record_def(expr.id, (def, LastMod(AllPublic)))
|
||||
}
|
||||
Some(_) => {
|
||||
self.session.span_bug(expr.span,
|
||||
@ -5353,7 +5386,7 @@ impl Resolver {
|
||||
};
|
||||
if candidate_traits.contains(&did) {
|
||||
self.add_trait_info(&mut found_traits, did, name);
|
||||
self.used_imports.insert(import.type_id.get());
|
||||
self.used_imports.insert((import.type_id.get(), TypeNS));
|
||||
}
|
||||
}
|
||||
|
||||
@ -5395,6 +5428,8 @@ impl Resolver {
|
||||
fn record_def(&mut self, node_id: NodeId, (def, lp): (Def, LastPrivate)) {
|
||||
debug!("(recording def) recording {:?} for {:?}, last private {:?}",
|
||||
def, node_id, lp);
|
||||
assert!(match lp {LastImport{..} => false, _ => true},
|
||||
"Import should only be used for `use` directives");
|
||||
self.last_private.insert(node_id, lp);
|
||||
let mut def_map = self.def_map.borrow_mut();
|
||||
def_map.get().insert_or_update_with(node_id, def, |_, old_value| {
|
||||
@ -5426,16 +5461,17 @@ impl Resolver {
|
||||
//
|
||||
// Unused import checking
|
||||
//
|
||||
// Although this is a lint pass, it lives in here because it depends on
|
||||
// resolve data structures.
|
||||
// Although this is mostly a lint pass, it lives in here because it depends on
|
||||
// resolve data structures and because it finalises the privacy information for
|
||||
// `use` directives.
|
||||
//
|
||||
|
||||
fn check_for_unused_imports(&self, krate: &ast::Crate) {
|
||||
fn check_for_unused_imports(&mut self, krate: &ast::Crate) {
|
||||
let mut visitor = UnusedImportCheckVisitor{ resolver: self };
|
||||
visit::walk_crate(&mut visitor, krate, ());
|
||||
}
|
||||
|
||||
fn check_for_item_unused_imports(&self, vi: &ViewItem) {
|
||||
fn check_for_item_unused_imports(&mut self, vi: &ViewItem) {
|
||||
// Ignore is_public import statements because there's no way to be sure
|
||||
// whether they're used or not. Also ignore imports with a dummy span
|
||||
// because this means that they were generated in some fashion by the
|
||||
@ -5448,29 +5484,73 @@ impl Resolver {
|
||||
ViewItemUse(ref path) => {
|
||||
for p in path.iter() {
|
||||
match p.node {
|
||||
ViewPathSimple(_, _, id) | ViewPathGlob(_, id) => {
|
||||
if !self.used_imports.contains(&id) {
|
||||
self.session.add_lint(UnusedImports,
|
||||
id, p.span,
|
||||
~"unused import");
|
||||
}
|
||||
}
|
||||
|
||||
ViewPathSimple(_, _, id) => self.finalize_import(id, p.span),
|
||||
ViewPathList(_, ref list, _) => {
|
||||
for i in list.iter() {
|
||||
if !self.used_imports.contains(&i.node.id) {
|
||||
self.session.add_lint(UnusedImports,
|
||||
i.node.id, i.span,
|
||||
~"unused import");
|
||||
}
|
||||
self.finalize_import(i.node.id, i.span);
|
||||
}
|
||||
}
|
||||
},
|
||||
ViewPathGlob(_, id) => {
|
||||
if !self.used_imports.contains(&(id, TypeNS)) &&
|
||||
!self.used_imports.contains(&(id, ValueNS)) {
|
||||
self.session.add_lint(UnusedImports, id, p.span, ~"unused import");
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We have information about whether `use` (import) directives are actually used now.
|
||||
// If an import is not used at all, we signal a lint error. If an import is only used
|
||||
// for a single namespace, we remove the other namespace from the recorded privacy
|
||||
// information. That means in privacy.rs, we will only check imports and namespaces
|
||||
// which are used. In particular, this means that if an import could name either a
|
||||
// public or private item, we will check the correct thing, dependent on how the import
|
||||
// is used.
|
||||
fn finalize_import(&mut self, id: NodeId, span: Span) {
|
||||
debug!("finalizing import uses for {}", self.session.codemap.span_to_snippet(span));
|
||||
|
||||
if !self.used_imports.contains(&(id, TypeNS)) &&
|
||||
!self.used_imports.contains(&(id, ValueNS)) {
|
||||
self.session.add_lint(UnusedImports, id, span, ~"unused import");
|
||||
}
|
||||
|
||||
let (v_priv, t_priv) = match self.last_private.find(&id) {
|
||||
Some(&LastImport{value_priv: v,
|
||||
value_used: _,
|
||||
type_priv: t,
|
||||
type_used: _}) => (v, t),
|
||||
Some(_) => fail!("We should only have LastImport for `use` directives"),
|
||||
_ => return,
|
||||
};
|
||||
|
||||
let mut v_used = if self.used_imports.contains(&(id, ValueNS)) {
|
||||
Used
|
||||
} else {
|
||||
Unused
|
||||
};
|
||||
let t_used = if self.used_imports.contains(&(id, TypeNS)) {
|
||||
Used
|
||||
} else {
|
||||
Unused
|
||||
};
|
||||
|
||||
match (v_priv, t_priv) {
|
||||
// Since some items may be both in the value _and_ type namespaces (e.g., structs)
|
||||
// we might have two LastPrivates pointing at the same thing. There is no point
|
||||
// checking both, so lets not check the value one.
|
||||
(Some(DependsOn(def_v)), Some(DependsOn(def_t))) if def_v == def_t => v_used = Unused,
|
||||
_ => {},
|
||||
}
|
||||
|
||||
self.last_private.insert(id, LastImport{value_priv: v_priv,
|
||||
value_used: v_used,
|
||||
type_priv: t_priv,
|
||||
type_used: t_used});
|
||||
}
|
||||
|
||||
//
|
||||
// Diagnostics
|
||||
//
|
||||
|
66
src/test/compile-fail/privacy-ns1.rs
Normal file
66
src/test/compile-fail/privacy-ns1.rs
Normal file
@ -0,0 +1,66 @@
|
||||
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Check we do the correct privacy checks when we import a name and there is an
|
||||
// item with that name in both the value and type namespaces.
|
||||
|
||||
#[feature(globs)];
|
||||
#[allow(dead_code)];
|
||||
#[allow(unused_imports)];
|
||||
|
||||
// public type, private value
|
||||
pub mod foo1 {
|
||||
pub trait Bar {
|
||||
}
|
||||
pub struct Baz;
|
||||
|
||||
fn Bar() { }
|
||||
}
|
||||
|
||||
fn test_glob1() {
|
||||
use foo1::*;
|
||||
|
||||
Bar(); //~ ERROR unresolved name `Bar`.
|
||||
}
|
||||
|
||||
// private type, public value
|
||||
pub mod foo2 {
|
||||
trait Bar {
|
||||
}
|
||||
pub struct Baz;
|
||||
|
||||
pub fn Bar() { }
|
||||
}
|
||||
|
||||
fn test_glob2() {
|
||||
use foo2::*;
|
||||
|
||||
let _x: ~Bar; //~ ERROR use of undeclared type name `Bar`
|
||||
}
|
||||
|
||||
// neither public
|
||||
pub mod foo3 {
|
||||
trait Bar {
|
||||
}
|
||||
pub struct Baz;
|
||||
|
||||
fn Bar() { }
|
||||
}
|
||||
|
||||
fn test_glob3() {
|
||||
use foo3::*;
|
||||
|
||||
Bar(); //~ ERROR unresolved name `Bar`.
|
||||
let _x: ~Bar; //~ ERROR use of undeclared type name `Bar`
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
||||
|
91
src/test/compile-fail/privacy-ns2.rs
Normal file
91
src/test/compile-fail/privacy-ns2.rs
Normal file
@ -0,0 +1,91 @@
|
||||
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// Check we do the correct privacy checks when we import a name and there is an
|
||||
// item with that name in both the value and type namespaces.
|
||||
|
||||
#[feature(globs)];
|
||||
#[allow(dead_code)];
|
||||
#[allow(unused_imports)];
|
||||
|
||||
// public type, private value
|
||||
pub mod foo1 {
|
||||
pub trait Bar {
|
||||
}
|
||||
pub struct Baz;
|
||||
|
||||
fn Bar() { }
|
||||
}
|
||||
|
||||
fn test_single1() {
|
||||
// In an ideal world, these would be private instead of inaccessible.
|
||||
use foo1::Bar; //~ ERROR `Bar` is inaccessible
|
||||
|
||||
Bar();
|
||||
}
|
||||
|
||||
fn test_list1() {
|
||||
use foo1::{Bar,Baz}; //~ ERROR `Bar` is inaccessible
|
||||
|
||||
Bar();
|
||||
}
|
||||
|
||||
// private type, public value
|
||||
pub mod foo2 {
|
||||
trait Bar {
|
||||
}
|
||||
pub struct Baz;
|
||||
|
||||
pub fn Bar() { }
|
||||
}
|
||||
|
||||
fn test_single2() {
|
||||
use foo2::Bar; //~ ERROR `Bar` is private
|
||||
|
||||
let _x : ~Bar;
|
||||
}
|
||||
|
||||
fn test_list2() {
|
||||
use foo2::{Bar,Baz}; //~ ERROR `Bar` is private
|
||||
|
||||
let _x: ~Bar;
|
||||
}
|
||||
|
||||
// neither public
|
||||
pub mod foo3 {
|
||||
trait Bar {
|
||||
}
|
||||
pub struct Baz;
|
||||
|
||||
fn Bar() { }
|
||||
}
|
||||
|
||||
fn test_unused3() {
|
||||
use foo3::Bar; //~ ERROR `Bar` is private
|
||||
use foo3::{Bar,Baz}; //~ ERROR `Bar` is private
|
||||
}
|
||||
|
||||
fn test_single3() {
|
||||
use foo3::Bar; //~ ERROR `Bar` is private
|
||||
|
||||
Bar();
|
||||
let _x: ~Bar;
|
||||
}
|
||||
|
||||
fn test_list3() {
|
||||
use foo3::{Bar,Baz}; //~ ERROR `Bar` is private
|
||||
|
||||
Bar();
|
||||
let _x: ~Bar;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
||||
|
124
src/test/run-pass/privacy-ns.rs
Normal file
124
src/test/run-pass/privacy-ns.rs
Normal file
@ -0,0 +1,124 @@
|
||||
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
// ignore-fast
|
||||
|
||||
// Check we do the correct privacy checks when we import a name and there is an
|
||||
// item with that name in both the value and type namespaces.
|
||||
|
||||
#[feature(globs)];
|
||||
#[allow(dead_code)];
|
||||
#[allow(unused_imports)];
|
||||
|
||||
// public type, private value
|
||||
pub mod foo1 {
|
||||
pub trait Bar {
|
||||
}
|
||||
pub struct Baz;
|
||||
|
||||
fn Bar() { }
|
||||
}
|
||||
|
||||
fn test_unused1() {
|
||||
use foo1::Bar;
|
||||
use foo1::{Bar,Baz};
|
||||
use foo1::*;
|
||||
}
|
||||
|
||||
fn test_single1() {
|
||||
use foo1::Bar;
|
||||
|
||||
let _x: ~Bar;
|
||||
}
|
||||
|
||||
fn test_list1() {
|
||||
use foo1::{Bar,Baz};
|
||||
|
||||
let _x: ~Bar;
|
||||
}
|
||||
|
||||
fn test_glob1() {
|
||||
use foo1::*;
|
||||
|
||||
let _x: ~Bar;
|
||||
}
|
||||
|
||||
// private type, public value
|
||||
pub mod foo2 {
|
||||
trait Bar {
|
||||
}
|
||||
pub struct Baz;
|
||||
|
||||
pub fn Bar() { }
|
||||
}
|
||||
|
||||
fn test_unused2() {
|
||||
use foo2::Bar;
|
||||
use foo2::{Bar,Baz};
|
||||
use foo2::*;
|
||||
}
|
||||
|
||||
fn test_single2() {
|
||||
use foo2::Bar;
|
||||
|
||||
Bar();
|
||||
}
|
||||
|
||||
fn test_list2() {
|
||||
use foo2::{Bar,Baz};
|
||||
|
||||
Bar();
|
||||
}
|
||||
|
||||
fn test_glob2() {
|
||||
use foo2::*;
|
||||
|
||||
Bar();
|
||||
}
|
||||
|
||||
// public type, public value
|
||||
pub mod foo3 {
|
||||
pub trait Bar {
|
||||
}
|
||||
pub struct Baz;
|
||||
|
||||
pub fn Bar() { }
|
||||
}
|
||||
|
||||
fn test_unused3() {
|
||||
use foo3::Bar;
|
||||
use foo3::{Bar,Baz};
|
||||
use foo3::*;
|
||||
}
|
||||
|
||||
fn test_single3() {
|
||||
use foo3::Bar;
|
||||
|
||||
Bar();
|
||||
let _x: ~Bar;
|
||||
}
|
||||
|
||||
fn test_list3() {
|
||||
use foo3::{Bar,Baz};
|
||||
|
||||
Bar();
|
||||
let _x: ~Bar;
|
||||
}
|
||||
|
||||
fn test_glob3() {
|
||||
use foo3::*;
|
||||
|
||||
Bar();
|
||||
let _x: ~Bar;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user