Skip to content

Commit 742fcc7

Browse files
committed
Auto merge of #57586 - Aaron1011:feature/pub-priv-dep, r=petrochenkov
Implement public/private dependency feature Implements #44663 The core implementation is done - however, there are a few issues that still need to be resolved: - [x] The `EXTERNAL_PRIVATE_DEPENDENCY` lint currently does notthing when the `public_private_dependencies` is not enabled. Should mentioning the lint (in an `allow` or `deny` attribute) be an error if the feature is not enabled? (Resolved- the feature was removed) - [x] Crates with the name `core` and `std` are always marked public, without the need to explcitily specify them on the command line. Is this what we want to do? Do we want to allow`no_std`/`no_core` crates to explicitly control this in some way? (Resolved - private crates are now explicitly specified) - [x] Should I add additional UI tests? (Resolved - added more tests) - [x] Does it make sense to be able to allow/deny the `EXTERNAL_PRIVATE_DEPENDENCY` on an individual item? (Resolved - this is implemented)
2 parents c9a8687 + 369faae commit 742fcc7

File tree

12 files changed

+183
-2
lines changed

12 files changed

+183
-2
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -2878,6 +2878,7 @@ dependencies = [
28782878
name = "rustc_privacy"
28792879
version = "0.0.0"
28802880
dependencies = [
2881+
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
28812882
"rustc 0.0.0",
28822883
"rustc_data_structures 0.0.0",
28832884
"rustc_typeck 0.0.0",

src/librustc/lint/builtin.rs

+7
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,12 @@ declare_lint! {
125125
"detect private items in public interfaces not caught by the old implementation"
126126
}
127127

128+
declare_lint! {
129+
pub EXPORTED_PRIVATE_DEPENDENCIES,
130+
Warn,
131+
"public interface leaks type from a private dependency"
132+
}
133+
128134
declare_lint! {
129135
pub PUB_USE_OF_PRIVATE_EXTERN_CRATE,
130136
Deny,
@@ -405,6 +411,7 @@ impl LintPass for HardwiredLints {
405411
TRIVIAL_CASTS,
406412
TRIVIAL_NUMERIC_CASTS,
407413
PRIVATE_IN_PUBLIC,
414+
EXPORTED_PRIVATE_DEPENDENCIES,
408415
PUB_USE_OF_PRIVATE_EXTERN_CRATE,
409416
INVALID_TYPE_PARAM_DEFAULT,
410417
CONST_ERR,

src/librustc/session/config.rs

+25-1
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,10 @@ top_level_options!(
411411
remap_path_prefix: Vec<(PathBuf, PathBuf)> [UNTRACKED],
412412

413413
edition: Edition [TRACKED],
414+
415+
// The list of crates to consider private when
416+
// checking leaked private dependency types in public interfaces
417+
extern_private: Vec<String> [TRACKED],
414418
}
415419
);
416420

@@ -606,6 +610,7 @@ impl Default for Options {
606610
cli_forced_thinlto_off: false,
607611
remap_path_prefix: Vec::new(),
608612
edition: DEFAULT_EDITION,
613+
extern_private: Vec::new()
609614
}
610615
}
611616
}
@@ -1724,6 +1729,12 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
17241729
"Specify where an external rust library is located",
17251730
"NAME=PATH",
17261731
),
1732+
opt::multi_s(
1733+
"",
1734+
"extern-private",
1735+
"Specify where an extern rust library is located, marking it as a private dependency",
1736+
"NAME=PATH",
1737+
),
17271738
opt::opt_s("", "sysroot", "Override the system root", "PATH"),
17281739
opt::multi("Z", "", "Set internal debugging options", "FLAG"),
17291740
opt::opt_s(
@@ -1905,6 +1916,7 @@ pub fn build_session_options_and_crate_config(
19051916
let crate_types = parse_crate_types_from_list(unparsed_crate_types)
19061917
.unwrap_or_else(|e| early_error(error_format, &e[..]));
19071918

1919+
19081920
let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format);
19091921

19101922
let mut debugging_opts = build_debugging_options(matches, error_format);
@@ -2218,8 +2230,18 @@ pub fn build_session_options_and_crate_config(
22182230
);
22192231
}
22202232

