about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAaron Hill <aa1ronham@gmail.com>2018-12-15 07:00:15 -0500
committerAaron Hill <aa1ronham@gmail.com>2019-02-01 09:43:55 -0500
commit8b6f3ddf2347509f9ff90c34e48673a25624d6e5 (patch)
tree33e1088a025f84a55b2dca68eca912874e98e113
parent1aa250635e9fd2646116726c88a31c15eb4ab74e (diff)
downloadrust-8b6f3ddf2347509f9ff90c34e48673a25624d6e5.tar.gz
rust-8b6f3ddf2347509f9ff90c34e48673a25624d6e5.zip
Initial implementation work
-rw-r--r--src/librustc/lint/builtin.rs6
-rw-r--r--src/librustc/session/config.rs24
-rw-r--r--src/librustc_lint/builtin.rs2
-rw-r--r--src/librustc_lint/lib.rs5
-rw-r--r--src/librustc_privacy/lib.rs129
-rw-r--r--src/libsyntax/feature_gate.rs3
6 files changed, 164 insertions, 5 deletions
diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs
index 35a03823595..16f7f9903f7 100644
--- a/src/librustc/lint/builtin.rs
+++ b/src/librustc/lint/builtin.rs
@@ -126,6 +126,12 @@ declare_lint! {
 }
 
 declare_lint! {
+    pub LEAKED_PRIVATE_DEPENDENCY,
+    Warn,
+    "public interface leaks type from a private dependency"
+}
+
+declare_lint! {
     pub PUB_USE_OF_PRIVATE_EXTERN_CRATE,
     Deny,
     "detect public re-exports of private extern crates"
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index 4b1aefb2216..5c2aa882a06 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -411,6 +411,10 @@ top_level_options!(
         remap_path_prefix: Vec<(PathBuf, PathBuf)> [UNTRACKED],
 
         edition: Edition [TRACKED],
+
+        // The list of crates to consider public for
+        // checking leaked private dependency types in public interfaces
+        extern_public: FxHashSet<String> [UNTRACKED],
     }
 );
 
@@ -606,6 +610,7 @@ impl Default for Options {
             cli_forced_thinlto_off: false,
             remap_path_prefix: Vec::new(),
             edition: DEFAULT_EDITION,
+            extern_public: FxHashSet::default()
         }
     }
 }
@@ -1648,6 +1653,13 @@ pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> {
                                     for the compiler to emit",
             "[bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]",
         ),
+        opt::multi_s(
+            "",
+            "extern-public",
+            "Comma separated list of crates to consider 'public'
+                                     for linting purposes",
+            "CRATES",
+        ),
         opt::opt_s(
             "",
             "crate-name",
@@ -1905,6 +1917,17 @@ pub fn build_session_options_and_crate_config(
     let crate_types = parse_crate_types_from_list(unparsed_crate_types)
         .unwrap_or_else(|e| early_error(error_format, &e[..]));
 
+    if matches.opt_present("extern-public") && !nightly_options::is_nightly_build() {
+        early_error(
+            ErrorOutputType::default(),
+            "'--extern-public' is unstable and only \
+            available for nightly builds of rustc."
+        )
+    }
+
+    let extern_public: FxHashSet<String> = matches.opt_strs("extern-public").
+        iter().cloned().collect();
+
     let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format);
 
     let mut debugging_opts = build_debugging_options(matches, error_format);
@@ -2287,6 +2310,7 @@ pub fn build_session_options_and_crate_config(
             cli_forced_thinlto_off: disable_thinlto,
             remap_path_prefix,
             edition,
+            extern_public
         },
         cfg,
     )
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index 46e784c4099..b0846822b24 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -1883,3 +1883,5 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ExplicitOutlivesRequirements {
     }
 
 }
