Skip to content

Commit 8b6f3dd

Browse files
committed
Initial implementation work
1 parent 1aa2506 commit 8b6f3dd

File tree

6 files changed

+164
-5
lines changed

6 files changed

+164
-5
lines changed

src/librustc/lint/builtin.rs

Lines changed: 6 additions & 0 deletions
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 LEAKED_PRIVATE_DEPENDENCY,
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,

src/librustc/session/config.rs

Lines changed: 24 additions & 0 deletions
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 public for
416+
// checking leaked private dependency types in public interfaces
417+
extern_public: FxHashSet<String> [UNTRACKED],
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_public: FxHashSet::default()
609614
}
610615
}
611616
}
@@ -1648,6 +1653,13 @@ pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> {
16481653
for the compiler to emit",
16491654
"[bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]",
16501655
),
1656+
opt::multi_s(
1657+
"",
1658+
"extern-public",
1659+
"Comma separated list of crates to consider 'public'
1660+
for linting purposes",
1661+
"CRATES",
1662+
),
16511663
opt::opt_s(
16521664
"",
16531665
"crate-name",
@@ -1905,6 +1917,17 @@ pub fn build_session_options_and_crate_config(
19051917
let crate_types = parse_crate_types_from_list(unparsed_crate_types)
19061918
.unwrap_or_else(|e| early_error(error_format, &e[..]));
19071919

1920+
if matches.opt_present("extern-public") && !nightly_options::is_nightly_build() {
1921+
early_error(
1922+
ErrorOutputType::default(),
1923+
"'--extern-public' is unstable and only \
1924+
available for nightly builds of rustc."
1925+
)
1926+
}
1927+
1928+
let extern_public: FxHashSet<String> = matches.opt_strs("extern-public").
1929+
iter().cloned().collect();
1930+
19081931
let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format);
19091932

19101933
let mut debugging_opts = build_debugging_options(matches, error_format);
@@ -2287,6 +2310,7 @@ pub fn build_session_options_and_crate_config(
22872310
cli_forced_thinlto_off: disable_thinlto,
22882311
remap_path_prefix,
22892312
edition,
2313+
extern_public
22902314
},
22912315
cfg,
22922316
)

src/librustc_lint/builtin.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1883,3 +1883,5 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ExplicitOutlivesRequirements {
18831883
}
18841884

18851885
}
1886+
1887+

src/librustc_lint/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,11 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
229229
reference: "issue #34537 <https://github.com/rust-lang/rust/issues/34537>",
230230
edition: None,
231231
},
232+
FutureIncompatibleInfo {
233+
id: LintId::of(LEAKED_PRIVATE_DEPENDENCY),
234+
reference: "issue #44663 <https://github.com/rust-lang/rust/issues/44663>",
235+
edition: None,
236+
},
232237
FutureIncompatibleInfo {
233238
id: LintId::of(PUB_USE_OF_PRIVATE_EXTERN_CRATE),
234239
reference: "issue #34537 <https://github.com/rust-lang/rust/issues/34537>",

src/librustc_privacy/lib.rs

Lines changed: 124 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1458,6 +1458,7 @@ struct SearchInterfaceForPrivateItemsVisitor<'a, 'tcx: 'a> {
14581458
has_pub_restricted: bool,
14591459
has_old_errors: bool,
14601460
in_assoc_ty: bool,
1461+
public_crates: FxHashSet<CrateNum>
14611462
}
14621463