2233+
if matches.opt_present("extern-private") && !debugging_opts.unstable_options {
2234+
early_error(
2235+
ErrorOutputType::default(),
2236+
"'--extern-private' is unstable and only \
2237+
available for nightly builds of rustc."
2238+
)
2239+
}
2240+
2241+
let extern_private = matches.opt_strs("extern-private");
2242+
22212243
let mut externs: BTreeMap<_, BTreeSet<_>> = BTreeMap::new();
2222-
for arg in &matches.opt_strs("extern") {
2244+
for arg in matches.opt_strs("extern").into_iter().chain(matches.opt_strs("extern-private")) {
22232245
let mut parts = arg.splitn(2, '=');
22242246
let name = parts.next().unwrap_or_else(||
22252247
early_error(error_format, "--extern value must not be empty"));
@@ -2287,6 +2309,7 @@ pub fn build_session_options_and_crate_config(
22872309
cli_forced_thinlto_off: disable_thinlto,
22882310
remap_path_prefix,
22892311
edition,
2312+
extern_private
22902313
},
22912314
cfg,
22922315
)
@@ -2460,6 +2483,7 @@ mod dep_tracking {
24602483
impl_dep_tracking_hash_via_hash!(Option<usize>);
24612484
impl_dep_tracking_hash_via_hash!(Option<String>);
24622485
impl_dep_tracking_hash_via_hash!(Option<(String, u64)>);
2486+
impl_dep_tracking_hash_via_hash!(Option<Vec<String>>);
24632487
impl_dep_tracking_hash_via_hash!(Option<MergeFunctions>);
24642488
impl_dep_tracking_hash_via_hash!(Option<PanicStrategy>);
24652489
impl_dep_tracking_hash_via_hash!(Option<RelroLevel>);

src/librustc_privacy/Cargo.toml

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ rustc = { path = "../librustc" }
1313
rustc_typeck = { path = "../librustc_typeck" }
1414
syntax = { path = "../libsyntax" }
1515
syntax_pos = { path = "../libsyntax_pos" }
16-
rustc_data_structures = { path = "../librustc_data_structures" }
16+
rustc_data_structures = { path = "../librustc_data_structures" }
17+
log = "0.4"

src/librustc_privacy/lib.rs

+38
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#[macro_use] extern crate rustc;
1111
#[macro_use] extern crate syntax;
12+
#[macro_use] extern crate log;
1213
extern crate rustc_typeck;
1314
extern crate syntax_pos;
1415
extern crate rustc_data_structures;
@@ -1451,13 +1452,15 @@ impl<'a, 'tcx> Visitor<'tcx> for ObsoleteVisiblePrivateTypesVisitor<'a, 'tcx> {
14511452

14521453
struct SearchInterfaceForPrivateItemsVisitor<'a, 'tcx: 'a> {
14531454
tcx: TyCtxt<'a, 'tcx, 'tcx>,
1455+
item_id: ast::NodeId,
14541456
item_def_id: DefId,
14551457
span: Span,
14561458
/// The visitor checks that each component type is at least this visible.
14571459
required_visibility: ty::Visibility,
14581460
has_pub_restricted: bool,
14591461
has_old_errors: bool,
14601462
in_assoc_ty: bool,
1463+
private_crates: FxHashSet<CrateNum>
14611464
}
14621465

14631466
impl<'a, 'tcx: 'a> SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> {
@@ -1492,6 +1495,16 @@ impl<'a, 'tcx: 'a> SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> {
14921495
}
14931496