+
+
diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs
index 6607951d2cd..e2e63b418c7 100644
--- a/src/librustc_lint/lib.rs
+++ b/src/librustc_lint/lib.rs
@@ -230,6 +230,11 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
             edition: None,
         },
         FutureIncompatibleInfo {
+            id: LintId::of(LEAKED_PRIVATE_DEPENDENCY),
+            reference: "issue #44663 <https://github.com/rust-lang/rust/issues/44663>",
+            edition: None,
+        },
+        FutureIncompatibleInfo {
             id: LintId::of(PUB_USE_OF_PRIVATE_EXTERN_CRATE),
             reference: "issue #34537 <https://github.com/rust-lang/rust/issues/34537>",
             edition: None,
diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs
index dcbb9ff4a75..c7cdb30521b 100644
--- a/src/librustc_privacy/lib.rs
+++ b/src/librustc_privacy/lib.rs
@@ -1458,6 +1458,7 @@ struct SearchInterfaceForPrivateItemsVisitor<'a, 'tcx: 'a> {
     has_pub_restricted: bool,
     has_old_errors: bool,
     in_assoc_ty: bool,
+    public_crates: FxHashSet<CrateNum>
 }
 
 impl<'a, 'tcx: 'a> SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> {
@@ -1514,22 +1515,134 @@ impl<'a, 'tcx: 'a> SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> {
                 self.tcx.lint_node(lint::builtin::PRIVATE_IN_PUBLIC, node_id, self.span,
                                    &format!("{} (error {})", msg, err_code));
             }
+
+            if self.leaks_private_dep(trait_ref.def_id) {
+                self.tcx.lint_node(lint::builtin::LEAKED_PRIVATE_DEPENDENCY,
+                                   node_id,
+                                   self.span,
+                                   &format!("trait `{}` from private dependency '{}' in public \
+                                             interface", trait_ref,
+                                             trait_ref.def_id.krate));
+
+            }
         }
-        false
+    }
+
+    /// An item is 'leaked' from a private dependency if all
+    /// of the following are true:
+    /// 1. It's contained within a public type
+    /// 2. It does not come from a crate marked as public
+    fn leaks_private_dep(&self, item_id: DefId) -> bool {
+        // Never do any leak checking if the feature is not enabled
+        if !self.tcx.features().public_private_dependencies {
+            return false
+        }
+        self.required_visibility == ty::Visibility::Public &&
+            !item_id.is_local() &&
+            !self.public_crates.contains(&item_id.krate)
     }
 }
 
