Skip to content

Commit df1686d

Browse files
committed
Make priavcy checking aware that a use directive can point to two defintions (namespaces) with different privacy. Closes #4110
1 parent 425f574 commit df1686d

File tree

6 files changed

+550
-109
lines changed

6 files changed

+550
-109
lines changed

src/librustc/middle/privacy.rs

+118-38
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ pub type ExportedItems = HashSet<ast::NodeId>;
4141
/// reexporting a public struct doesn't inline the doc).
4242
pub type PublicItems = HashSet<ast::NodeId>;
4343

44+
/// Result of a checking operation - None => no errors were found. Some => an
45+
/// error and contains the span and message for reporting that error and
46+
/// optionally the same for a note about the error.
47+
type CheckResult = Option<(Span, ~str, Option<(Span, ~str)>)>;
48+
4449
////////////////////////////////////////////////////////////////////////////////
4550
/// The parent visitor, used to determine what's the parent of what (node-wise)
4651
////////////////////////////////////////////////////////////////////////////////
@@ -510,40 +515,50 @@ impl<'a> PrivacyVisitor<'a> {
510515
}
511516
}
512517

513-
/// Guarantee that a particular definition is public, possibly emitting an
514-
/// error message if it's not.
518+
fn report_error(&self, result: CheckResult) -> bool {
519+
match result {
520+
None => true,
521+
Some((span, msg, note)) => {
522+
self.tcx.sess.span_err(span, msg);
523+
match note {
524+
Some((span, msg)) => self.tcx.sess.span_note(span, msg),
525+
None => {},
526+
}
527+
false
528+
},
529+
}
530+
}
531+
532+
/// Guarantee that a particular definition is public. Returns a CheckResult
533+
/// which contains any errors found. These can be reported using `report_error`.
534+
/// If the result is `None`, no errors were found.
515535
fn ensure_public(&self, span: Span, to_check: ast::DefId,
516-
source_did: Option<ast::DefId>, msg: &str) -> bool {
536+
source_did: Option<ast::DefId>, msg: &str) -> CheckResult {
517537
match self.def_privacy(to_check) {
518-
ExternallyDenied => {
519-
self.tcx.sess.span_err(span, format!("{} is private", msg))
520-
}
538+
ExternallyDenied => Some((span, format!("{} is private", msg), None)),
521539
DisallowedBy(id) => {
522-
if id == source_did.unwrap_or(to_check).node {
523-
self.tcx.sess.span_err(span, format!("{} is private", msg));
524-
return false;
540+
let (err_span, err_msg) = if id == source_did.unwrap_or(to_check).node {
541+
return Some((span, format!("{} is private", msg), None));
525542
} else {
526-
self.tcx.sess.span_err(span, format!("{} is inaccessible",
527-
msg));
528-
}
543+
(span, format!("{} is inaccessible", msg))
544+
};
529545
match self.tcx.map.find(id) {
530546
Some(ast_map::NodeItem(item)) => {
531547
let desc = match item.node {
532548
ast::ItemMod(..) => "module",
533549
ast::ItemTrait(..) => "trait",
534-
_ => return false,
550+
_ => return Some((err_span, err_msg, None)),
535551
};
536552
let msg = format!("{} `{}` is private",
537553
desc,
538554
token::get_ident(item.ident));
539-
self.tcx.sess.span_note(span, msg);
540-
}
541-
Some(..) | None => {}
555+
Some((err_span, err_msg, Some((span, msg))))
556+
},
557+
_ => Some((err_span, err_msg, None)),
542558
}
543-
}
544-
Allowable => return true
559+
},
560+
Allowable => None,
545561
}
546-
return false;
547562
}
548563

549564
// Checks that a field is in scope.
@@ -613,34 +628,99 @@ impl<'a> PrivacyVisitor<'a> {
613628
let method_id = ty::method(self.tcx, method_id).provided_source
614629
.unwrap_or(method_id);
615630

616-
self.ensure_public(span,
617-
method_id,
618-
None,
619-
format!("method `{}`", token::get_ident(name)));
631+
let string = token::get_ident(name);
632+
self.report_error(self.ensure_public(span,
633+
method_id,
634+
None,
635+
format!("method `{}`", string)));
620636
}
621637