14941497
fn check_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool {
1498+
if self.leaks_private_dep(def_id) {
1499+
self.tcx.lint_node(lint::builtin::EXPORTED_PRIVATE_DEPENDENCIES,
1500+
self.item_id,
1501+
self.span,
1502+
&format!("{} `{}` from private dependency '{}' in public \
1503+
interface", kind, descr,
1504+
self.tcx.crate_name(def_id.krate)));
1505+
1506+
}
1507+
14951508
let node_id = match self.tcx.hir().as_local_node_id(def_id) {
14961509
Some(node_id) => node_id,
14971510
None => return false,
@@ -1514,9 +1527,23 @@ impl<'a, 'tcx: 'a> SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> {
15141527
self.tcx.lint_node(lint::builtin::PRIVATE_IN_PUBLIC, node_id, self.span,
15151528
&format!("{} (error {})", msg, err_code));
15161529
}
1530+
15171531
}
1532+
15181533
false
15191534
}
1535+
1536+
/// An item is 'leaked' from a private dependency if all
1537+
/// of the following are true:
1538+
/// 1. It's contained within a public type
1539+
/// 2. It comes from a private crate
1540+
fn leaks_private_dep(&self, item_id: DefId) -> bool {
1541+
let ret = self.required_visibility == ty::Visibility::Public &&
1542+
self.private_crates.contains(&item_id.krate);
1543+
1544+
debug!("leaks_private_dep(item_id={:?})={}", item_id, ret);
1545+
return ret;
1546+
}
15201547
}
15211548

15221549
impl<'a, 'tcx> DefIdVisitor<'a, 'tcx> for SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> {
@@ -1530,6 +1557,7 @@ struct PrivateItemsInPublicInterfacesVisitor<'a, 'tcx: 'a> {
15301557
tcx: TyCtxt<'a, 'tcx, 'tcx>,
15311558
has_pub_restricted: bool,
15321559
old_error_set: &'a NodeSet,
1560+
private_crates: FxHashSet<CrateNum>
15331561
}
15341562

