Skip to content

Commit 45f0ce7

Browse files
committed
Auto merge of #31920 - jseyfried:fix_spurious_privacy_error, r=nikomatsakis
This PR allows using methods from traits that are visible but are defined in an inaccessible module (fixes #18241). For example, ```rust mod foo { pub use foo::bar::Tr; mod bar { // This module is inaccessible from `g` pub trait Tr { fn f(&self) {} } } } fn g<T: foo::Tr>(t: T) { t.f(); // Currently, this is a privacy error even though `foo::Tr` is visible } ``` After this PR, it will continue to be a privacy error to use a method from a trait that is not visible. This can happen when a public trait inherits from a private trait (in violation of the `public_in_private` lint) -- see @petrochenkov's example in #28504. r? @nikomatsakis
2 parents 52e0bda + d908ff1 commit 45f0ce7

File tree

3 files changed

+67
-73
lines changed

3 files changed

+67
-73
lines changed

src/librustc_privacy/lib.rs

+36-72
Original file line numberDiff line numberDiff line change
@@ -492,11 +492,6 @@ enum FieldName {
492492
}
493493

494494
impl<'a, 'tcx> PrivacyVisitor<'a, 'tcx> {
495-
// used when debugging
496-
fn nodestr(&self, id: ast::NodeId) -> String {
497-
self.tcx.map.node_to_string(id).to_string()
498-
}
499-
500495
// Determines whether the given definition is public from the point of view
501496
// of the current item.
502497
fn def_privacy(&self, did: DefId) -> PrivacyResult {
@@ -604,75 +599,44 @@ impl<'a, 'tcx> PrivacyVisitor<'a, 'tcx> {
604599
return Allowable;
605600
}
606601

607-
// We now know that there is at least one private member between the
608-
// destination and the root.
609-
let mut closest_private_id = node_id;
610-
loop {
611-
debug!("privacy - examining {}", self.nodestr(closest_private_id));
612-
let vis = match self.tcx.map.find(closest_private_id) {
613-
// If this item is a method, then we know for sure that it's an
614-
// actual method and not a static method. The reason for this is
615-
// that these cases are only hit in the ExprMethodCall
616-
// expression, and ExprCall will have its path checked later
617-
// (the path of the trait/impl) if it's a static method.
618-
//
619-
// With this information, then we can completely ignore all
620-
// trait methods. The privacy violation would be if the trait
621-
// couldn't get imported, not if the method couldn't be used
622-
// (all trait methods are public).
623-
//
624-
// However, if this is an impl method, then we dictate this
625-
// decision solely based on the privacy of the method
626-
// invocation.
627-
// FIXME(#10573) is this the right behavior? Why not consider
628-
// where the method was defined?
629-
Some(ast_map::NodeImplItem(ii)) => {
630-
match ii.node {
631-
hir::ImplItemKind::Const(..) |
632-
hir::ImplItemKind::Method(..) => {
633-
let imp = self.tcx.map
634-
.get_parent_did(closest_private_id);
635-
match self.tcx.impl_trait_ref(imp) {
636-
Some(..) => return Allowable,
637-
_ if ii.vis == hir::Public => {
638-
return Allowable
639-
}
640-
_ => ii.vis
641-
}
642-
}
643-
hir::ImplItemKind::Type(_) => return Allowable,
644-
}
645-
}
646-
Some(ast_map::NodeTraitItem(_)) => {
647-
return Allowable;
602+
let vis = match self.tcx.map.find(node_id) {
603+
// If this item is a method, then we know for sure that it's an
604+
// actual method and not a static method. The reason for this is
605+
// that these cases are only hit in the ExprMethodCall
606+
// expression, and ExprCall will have its path checked later
607+
// (the path of the trait/impl) if it's a static method.
608+
//
609+
// With this information, then we can completely ignore all
610+
// trait methods. The privacy violation would be if the trait
611+
// couldn't get imported, not if the method couldn't be used
612+
// (all trait methods are public).
613+
//
614+
// However, if this is an impl method, then we dictate this
615+
// decision solely based on the privacy of the method
616+
// invocation.
617+
Some(ast_map::NodeImplItem(ii)) => {
618+
let imp = self.tcx.map.get_parent_did(node_id);
619+
match self.tcx.impl_trait_ref(imp) {
620+
Some(..) => hir::Public,
621+
_ => ii.vis,
648622
}
623+
}
624+
Some(ast_map::NodeTraitItem(_)) => hir::Public,
649625

650-
// This is not a method call, extract the visibility as one
651-
// would normally look at it
652-
Some(ast_map::NodeItem(it)) => it.vis,
653-
Some(ast_map::NodeForeignItem(_)) => {
654-
self.tcx.map.get_foreign_vis(closest_private_id)
655-
}
656-
Some(ast_map::NodeVariant(..)) => {
657-
hir::Public // need to move up a level (to the enum)
658-
}
659-
_ => hir::Public,
660-
};
661-
if vis != hir::Public { break }
662-
// if we've reached the root, then everything was allowable and this
663-
// access is public.
664-
if closest_private_id == ast::CRATE_NODE_ID { return Allowable }
665-
closest_private_id = *self.parents.get(&closest_private_id).unwrap();
666-
667-
// If we reached the top, then we were public all the way down and
668-
// we can allow this access.
669-
if closest_private_id == ast::DUMMY_NODE_ID { return Allowable }
670-
}
671-
debug!("privacy - closest priv {}", self.nodestr(closest_private_id));
672-
if self.private_accessible(closest_private_id) {
626+
// This is not a method call, extract the visibility as one
627+
// would normally look at it
628+
Some(ast_map::NodeItem(it)) => it.vis,
629+
Some(ast_map::NodeForeignItem(_)) => {
630+
self.tcx.map.get_foreign_vis(node_id)
631+
}
632+
_ => hir::Public,
633+
};
634+
if vis == hir::Public { return Allowable }
635+
636+
if self.private_accessible(node_id) {
673637
Allowable
674638
} else {
675-
DisallowedBy(closest_private_id)
639+
DisallowedBy(node_id)
676640
}
677641
}
678642

@@ -834,8 +798,8 @@ impl<'a, 'tcx> PrivacyVisitor<'a, 'tcx> {
834798
// Trait methods are always all public. The only controlling factor
835799
// is whether the trait itself is accessible or not.
836800
ty::TraitContainer(trait_def_id) => {
837-
self.report_error(self.ensure_public(span, trait_def_id,
838-
None, "source trait"));
801+
let msg = format!("source trait `{}`", self.tcx.item_path_str(trait_def_id));
802+
self.report_error(self.ensure_public(span, trait_def_id, None, &msg));
839803
}
840804
}
841805
}

src/test/compile-fail/trait-not-accessible.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ struct S;
2020
impl m::Pub for S {}
2121

2222
fn g<T: m::Pub>(arg: T) {
23-
arg.f(); //~ ERROR: source trait is private
23+
arg.f(); //~ ERROR: source trait `m::Priv` is private
2424
}
2525

2626
fn main() {
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(rustc_attrs)]
12+
#![allow(dead_code)]
13+
14+
mod foo {
15+
pub use self::bar::T;
16+
mod bar {
17+
pub trait T {
18+
fn f(&self) {}
19+
}
20+
impl T for () {}
21+
}
22+
}
23+
24+
fn g() {
25+
use foo::T;
26+
().f(); // Check that this does not trigger a privacy error
27+
}
28+
29+
#[rustc_error]
30+
fn main() {} //~ ERROR compilation successful

0 commit comments

Comments
 (0)