-impl<'a, 'tcx> DefIdVisitor<'a, 'tcx> for SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> {
-    fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> { self.tcx }
-    fn visit_def_id(&mut self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool {
-        self.check_def_id(def_id, kind, descr)
+impl<'a, 'tcx: 'a> TypeVisitor<'tcx> for SearchInterfaceForPrivateItemsVisitor<'a, 'tcx> {
+    fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
+        let ty_def_id = match ty.sty {
+            ty::Adt(adt, _) => Some(adt.did),
+            ty::Foreign(did) => Some(did),
+            ty::Dynamic(ref obj, ..) => Some(obj.principal().def_id()),
+            ty::Projection(ref proj) => {
+                if self.required_visibility == ty::Visibility::Invisible {
+                    // Conservatively approximate the whole type alias as public without
+                    // recursing into its components when determining impl publicity.
+                    // For example, `impl <Type as Trait>::Alias {...}` may be a public impl
+                    // even if both `Type` and `Trait` are private.
+                    // Ideally, associated types should be substituted in the same way as
+                    // free type aliases, but this isn't done yet.
+                    return false;
+                }
+                let trait_ref = proj.trait_ref(self.tcx);
+                Some(trait_ref.def_id)
+            }
+            _ => None
+        };
+
+        if let Some(def_id) = ty_def_id {
+            // Non-local means public (private items can't leave their crate, modulo bugs).
+            if let Some(node_id) = self.tcx.hir().as_local_node_id(def_id) {
+                let hir_vis = match self.tcx.hir().find(node_id) {
+                    Some(Node::Item(item)) => &item.vis,
+                    Some(Node::ForeignItem(item)) => &item.vis,
+                    _ => bug!("expected item of foreign item"),
+                };
+
+                let vis = ty::Visibility::from_hir(hir_vis, node_id, self.tcx);
+
+                if !vis.is_at_least(self.min_visibility, self.tcx) {
+                    self.min_visibility = vis;
+                }
+                if !vis.is_at_least(self.required_visibility, self.tcx) {
+                    let vis_adj = match hir_vis.node {
+                        hir::VisibilityKind::Crate(_) => "crate-visible",
+                        hir::VisibilityKind::Restricted { .. } => "restricted",
+                        _ => "private"
+                    };
+
+                    if self.has_pub_restricted || self.has_old_errors || self.in_assoc_ty {
+                        let mut err = struct_span_err!(self.tcx.sess, self.span, E0446,
+                            "{} type `{}` in public interface", vis_adj, ty);
+                        err.span_label(self.span, format!("can't leak {} type", vis_adj));
+                        err.span_label(hir_vis.span, format!("`{}` declared as {}", ty, vis_adj));
+                        err.emit();
+                    } else {
+                        self.tcx.lint_node(lint::builtin::PRIVATE_IN_PUBLIC,
+                                           node_id,
+                                           self.span,
+                                           &format!("{} type `{}` in public \
+                                                     interface (error E0446)", vis_adj, ty));
+                    }
+                }
+
+                if self.leaks_private_dep(def_id) {
+                    self.tcx.lint_node(lint::builtin::LEAKED_PRIVATE_DEPENDENCY,
+                                       node_id,
+                                       self.span,
+                                       &format!("type '{}' from private dependency '{}' in \
+                                                public interface", ty, def_id.krate));
+                }
+            }
+        }
+
+        ty.super_visit_with(self)
+    }
+}
+
+/*struct LeakedPrivateDependenciesVisitor<'a, 'tcx: 'a> {
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    public_crates: FxHashSet<CrateNum>
+}
+
+impl<'a, 'tcx> LeakedPrivateDependenciesVisitor<'a, 'tcx> {
+    fn is_private_dep(&self, item_id: DefId) {
+        !item_id.is_local() && !self.public_crates.contains(item_id.krate)
     }
+
 }
 
+impl<'a, 'tcx> Visitor<'tcx> for LeakedPrivateDependenciesVisitor<'a, 'tcx> {
+    fn nested_visit_map<'this>(&'this mut self) -> nestedvisitormap<'this, 'tcx> {
+        nestedvisitormap::onlybodies(&self.tcx.hir())
+    }
+
+    fn visit_item(&mut self, item: &'tcx hir::Item) {
+
+    }
+
+}*/
+
 struct PrivateItemsInPublicInterfacesVisitor<'a, 'tcx: 'a> {
     tcx: TyCtxt<'a, 'tcx, 'tcx>,
     has_pub_restricted: bool,
     old_error_set: &'a NodeSet,
+    public_crates: FxHashSet<CrateNum>
 }
 
 impl<'a, 'tcx> PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> {
@@ -1566,6 +1679,7 @@ impl<'a, 'tcx> PrivateItemsInPublicInterfacesVisitor<'a, 'tcx> {
             has_pub_restricted: self.has_pub_restricted,
             has_old_errors,
             in_assoc_ty: false,
+            public_crates: self.public_crates.clone()
         }
     }
 
@@ -1690,6 +1804,10 @@ pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Lrc<AccessLevels> {
 fn check_mod_privacy<'tcx>(tcx: TyCtxt<'_, 'tcx, 'tcx>, module_def_id: DefId) {
     let empty_tables = ty::TypeckTables::empty(None);
 
+    let public_crates: FxHashSet<CrateNum> = tcx.sess.opts.extern_public.iter().flat_map(|c| {
+        tcx.crates().iter().find(|&&krate| &tcx.crate_name(krate) == c).cloned()
+    }).collect();
+
     // Check privacy of names not checked in previous compilation stages.
     let mut visitor = NamePrivacyVisitor {
         tcx,
@@ -1767,6 +1885,7 @@ fn privacy_access_levels<'tcx>(
             tcx,
             has_pub_restricted,
             old_error_set: &visitor.old_error_set,
+            public_crates
         };
         krate.visit_all_item_likes(&mut DeepVisitor::new(&mut visitor));
     }
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 9dd17b420aa..55eb6723188 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -462,6 +462,9 @@ declare_features! (
 
     // #[optimize(X)]
     (active, optimize_attribute, "1.34.0", Some(54882), None),
+
+    // Allows using the 'leaked private dependencies' lint
+    (active, public_private_dependencies, "1.32.0", Some(44663), None),
 );
 
 declare_features! (