15351563
impl<'a, 'tcx> PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> {
@@ -1560,12 +1588,14 @@ impl<'a, 'tcx> PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> {
15601588

15611589
SearchInterfaceForPrivateItemsVisitor {
15621590
tcx: self.tcx,
1591+
item_id,
15631592
item_def_id: self.tcx.hir().local_def_id(item_id),
15641593
span: self.tcx.hir().span(item_id),
15651594
required_visibility,
15661595
has_pub_restricted: self.has_pub_restricted,
15671596
has_old_errors,
15681597
in_assoc_ty: false,
1598+
private_crates: self.private_crates.clone()
15691599
}
15701600
}
15711601

@@ -1690,6 +1720,7 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Lrc<AccessLevels> {
16901720
fn check_mod_privacy<'tcx>(tcx: TyCtxt<'_, 'tcx, 'tcx>, module_def_id: DefId) {
16911721
let empty_tables = ty::TypeckTables::empty(None);
16921722

1723+
16931724
// Check privacy of names not checked in previous compilation stages.
16941725
let mut visitor = NamePrivacyVisitor {
16951726
tcx,
@@ -1725,6 +1756,12 @@ fn privacy_access_levels<'tcx>(
17251756
tcx.ensure().check_mod_privacy(tcx.hir().local_def_id(module));
17261757
}
17271758

1759+
let private_crates: FxHashSet<CrateNum> = tcx.sess.opts.extern_private.iter()
1760+
.flat_map(|c| {
1761+
tcx.crates().iter().find(|&&krate| &tcx.crate_name(krate) == c).cloned()
1762+
}).collect();
1763+
1764+
17281765
// Build up a set of all exported items in the AST. This is a set of all
17291766
// items which are reachable from external crates based on visibility.
17301767
let mut visitor = EmbargoVisitor {
@@ -1767,6 +1804,7 @@ fn privacy_access_levels<'tcx>(
17671804
tcx,
17681805
has_pub_restricted,
17691806
old_error_set: &visitor.old_error_set,
1807+
private_crates
17701808
};
17711809
krate.visit_all_item_likes(&mut DeepVisitor::new(&mut visitor));
17721810
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub struct PubType;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// This test is different from other feature gate tests.
2+
// Instead of checking that an error occurs without the feature gate,
3+
// it checks that *no* errors/warnings occurs without the feature gate.
4+
// This is due to the fact that 'public_private_dependencies' just enables
5+
// a lint, so disabling it shouldn't cause any code to stop compiling.
6+
7+
// run-pass
8+
// aux-build:pub_dep.rs
9+
10+
// Without ![feature(public_private_dependencies)],
11+
// this should do nothing/
12+
#![deny(exported_private_dependencies)]
13+
14+
extern crate pub_dep;
15+
16+
pub struct Foo {
17+
pub field: pub_dep::PubType
18+
}
19+
20+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
pub struct OtherType;
2+
pub trait OtherTrait {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub struct PubType;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// aux-build:priv_dep.rs
2+
// aux-build:pub_dep.rs
3+
// compile-flags: --extern-private priv_dep
4+
#![deny(exported_private_dependencies)]
5+
6+
// This crate is a private dependency
7+
extern crate priv_dep;
8+
// This crate is a public dependenct
9+
extern crate pub_dep;
10+
11+
use priv_dep::{OtherType, OtherTrait};
12+
use pub_dep::PubType;
13+
14+
// Type from private dependency used in private
15+
// type - this is fine
16+
struct PrivateType {
17+
field: OtherType
18+
}
19+
20+
pub struct PublicType {
21+
pub field: OtherType,
22+
//~^ ERROR type `priv_dep::OtherType` from private dependency 'priv_dep' in public interface
23+
priv_field: OtherType, // Private field - this is fine
24+
pub other_field: PubType // Type from public dependency - this is fine
25+
}
26+
27+
impl PublicType {
28+
pub fn pub_fn(param: OtherType) {}
29+
//~^ ERROR type `priv_dep::OtherType` from private dependency 'priv_dep' in public interface
30+
31+
fn priv_fn(param: OtherType) {}
32+
}
33+
34+
pub trait MyPubTrait {
35+
type Foo: OtherTrait;
36+
}
37+
//~^^^ ERROR trait `priv_dep::OtherTrait` from private dependency 'priv_dep' in public interface
38+
39+
pub struct AllowedPrivType {
40+
#[allow(exported_private_dependencies)]
41+
pub allowed: OtherType
42+
}
43+
44+
45+
46+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
error: type `priv_dep::OtherType` from private dependency 'priv_dep' in public interface
2+
--> $DIR/pub-priv1.rs:21:5
3+
|
4+
LL | pub field: OtherType,
5+
| ^^^^^^^^^^^^^^^^^^^^
6+
|
7+
note: lint level defined here
8+
--> $DIR/pub-priv1.rs:4:9
9+
|
10+
LL | #![deny(exported_private_dependencies)]
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
13+
error: type `priv_dep::OtherType` from private dependency 'priv_dep' in public interface
14+
--> $DIR/pub-priv1.rs:28:5
15+
|
16+
LL | pub fn pub_fn(param: OtherType) {}
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
18+
19+
error: trait `priv_dep::OtherTrait` from private dependency 'priv_dep' in public interface
20+
--> $DIR/pub-priv1.rs:34:1
21+
|
22+
LL | / pub trait MyPubTrait {
23+
LL | | type Foo: OtherTrait;
24+
LL | | }
25+
| |_^
26+
27+
error: aborting due to 3 previous errors
28+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// The 'std' crates should always be implicitly public,
2+
// without having to pass any compiler arguments
3+
4+
// run-pass
5+
6+
#![deny(exported_private_dependencies)]
7+
8+
pub struct PublicType {
9+
pub field: Option<u8>
10+
}
11+
12+
fn main() {}

0 commit comments

Comments
 (0)