622638
// Checks that a path is in scope.
623639
fn check_path(&mut self, span: Span, path_id: ast::NodeId, path: &ast::Path) {
624640
debug!("privacy - path {}", self.nodestr(path_id));
625641
let def_map = self.tcx.def_map.borrow();
626-
let def = def_map.get().get_copy(&path_id);
642+
let orig_def = def_map.get().get_copy(&path_id);
627643
let ck = |tyname: &str| {
628-
let origdid = def_id_of_def(def);
644+
let ck_public = |def: ast::DefId| {
645+
let name = token::get_ident(path.segments
646+
.last()
647+
.unwrap()
648+
.identifier);
649+
let origdid = def_id_of_def(orig_def);
650+
self.ensure_public(span,
651+
def,
652+
Some(origdid),
653+
format!("{} `{}`",
654+
tyname,
655+
name))
656+
};
657+
629658
match *self.last_private_map.get(&path_id) {
630-
resolve::AllPublic => {},
631-
resolve::DependsOn(def) => {
632-
let name = token::get_ident(path.segments
633-
.last()
634-
.unwrap()
635-
.identifier);
636-
self.ensure_public(span,
637-
def,
638-
Some(origdid),
639-
format!("{} `{}`",
640-
tyname, name));
641-
}
659+
resolve::LastMod(resolve::AllPublic) => {},
660+
resolve::LastMod(resolve::DependsOn(def)) => {
661+
self.report_error(ck_public(def));
662+
},
663+
resolve::LastImport{value_priv: value_priv,
664+
value_used: check_value,
665+
type_priv: type_priv,
666+
type_used: check_type} => {
667+
// This dance with found_error is because we don't want to report
668+
// a privacy error twice for the same directive.
669+
let found_error = match (type_priv, check_type) {
670+
(Some(resolve::DependsOn(def)), resolve::Used) => {
671+
!self.report_error(ck_public(def))
672+
},
673+
_ => false,
674+
};
675+
if !found_error {
676+
match (value_priv, check_value) {
677+
(Some(resolve::DependsOn(def)), resolve::Used) => {
678+
self.report_error(ck_public(def));
679+
},
680+
_ => {},
681+
}
682+
}
683+
// If an import is not used in either namespace, we still want to check
684+
// that it could be legal. Therefore we check in both namespaces and only
685+
// report an error if both would be illegal. We only report one error,
686+
// even if it is illegal to import from both namespaces.
687+
match (value_priv, check_value, type_priv, check_type) {
688+
(Some(p), resolve::Unused, None, _) |
689+
(None, _, Some(p), resolve::Unused) => {
690+
let p = match p {
691+
resolve::AllPublic => None,
692+
resolve::DependsOn(def) => ck_public(def),
693+
};
694+
if p.is_some() {
695+
self.report_error(p);
696+
}
697+
},
698+
(Some(v), resolve::Unused, Some(t), resolve::Unused) => {
699+
let v = match v {
700+
resolve::AllPublic => None,
701+
resolve::DependsOn(def) => ck_public(def),
702+
};
703+
let t = match t {
704+
resolve::AllPublic => None,
705+
resolve::DependsOn(def) => ck_public(def),
706+
};
707+
match (v, t) {
708+
(Some(_), Some(t)) => {
709+
self.report_error(Some(t));
710+
},
711+
_ => {},
712+
}
713+
},
714+
_ => {},
715+
}
716+
},
642717
}
643718
};
719+
// FIXME(#12334) Imports can refer to definitions in both the type and
720+
// value namespaces. The privacy information is aware of this, but the
721+
// def map is not. Therefore the names we work out below will not always
722+
// be accurate and we can get slightly wonky error messages (but type
723+
// checking is always correct).
644724
let def_map = self.tcx.def_map.borrow();
645725
match def_map.get().get_copy(&path_id) {
646726
ast::DefStaticMethod(..) => ck("static method"),
@@ -668,7 +748,7 @@ impl<'a> PrivacyVisitor<'a> {
668748
// is whether the trait itself is accessible or not.
669749
method_param(method_param { trait_id: trait_id, .. }) |
670750
method_object(method_object { trait_id: trait_id, .. }) => {
671-
self.ensure_public(span, trait_id, None, "source trait");
751+
self.report_error(self.ensure_public(span, trait_id, None, "source trait"));
672752
}
673753
}
674754
}

0 commit comments

Comments
 (0)