14631464
impl<'a, 'tcx: 'a> SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> {
@@ -1514,22 +1515,134 @@ impl<'a, 'tcx: 'a> SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> {
15141515
self.tcx.lint_node(lint::builtin::PRIVATE_IN_PUBLIC, node_id, self.span,
15151516
&format!("{} (error {})", msg, err_code));
15161517
}
1518+
1519+
if self.leaks_private_dep(trait_ref.def_id) {
1520+
self.tcx.lint_node(lint::builtin::LEAKED_PRIVATE_DEPENDENCY,
1521+
node_id,
1522+
self.span,
1523+
&format!("trait `{}` from private dependency '{}' in public \
1524+
interface", trait_ref,
1525+
trait_ref.def_id.krate));
1526+
1527+
}
15171528
}
1518-
false
1529+
}
1530+
1531+
/// An item is 'leaked' from a private dependency if all
1532+
/// of the following are true:
1533+
/// 1. It's contained within a public type
1534+
/// 2. It does not come from a crate marked as public
1535+
fn leaks_private_dep(&self, item_id: DefId) -> bool {
1536+
// Never do any leak checking if the feature is not enabled
1537+
if !self.tcx.features().public_private_dependencies {
1538+
return false
1539+
}
1540+
self.required_visibility == ty::Visibility::Public &&
1541+
!item_id.is_local() &&
1542+
!self.public_crates.contains(&item_id.krate)
15191543
}
15201544
}
15211545

