Skip to content

Commit 79c63fb

Browse files
committed
fix: Calculate privacy of dependents wrt current local crate
1 parent b87eda7 commit 79c63fb

File tree

10 files changed

+154
-49
lines changed

10 files changed

+154
-49
lines changed

compiler/rustc_metadata/src/creader.rs

+50-34
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ use tracing::{debug, info, trace};
3939
use crate::errors;
4040
use crate::locator::{CrateError, CrateLocator, CratePaths};
4141
use crate::rmeta::{
42-
CrateDep, CrateMetadata, CrateNumMap, CrateRoot, MetadataBlob, TargetModifiers,
42+
CrateDep, CrateMetadata, CrateNumMap, CrateRoot, DepPrivacy, MetadataBlob, TargetModifiers,
4343
};
4444

4545
/// The backend's way to give the crate store access to the metadata in a library.
@@ -162,6 +162,14 @@ impl<'a> std::fmt::Debug for CrateDump<'a> {
162162
}
163163
}
164164

165+
#[derive(Clone, Copy)]
166+
struct DepOf<'b> {
167+
dep_root: &'b CratePaths,
168+
parent: CrateNum,
169+
privacy_of_parent: DepPrivacy,
170+
dep: &'b CrateDep,
171+
}
172+
165173
impl CStore {
166174
pub fn from_tcx(tcx: TyCtxt<'_>) -> FreezeReadGuard<'_, CStore> {
167175
FreezeReadGuard::map(tcx.untracked().cstore.read(), |cstore| {
@@ -493,36 +501,41 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
493501
/// Sometimes the directly dependent crate is not specified by `--extern`, in this case,
494502
/// `private-dep` is none during loading. This is equivalent to the scenario where the
495503
/// command parameter is set to `public-dependency`
496-
fn is_private_dep(
497-
&self,
498-
name: Symbol,
499-
private_dep: Option<bool>,
500-
dep_root: Option<&CratePaths>,
501-
) -> bool {
504+
fn privacy_of_dep(&self, name: Symbol, dep_of: Option<DepOf<'_>>) -> DepPrivacy {
502505
// Standard library crates are never private.
503506
if STDLIB_STABLE_CRATES.contains(&name) {
504507
tracing::info!("returning false for {name} is private");
505-
return false;
508+
return DepPrivacy::Direct(false);
506509
}
507510

508511
let extern_private = self.sess.opts.externs.get(name.as_str()).map(|e| e.is_private_dep);
509512

510513
// Any descendants of `std` should be private. These crates are usually not marked
511514
// private in metadata, so we ignore that field.
512515
if extern_private.is_none()
513-
&& let Some(dep) = dep_root
516+
&& let Some(dep) = dep_of.map(|d| d.dep_root)
514517
&& STDLIB_STABLE_CRATES.contains(&dep.name)
515518
{
516-
return true;
519+
return DepPrivacy::Direct(true);
517520
}
518521

519-
match (extern_private, private_dep) {
520-
// Explicit non-private via `--extern`, explicit non-private from metadata, or
521-
// unspecified with default to public.
522-
(Some(false), _) | (_, Some(false)) | (None, None) => false,
523-
// Marked private via `--extern priv:mycrate` or in metadata.
524-
(Some(true) | None, Some(true) | None) => true,
525-
}
522+
let dep_privacy = if let Some(dep_of) = dep_of {
523+
let is_private = dep_of.dep.is_private;
524+
if dep_of.parent == LOCAL_CRATE {
525+
DepPrivacy::Direct(is_private)
526+
} else {
527+
match dep_of.privacy_of_parent {
528+
DepPrivacy::Direct(false) if !is_private => DepPrivacy::TransitivePublic,
529+
DepPrivacy::TransitivePublic if !is_private => DepPrivacy::TransitivePublic,
530+
DepPrivacy::ExternCrate if !is_private => DepPrivacy::TransitivePublic,
531+
_ => DepPrivacy::TransitivePrivate,
532+
}
533+
}
534+
} else {
535+
DepPrivacy::ExternCrate
536+
};
537+
538+
extern_private.map_or(dep_privacy, |private| dep_privacy.merge(DepPrivacy::Direct(private)))
526539
}
527540

528541
fn register_crate(
@@ -532,25 +545,24 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
532545
lib: Library,
533546
dep_kind: CrateDepKind,
534547
name: Symbol,
535-
private_dep: Option<bool>,
548+
dep_privacy: DepPrivacy,
536549
) -> Result<CrateNum, CrateError> {
537550
let _prof_timer =
538551
self.sess.prof.generic_activity_with_arg("metadata_register_crate", name.as_str());
539552

540553
let Library { source, metadata } = lib;
541554
let crate_root = metadata.get_root();
542555
let host_hash = host_lib.as_ref().map(|lib| lib.metadata.get_root().hash());
543-
let private_dep = self.is_private_dep(name, private_dep, dep_root);
544556

545557
// Claim this crate number and cache it
546558
let feed = self.cstore.intern_stable_crate_id(&crate_root, self.tcx)?;
547559
let cnum = feed.key();
548560

549561
info!(
550-
"register crate `{}` (cnum = {}. private_dep = {})",
562+
"register crate `{}` (cnum = {}. dep_privacy = {:?})",
551563
crate_root.name(),
552564
cnum,
553-
private_dep
565+
dep_privacy
554566
);
555567

556568
// Maintain a reference to the top most crate.
@@ -563,7 +575,8 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
563575
&crate_paths
564576
};
565577

566-
let cnum_map = self.resolve_crate_deps(dep_root, &crate_root, &metadata, cnum, dep_kind)?;
578+
let cnum_map =
579+
self.resolve_crate_deps(dep_root, &crate_root, &metadata, cnum, dep_kind, dep_privacy)?;
567580

568581
let raw_proc_macros = if crate_root.is_proc_macro_crate() {
569582
let temp_root;
@@ -590,7 +603,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
590603
cnum_map,
591604
dep_kind,
592605
source,
593-
private_dep,
606+
dep_privacy,
594607
host_hash,
595608
);
596609

@@ -681,24 +694,24 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
681694
}
682695
}
683696

684-
fn maybe_resolve_crate<'b>(
685-
&'b mut self,
697+
fn maybe_resolve_crate(
698+
&mut self,
686699
name: Symbol,
687700
mut dep_kind: CrateDepKind,
688-
dep_of: Option<(&'b CratePaths, &'b CrateDep)>,
701+
dep_of: Option<DepOf<'_>>,
689702
) -> Result<CrateNum, CrateError> {
690703
info!("resolving crate `{}`", name);
691704
if !name.as_str().is_ascii() {
692705
return Err(CrateError::NonAsciiName(name));
693706
}
694707

695-
let dep_root = dep_of.map(|d| d.0);
696-
let dep = dep_of.map(|d| d.1);
708+
let dep_root = dep_of.map(|d| d.dep_root);
709+
let dep = dep_of.map(|d| d.dep);
697710
let hash = dep.map(|d| d.hash);
698711
let host_hash = dep.map(|d| d.host_hash).flatten();
699712
let extra_filename = dep.map(|d| &d.extra_filename[..]);
700713
let path_kind = if dep.is_some() { PathKind::Dependency } else { PathKind::Crate };
701-
let private_dep = dep.map(|d| d.is_private);
714+
let dep_privacy = self.privacy_of_dep(name, dep_of);
702715

703716
let result = if let Some(cnum) = self.existing_match(name, hash, path_kind) {
704717
(LoadResult::Previous(cnum), None)
@@ -732,22 +745,22 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
732745
match result {
733746
(LoadResult::Previous(cnum), None) => {
734747
info!("library for `{}` was loaded previously", name);
735-
// When `private_dep` is none, it indicates the directly dependent crate. If it is
748+
// When `dep_of` is none, it indicates the directly dependent crate. If it is
736749
// not specified by `--extern` on command line parameters, it may be
737750
// `private-dependency` when `register_crate` is called for the first time. Then it must be updated to
738751
// `public-dependency` here.
739-
let private_dep = self.is_private_dep(name, private_dep, dep_root);
740752
let data = self.cstore.get_crate_data_mut(cnum);
741753
if data.is_proc_macro_crate() {
742754
dep_kind = CrateDepKind::MacrosOnly;
743755
}
744756
data.set_dep_kind(cmp::max(data.dep_kind(), dep_kind));
745-
data.update_and_private_dep(private_dep);
757+
data.update_merge_dep_privacy(dep_privacy);
746758
Ok(cnum)
747759
}
748760
(LoadResult::Loaded(library), host_library) => {
749761
info!("register newly loaded library for `{}`", name);
750-
self.register_crate(host_library, dep_root, library, dep_kind, name, private_dep)
762+
763+
self.register_crate(host_library, dep_root, library, dep_kind, name, dep_privacy)
751764
}
752765
_ => panic!(),
753766
}
@@ -795,6 +808,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
795808
metadata: &MetadataBlob,
796809
krate: CrateNum,
797810
dep_kind: CrateDepKind,
811+
crate_root_privacy: DepPrivacy,
798812
) -> Result<CrateNumMap, CrateError> {
799813
debug!(
800814
"resolving deps of external crate `{}` with dep root `{}`",
@@ -823,7 +837,9 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
823837
CrateDepKind::MacrosOnly => CrateDepKind::MacrosOnly,
824838
_ => dep.kind,
825839
};
826-
let cnum = self.maybe_resolve_crate(dep.name, dep_kind, Some((dep_root, &dep)))?;
840+
let dep_of =
841+
DepOf { dep_root, parent: krate, privacy_of_parent: crate_root_privacy, dep: &dep };
842+
let cnum = self.maybe_resolve_crate(dep.name, dep_kind, Some(dep_of))?;
827843
crate_num_map.push(cnum);
828844
}
829845

compiler/rustc_metadata/src/rmeta/decoder.rs

+49-5
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ pub(crate) struct CrateMetadata {
122122
/// Whether or not this crate should be consider a private dependency.
123123
/// Used by the 'exported_private_dependencies' lint, and for determining
124124
/// whether to emit suggestions that reference this crate.
125-
private_dep: bool,
125+
dep_privacy: DepPrivacy,
126126
/// The hash for the host proc macro. Used to support `-Z dual-proc-macro`.
127127
host_hash: Option<Svh>,
128128
/// The crate was used non-speculatively.
@@ -153,6 +153,50 @@ struct ImportedSourceFile {
153153
translated_source_file: Arc<rustc_span::SourceFile>,
154154
}
155155

156+
#[derive(Clone, Copy, Debug)]
157+
pub(crate) enum DepPrivacy {
158+
/// Direct dependency, with privacy of
159+
Direct(bool),
160+
/// This is a transitive dependency and the dep graph from `LOCAL_CRATE` to this is
161+
/// all public.
162+
TransitivePublic,
163+
/// This is a transitive dependency and one or more edge of dep graph from `LOCAL_CRATE`
164+
/// to this is private.
165+
TransitivePrivate,
166+
/// From `extern crate`. This is considered as public dependency unless
167+
/// `--extern priv:` is set
168+
ExternCrate,
169+
}
170+
171+
impl DepPrivacy {
172+
pub(crate) fn is_private(self) -> bool {
173+
let res = match self {
174+
Self::Direct(private) => private,
175+
Self::TransitivePublic => false,
176+
Self::TransitivePrivate => true,
177+
Self::ExternCrate => false,
178+
};
179+
res
180+
}
181+
182+
pub(crate) fn merge(self, other: Self) -> Self {
183+
use DepPrivacy::*;
184+
185+
match (self, other) {
186+
(Direct(a), Direct(b)) => Direct(a && b),
187+
// If a dependency is directly private but is re-exported as a public dependency
188+
// in a "chain of transitive public dependencies," treat it as public.
189+
(Direct(true), TransitivePublic) | (TransitivePublic, Direct(true)) => TransitivePublic,
190+
// Otherwise, prioritize direct privacy
191+
(Direct(a), _) | (_, Direct(a)) => Direct(a),
192+
// `extern crate` are public unless they are explicitly private
193+
(ExternCrate, _) | (_, ExternCrate) => ExternCrate,
194+
(TransitivePublic, _) | (_, TransitivePublic) => TransitivePublic,
195+
_ => other,
196+
}
197+
}
198+
}
199+
156200
pub(super) struct DecodeContext<'a, 'tcx> {
157201
opaque: MemDecoder<'a>,
158202
cdata: Option<CrateMetadataRef<'a>>,
@@ -1837,7 +1881,7 @@ impl CrateMetadata {
18371881
cnum_map: CrateNumMap,
18381882
dep_kind: CrateDepKind,
18391883
source: CrateSource,
1840-
private_dep: bool,
1884+
dep_privacy: DepPrivacy,
18411885
host_hash: Option<Svh>,
18421886
) -> CrateMetadata {
18431887
let trait_impls = root
@@ -1868,7 +1912,7 @@ impl CrateMetadata {
18681912
dependencies,
18691913
dep_kind,
18701914
source: Arc::new(source),
1871-
private_dep,
1915+
dep_privacy,
18721916
host_hash,
18731917
used: false,
18741918
extern_crate: None,
@@ -1920,8 +1964,8 @@ impl CrateMetadata {
19201964
self.dep_kind = dep_kind;
19211965
}
19221966

1923-
pub(crate) fn update_and_private_dep(&mut self, private_dep: bool) {
1924-
self.private_dep &= private_dep;
1967+
pub(crate) fn update_merge_dep_privacy(&mut self, dep_privacy: DepPrivacy) {
1968+
self.dep_privacy = self.dep_privacy.merge(dep_privacy);
19251969
}
19261970

19271971
pub(crate) fn used(&self) -> bool {

compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,7 @@ provide! { tcx, def_id, other, cdata,
349349
cross_crate_inlinable => { table_direct }
350350

351351
dylib_dependency_formats => { cdata.get_dylib_dependency_formats(tcx) }
352-
is_private_dep => { cdata.private_dep }
352+
is_private_dep => { cdata.dep_privacy.is_private() }
353353
is_panic_runtime => { cdata.root.panic_runtime }
354354
is_compiler_builtins => { cdata.root.compiler_builtins }
355355
has_global_allocator => { cdata.root.has_global_allocator }

compiler/rustc_metadata/src/rmeta/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::marker::PhantomData;
22
use std::num::NonZero;
33

4-
pub(crate) use decoder::{CrateMetadata, CrateNumMap, MetadataBlob, TargetModifiers};
4+
pub(crate) use decoder::{CrateMetadata, CrateNumMap, DepPrivacy, MetadataBlob, TargetModifiers};
55
use decoder::{DecodeContext, Metadata};
66
use def_path_hash_map::DefPathHashMapRef;
77
use encoder::EncodeContext;
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
//@ aux-crate:priv:reexport=reexport.rs
22
//@ compile-flags: -Zunstable-options
3-
//@ check-pass
43

54
// Checks the behavior of a reexported item from a private dependency.
65

@@ -9,7 +8,7 @@
98

109
extern crate reexport;
1110

12-
// FIXME: This should trigger.
1311
pub fn leaks_priv() -> reexport::Shared {
12+
//~^ ERROR type `Shared` from private dependency 'shared' in public interface
1413
reexport::Shared
1514
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: type `Shared` from private dependency 'shared' in public interface
2+
--> $DIR/reexport_from_priv.rs:11:1
3+
|
4+
LL | pub fn leaks_priv() -> reexport::Shared {
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/reexport_from_priv.rs:7:9
9+
|
10+
LL | #![deny(exported_private_dependencies)]
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
13+
error: aborting due to 1 previous error
14+

tests/ui/privacy/pub-priv-dep/shared_both_private.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
//@ aux-crate:priv:shared=shared.rs
2-
//@ aux-crate:reexport=reexport.rs
2+
//@ aux-crate:priv:reexport=reexport.rs
33
//@ compile-flags: -Zunstable-options
4-
//@ check-pass
54

65
// A shared dependency, where a private dependency reexports a public dependency.
76
//
@@ -21,12 +20,12 @@
2120
extern crate shared;
2221
extern crate reexport;
2322

24-
// FIXME: This should trigger.
2523
pub fn leaks_priv() -> shared::Shared {
24+
//~^ ERROR type `Shared` from private dependency 'shared' in public interface
2625
shared::Shared
2726
}
2827

29-
// FIXME: This should trigger.
3028
pub fn leaks_priv_reexport() -> reexport::Shared {
29+
//~^ ERROR type `Shared` from private dependency 'shared' in public interface
3130
reexport::Shared
3231
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error: type `Shared` from private dependency 'shared' in public interface
2+
--> $DIR/shared_both_private.rs:23:1
3+
|
4+
LL | pub fn leaks_priv() -> shared::Shared {
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/shared_both_private.rs:18:9
9+
|
10+
LL | #![deny(exported_private_dependencies)]
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
13+
error: type `Shared` from private dependency 'shared' in public interface
14+
--> $DIR/shared_both_private.rs:28:1
15+
|
16+
LL | pub fn leaks_priv_reexport() -> reexport::Shared {
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
18+
19+
error: aborting due to 2 previous errors
20+

0 commit comments

Comments
 (0)