1522-
impl<'a, 'tcx> DefIdVisitor<'a, 'tcx> for SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> {
1523-
fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> { self.tcx }
1524-
fn visit_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool {
1525-
self.check_def_id(def_id, kind, descr)
1546+
impl<'a, 'tcx: 'a> TypeVisitor<'tcx> for SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> {
1547+
fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
1548+
let ty_def_id = match ty.sty {
1549+
ty::Adt(adt, _) => Some(adt.did),
1550+
ty::Foreign(did) => Some(did),
1551+
ty::Dynamic(ref obj, ..) => Some(obj.principal().def_id()),
1552+
ty::Projection(ref proj) => {
1553+
if self.required_visibility == ty::Visibility::Invisible {
1554+
// Conservatively approximate the whole type alias as public without
1555+
// recursing into its components when determining impl publicity.
1556+
// For example, `impl <Type as Trait>::Alias {...}` may be a public impl
1557+
// even if both `Type` and `Trait` are private.
1558+
// Ideally, associated types should be substituted in the same way as
1559+
// free type aliases, but this isn't done yet.
1560+
return false;
1561+
}
1562+
let trait_ref = proj.trait_ref(self.tcx);
1563+
Some(trait_ref.def_id)
1564+
}
1565+
_ => None
1566+
};
1567+
1568+
if let Some(def_id) = ty_def_id {
1569+
// Non-local means public (private items can't leave their crate, modulo bugs).
1570+
if let Some(node_id) = self.tcx.hir().as_local_node_id(def_id) {
1571+
let hir_vis = match self.tcx.hir().find(node_id) {
1572+
Some(Node::Item(item)) => &item.vis,
1573+
Some(Node::ForeignItem(item)) => &item.vis,
1574+
_ => bug!("expected item of foreign item"),
1575+
};
1576+
1577+
let vis = ty::Visibility::from_hir(hir_vis, node_id, self.tcx);
1578+
1579+
if !vis.is_at_least(self.min_visibility, self.tcx) {
1580+
self.min_visibility = vis;
1581+
}
1582+
if !vis.is_at_least(self.required_visibility, self.tcx) {
1583+
let vis_adj = match hir_vis.node {
1584+
hir::VisibilityKind::Crate(_) => "crate-visible",
1585+
hir::VisibilityKind::Restricted { .. } => "restricted",
1586+
_ => "private"
1587+
};
1588+
1589+
if self.has_pub_restricted || self.has_old_errors || self.in_assoc_ty {
1590+
let mut err = struct_span_err!(self.tcx.sess, self.span, E0446,
1591+
"{} type `{}` in public interface", vis_adj, ty);
1592+
err.span_label(self.span, format!("can't leak {} type", vis_adj));
1593+
err.span_label(hir_vis.span, format!("`{}` declared as {}", ty, vis_adj));
1594+
err.emit();
1595+
} else {
1596+
self.tcx.lint_node(lint::builtin::PRIVATE_IN_PUBLIC,
1597+
node_id,
1598+
self.span,
1599+
&format!("{} type `{}` in public \
1600+
interface (error E0446)", vis_adj, ty));
1601+
}
1602+
}
1603+
1604+
if self.leaks_private_dep(def_id) {
1605+
self.tcx.lint_node(lint::builtin::LEAKED_PRIVATE_DEPENDENCY,
1606+
node_id,
1607+
self.span,
1608+
&format!("type '{}' from private dependency '{}' in \
1609+
public interface", ty, def_id.krate));
1610+
}
1611+
}
1612+
}
1613+
1614+
ty.super_visit_with(self)
1615+
}
1616+
}
1617+
1618+
/*struct LeakedPrivateDependenciesVisitor<'a, 'tcx: 'a> {
1619+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
1620+
public_crates: FxHashSet<CrateNum>
1621+
}
1622+
1623+
impl<'a, 'tcx> LeakedPrivateDependenciesVisitor<'a, 'tcx> {
1624+
fn is_private_dep(&self, item_id: DefId) {
1625+
!item_id.is_local() && !self.public_crates.contains(item_id.krate)
15261626
}
1627+
15271628
}
15281629
1630+
impl<'a, 'tcx> Visitor<'tcx> for LeakedPrivateDependenciesVisitor<'a, 'tcx> {
1631+
fn nested_visit_map<'this>(&'this mut self) -> nestedvisitormap<'this, 'tcx> {
1632+
nestedvisitormap::onlybodies(&self.tcx.hir())
1633+
}
1634+
1635+
fn visit_item(&mut self, item: &'tcx hir::Item) {
1636+
1637+
}
1638+
1639+
}*/
1640+
15291641
struct PrivateItemsInPublicInterfacesVisitor<'a, 'tcx: 'a> {
15301642
tcx: TyCtxt<'a, 'tcx, 'tcx>,
15311643
has_pub_restricted: bool,
15321644
old_error_set: &'a NodeSet,
1645+
public_crates: FxHashSet<CrateNum>
15331646
}
15341647

15351648
impl<'a, 'tcx> PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> {
@@ -1566,6 +1679,7 @@ impl<'a, 'tcx> PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> {
15661679
has_pub_restricted: self.has_pub_restricted,
15671680
has_old_errors,
15681681
in_assoc_ty: false,
1682+
public_crates: self.public_crates.clone()
15691683
}
15701684
}
15711685

@@ -1690,6 +1804,10 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Lrc<AccessLevels> {
16901804
fn check_mod_privacy<'tcx>(tcx: TyCtxt<'_, 'tcx, 'tcx>, module_def_id: DefId) {
16911805
let empty_tables = ty::TypeckTables::empty(None);
16921806

1807+
let public_crates: FxHashSet<CrateNum> = tcx.sess.opts.extern_public.iter().flat_map(|c| {
1808+
tcx.crates().iter().find(|&&krate| &tcx.crate_name(krate) == c).cloned()
1809+
}).collect();
1810+
16931811
// Check privacy of names not checked in previous compilation stages.
16941812
let mut visitor = NamePrivacyVisitor {
16951813
tcx,
@@ -1767,6 +1885,7 @@ fn privacy_access_levels<'tcx>(
17671885
tcx,
17681886
has_pub_restricted,
17691887
old_error_set: &visitor.old_error_set,
1888+
public_crates
17701889
};
17711890
krate.visit_all_item_likes(&mut DeepVisitor::new(&mut visitor));
17721891
}

src/libsyntax/feature_gate.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,9 @@ declare_features! (
462462

463463
// #[optimize(X)]
464464
(active, optimize_attribute, "1.34.0", Some(54882), None),
465+
466+
// Allows using the 'leaked private dependencies' lint
467+
(active, public_private_dependencies, "1.32.0", Some(44663), None),
465468
);
466469

467470
declare_features! (

0 commit comments

Comments
 (0)