Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

exported_private_dependencies lint only take effect in innermost dependency #119428

Open
Tracked by #44663
linyihai opened this issue Dec 22, 2023 · 21 comments
Open
Tracked by #44663
Labels
A-lints Area: Lints (warnings about flaws in source code) such as unused_mut. A-visibility Area: Visibility / privacy C-bug Category: This is a bug. F-public_private_dependencies feature: public_private_dependencies L-exported_private_dependencies Lint: exported_private_dependencies T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@linyihai
Copy link
Contributor

linyihai commented Dec 22, 2023

Problem

The exported_private_dependencies lint only take affect in the innermost dependency in a recursively dependent environment.

This inspired by #44663 (comment).

Steps

In order to prove this problem, I purposely contructed a code repository, here

in the repository, the crates folder has three crates,

  • grandparent_dep, the init crate provied the pub struct
  • parent_dep, the middle crate, add grandparent_dep as public and reexport pub struct from grandparent_dep
  • pub_dep, the outmost crate, add parent_dep as public and reexport pub struct from parent_dep

(the crates in crates can be treated as download from respority(like github, crates.io))

in src/lib.rs, add pub_dep as dependency but private.

After downloading the resposity,

1、 run cargo build, no lint warning message
2、 change the public = false in crates/pub_dep/Cargo.toml, then run cargo build, no lint warning message
3、 change the public = false in crates/parent_dep/Cargo.toml, run cargo build and a lint warning message comes up.

cargo build
   Compiling parent_dep v0.1.0 (/root/workspace/recursive_pub_reexport/crates/parent_dep)
   Compiling pub_dep v0.1.0 (/root/workspace/recursive_pub_reexport/crates/pub_dep)
   Compiling simple v0.1.0 (/root/workspace/recursive_pub_reexport)
warning: type `FromPriv` from private dependency 'grandparent_dep' in public interface
 --> src/lib.rs:3:1
  |
3 | pub fn use_pub(_: pub_dep::FromPriv) {}
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(exported_private_dependencies)]` on by default

Possible Solution(s)

The focus of this issue is to verify whether there is a problem with the current situation, so the solution will not be considered for the time being.

Notes

No response

Version

cargo 1.76.0-nightly (623b78849 2023-12-02)
release: 1.76.0-nightly
commit-hash: 623b788496b3e51dc2f9282373cf0f6971a229b5
commit-date: 2023-12-02
host: x86_64-unknown-linux-gnu
libgit2: 1.7.1 (sys:0.18.1 vendored)
libcurl: 8.4.0-DEV (sys:0.4.68+curl-8.4.0 vendored ssl:OpenSSL/1.1.1u)
ssl: OpenSSL 1.1.1u  30 May 2023
os: Ubuntu 22.04 (jammy) [64-bit]
@epage
Copy link
Contributor

epage commented Dec 27, 2023

I made a slight change to the repo, renaming use_pub to use_priv and added the function to each package along the way so you can more easily see what the whole tree was doing

image

image

image

image

@epage
Copy link
Contributor

epage commented Dec 27, 2023

I was curious what is done with diamonds: nothing unexpected.

image

image

image

image

@epage
Copy link
Contributor

epage commented Dec 27, 2023

Going back to main

$ cargo +nightly clean && cargo +nightly check --all -vvv
     Removed 44 files, 183.6KiB total
    Checking grandparent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/grandparent_dep)
     Running `CARGO=/home/epage/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo CARGO_CRATE_NAME=grandpa
rent_dep CARGO_MANIFEST_DIR=/home/epage/src/personal/dump/recursive_pub_reexport/crates/grandparent_dep CARGO_PKG_AUTH
ORS='' CARGO_PKG_DESCRIPTION='' CARGO_PKG_HOMEPAGE='' CARGO_PKG_LICENSE='' CARGO_PKG_LICENSE_FILE='' CARGO_PKG_NAME=gr
andparent_dep CARGO_PKG_README='' CARGO_PKG_REPOSITORY='' CARGO_PKG_RUST_VERSION='' CARGO_PKG_VERSION=0.1.0 CARGO_PKG_
VERSION_MAJOR=0 CARGO_PKG_VERSION_MINOR=1 CARGO_PKG_VERSION_PATCH=0 CARGO_PKG_VERSION_PRE='' CARGO_RUSTC_CURRENT_DIR=/
home/epage/src/personal/dump/recursive_pub_reexport LD_LIBRARY_PATH='/home/epage/src/personal/dump/recursive_pub_reexp
ort/target/debug/deps:/home/epage/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib:/home/epage/.rustup/toolchai
ns/nightly-x86_64-unknown-linux-gnu/lib' /home/epage/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustc --c
rate-name grandparent_dep --edition=2021 crates/grandparent_dep/src/lib.rs --error-format=json --json=diagnostic-rende
red-ansi,artifacts,future-incompat --diagnostic-width=118 --crate-type lib --emit=dep-info,metadata -C embed-bitcode=n
o -C debuginfo=2 -C metadata=ec231377d3a2b1c4 -C extra-filename=-ec231377d3a2b1c4 --out-dir /home/epage/src/personal/d
ump/recursive_pub_reexport/target/debug/deps -C incremental=/home/epage/src/personal/dump/recursive_pub_reexport/targe
t/debug/incremental -L dependency=/home/epage/src/personal/dump/recursive_pub_reexport/target/debug/deps`
    Checking parent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/parent_dep)
     Running `CARGO=/home/epage/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo CARGO_CRATE_NAME=parent_
dep CARGO_MANIFEST_DIR=/home/epage/src/personal/dump/recursive_pub_reexport/crates/parent_dep CARGO_PKG_AUTHORS='' CAR
GO_PKG_DESCRIPTION='' CARGO_PKG_HOMEPAGE='' CARGO_PKG_LICENSE='' CARGO_PKG_LICENSE_FILE='' CARGO_PKG_NAME=parent_dep C
ARGO_PKG_README='' CARGO_PKG_REPOSITORY='' CARGO_PKG_RUST_VERSION='' CARGO_PKG_VERSION=0.1.0 CARGO_PKG_VERSION_MAJOR=0
 CARGO_PKG_VERSION_MINOR=1 CARGO_PKG_VERSION_PATCH=0 CARGO_PKG_VERSION_PRE='' CARGO_RUSTC_CURRENT_DIR=/home/epage/src/
personal/dump/recursive_pub_reexport LD_LIBRARY_PATH='/home/epage/src/personal/dump/recursive_pub_reexport/target/debu
g/deps:/home/epage/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib:/home/epage/.rustup/toolchains/nightly-x86_
64-unknown-linux-gnu/lib' /home/epage/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustc --crate-name paren
t_dep --edition=2021 crates/parent_dep/src/lib.rs --error-format=json --json=diagnostic-rendered-ansi,artifacts,future
-incompat --diagnostic-width=118 --crate-type lib --emit=dep-info,metadata -C embed-bitcode=no -C debuginfo=2 -C metad
ata=b0e93a3b309a3856 -C extra-filename=-b0e93a3b309a3856 --out-dir /home/epage/src/personal/dump/recursive_pub_reexpor
t/target/debug/deps -C incremental=/home/epage/src/personal/dump/recursive_pub_reexport/target/debug/incremental -L de
pendency=/home/epage/src/personal/dump/recursive_pub_reexport/target/debug/deps --extern grandparent_dep=/home/epage/s
rc/personal/dump/recursive_pub_reexport/target/debug/deps/libgrandparent_dep-ec231377d3a2b1c4.rmeta`
    Checking pub_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/pub_dep)
     Running `CARGO=/home/epage/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo CARGO_CRATE_NAME=pub_dep
 CARGO_MANIFEST_DIR=/home/epage/src/personal/dump/recursive_pub_reexport/crates/pub_dep CARGO_PKG_AUTHORS='' CARGO_PKG
_DESCRIPTION='' CARGO_PKG_HOMEPAGE='' CARGO_PKG_LICENSE='' CARGO_PKG_LICENSE_FILE='' CARGO_PKG_NAME=pub_dep CARGO_PKG_
README='' CARGO_PKG_REPOSITORY='' CARGO_PKG_RUST_VERSION='' CARGO_PKG_VERSION=0.1.0 CARGO_PKG_VERSION_MAJOR=0 CARGO_PK
G_VERSION_MINOR=1 CARGO_PKG_VERSION_PATCH=0 CARGO_PKG_VERSION_PRE='' CARGO_RUSTC_CURRENT_DIR=/home/epage/src/personal/
dump/recursive_pub_reexport LD_LIBRARY_PATH='/home/epage/src/personal/dump/recursive_pub_reexport/target/debug/deps:/h
ome/epage/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib:/home/epage/.rustup/toolchains/nightly-x86_64-unknow
n-linux-gnu/lib' /home/epage/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustc --crate-name pub_dep --edit
ion=2021 crates/pub_dep/src/lib.rs --error-format=json --json=diagnostic-rendered-ansi,artifacts,future-incompat --dia
gnostic-width=118 --crate-type lib --emit=dep-info,metadata -C embed-bitcode=no -C debuginfo=2 -C metadata=523c770971b
0c118 -C extra-filename=-523c770971b0c118 --out-dir /home/epage/src/personal/dump/recursive_pub_reexport/target/debug/
deps -C incremental=/home/epage/src/personal/dump/recursive_pub_reexport/target/debug/incremental -L dependency=/home/
epage/src/personal/dump/recursive_pub_reexport/target/debug/deps --extern parent_dep=/home/epage/src/personal/dump/rec
ursive_pub_reexport/target/debug/deps/libparent_dep-b0e93a3b309a3856.rmeta`
    Checking simple v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport)
     Running `CARGO=/home/epage/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo CARGO_CRATE_NAME=simple
CARGO_MANIFEST_DIR=/home/epage/src/personal/dump/recursive_pub_reexport CARGO_PKG_AUTHORS='' CARGO_PKG_DESCRIPTION=''
CARGO_PKG_HOMEPAGE='' CARGO_PKG_LICENSE='' CARGO_PKG_LICENSE_FILE='' CARGO_PKG_NAME=simple CARGO_PKG_README='' CARGO_P
KG_REPOSITORY='' CARGO_PKG_RUST_VERSION='' CARGO_PKG_VERSION=0.1.0 CARGO_PKG_VERSION_MAJOR=0 CARGO_PKG_VERSION_MINOR=1
 CARGO_PKG_VERSION_PATCH=0 CARGO_PKG_VERSION_PRE='' CARGO_PRIMARY_PACKAGE=1 CARGO_RUSTC_CURRENT_DIR=/home/epage/src/pe
rsonal/dump/recursive_pub_reexport LD_LIBRARY_PATH='/home/epage/src/personal/dump/recursive_pub_reexport/target/debug/
deps:/home/epage/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib:/home/epage/.rustup/toolchains/nightly-x86_64
-unknown-linux-gnu/lib' /home/epage/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustc --crate-name simple
--edition=2021 src/lib.rs --error-format=json --json=diagnostic-rendered-ansi,artifacts,future-incompat --diagnostic-w
idth=118 --crate-type lib --emit=dep-info,metadata -C embed-bitcode=no -C debuginfo=2 -C metadata=2b1b07927f979b6c -C
extra-filename=-2b1b07927f979b6c --out-dir /home/epage/src/personal/dump/recursive_pub_reexport/target/debug/deps -C i
ncremental=/home/epage/src/personal/dump/recursive_pub_reexport/target/debug/incremental -L dependency=/home/epage/src
/personal/dump/recursive_pub_reexport/target/debug/deps --extern 'priv:pub_dep=/home/epage/src/personal/dump/recursive
_pub_reexport/target/debug/deps/libpub_dep-523c770971b0c118.rmeta' -Z unstable-options`
    Finished dev [unoptimized + debuginfo] target(s) in 0.08s

With line wrapping
image

@epage
Copy link
Contributor

epage commented Dec 27, 2023

We only pass --extern for direct dependencies which means rustc is only told about priv for direct dependencies and rustc then collect that information when recursing through dependencies. I think this should work if everything builds cleanly with --forbid exported_private_dependencies (or --deny but they respect the rules perfectly).

In the transitional case where foundational packages have not been updated, users will have to use #[allow(exported_private_dependencies)] when putting transitive types in their API.

In the transitional case where foundation packages have been updated but intermediate packages have not been, no warning will be given even when it should.

So the next questions are

  • Are there cases I'm missing?
  • What are our options for fixing this?
  • How much do we care?

@linyihai
Copy link
Contributor Author

Thank you for dig into this issue.

@epage
Copy link
Contributor

epage commented Dec 28, 2023

Another case I want to test is when a package has a private dependency in its API (so should warn) but it is declared public in a transitive dependency.

@epage
Copy link
Contributor

epage commented Dec 28, 2023

image

image

Looks like once a dependency is public, it is always viewed as public.

@epage
Copy link
Contributor

epage commented Dec 28, 2023

Summary: only direct dependencies are marked as public/private; all transitive uses of it respect the highest visibility present in that subtree of the dependency graph.

Example Side effects

  • If you directly depend on a transitive dependency that is marked as public, it will be considered public for you, not emitting any warnings
  • If a grandparent is re-exported by a parent (and declared it public) and you use it in your API, you won't get a warning about needing to mark the parent as public (or grandparent despite not directly depending on it) because the parent declaring the grandparent public makes the compiler think its also public for you

This is because we only pass --extern for direct dependencies and visibility is tied to --extern.

Possible solutions (without knowing the compiler side)

  • The compiler check the graph of externs to see what the effective visibility is for the current crate being compiled
  • Cargo pass visibility for the entire dependency graph, separate from --extern

@ehuss I think you were involved in the conversations on the cargo/rustc handshake for this. Any thoughts on how to move this forward?

@linyihai
Copy link
Contributor Author

linyihai commented Dec 29, 2023

#119428 (comment)

This scenario is more common and worth writing a test case for. I plan to add this in rust-lang/cargo#13183

@ehuss
Copy link
Contributor

ehuss commented Dec 29, 2023

I believe that is a rustc issue. It looks like it is checking the wrong thing when it is checking the public-status. It is looking at the DefId of the item, which is where the item is defined. However, I don't think that is correct in the face of re-exports. It should be checking the public status of the crate of the Use item from where it was re-exported. I don't know much about the DefIdVisitor or the visibility analysis stuff in general, so I can't really suggest a different approach. If you want to work on fixing it yourself, you'll probably want to ask the compiler team for assistance in coming up with a different implementation strategy.

I don't think it is necessary to pass more visibility data to rustc. It should be able to compute the effective visibility from the information it has.

It sounds like there needs to be a more principled definition of what it means to "leak" a private item, since it seems like there are some edge cases that the current approach doesn't handle correctly.

@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Dec 30, 2023
@epage epage transferred this issue from rust-lang/cargo Dec 30, 2023
@epage
Copy link
Contributor

epage commented Dec 30, 2023

If checking the visibility from where it is re-expoerted from has problems, another idea (without any compiler knowledge) is to compute the effective visibility between the current crate and the DefId, by walking the tree of public dependencies to see if there is a path that can reach it.

@apiraino
Copy link
Contributor

apiraino commented Jan 17, 2024

Nominating for a compiler meeting discussion

@rustbot label +I-compiler-nominated +T-compiler

@rustbot rustbot added I-compiler-nominated Nominated for discussion during a compiler team meeting. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jan 17, 2024
@wesleywiser
Copy link
Member

I believe that is a rustc issue. It looks like it is checking the wrong thing when it is checking the public-status. It is looking at the DefId of the item, which is where the item is defined. However, I don't think that is correct in the face of re-exports. It should be checking the public status of the crate of the Use item from where it was re-exported. I don't know much about the DefIdVisitor or the visibility analysis stuff in general, so I can't really suggest a different approach. If you want to work on fixing it yourself, you'll probably want to ask the compiler team for assistance in coming up with a different implementation strategy.

@petrochenkov do you have expertise with the visibility analysis? If so, do you agree with the assessment here or is there a better way to perform the exported_private_dependencies analysis?

@apiraino apiraino removed the I-compiler-nominated Nominated for discussion during a compiler team meeting. label Jan 18, 2024
@petrochenkov
Copy link
Contributor

petrochenkov commented Jan 18, 2024

@wesleywiser
This specific issue is not about visibility analysis, but about private_dep: bool flags set by rustc on all crates in the crate dependency graph.

The current implementation just set these flags to true... sometimes, without much of a consistent scheme.
I was talking about this soon after the initial implementation in 2019 (#44663 (comment)), but it was never fixed.

Someone need to decide how these flags should propagate through the dependency graph in general case (with all kinds of chain and diamond cases), after that it could be properly implemented in rustc_metadata.

@petrochenkov
Copy link
Contributor

Let's say A is a graph with N nodes with privacy flags PA1..PAN, and B is a graph with M nodes with privacy flags PB1..PBN.
Then we add Head(B) as a dependency to some node of A and get graph C with K ∈ [N; N + M] nodes (the node sets of A and B may intersect).
For privacies we then need to define some function PC(PA, PB) to generate the new privacy flags PC1..PCK.

The final flags will then be the result of a repeated application of this function for adding all direct dependencies of the current crate.

@ehuss
Copy link
Contributor

ehuss commented Jan 20, 2024

@petrochenkov To clarify, are you suggesting there should be some kind of precedence rules to deal with conflicts? For example:

foo → (public) bar → (public) baz
foo → (private) baz

Here, bar publicly re-exports Thing from baz. There are two ways foo could expose the item Thing from baz, either from baz::Thing, or bar::Thing. They refer to the same item, just through different paths. Are you suggesting there should be some rule that says foo exposing either baz::Thing or bar::Thing should result in the same behavior (either warning or not)?

I was originally thinking it would be path-sensitive, so that they would be different (bar::Thing would be OK, but baz::Thing would not). Then, rustc would only care about the visibility of direct dependencies. However, thinking about that more, I'm feeling like I might be wrong. Either way, I can imagine this has the potential to be confusing.

Do people have an intuition of which approach would be easier to grasp? If it is based on where items are defined (as opposed to path-based), does anyone want to suggest what rules would be used to break conflicts? (I started writing some down, but they seemed horribly complicated.)

I'm trying to think about what the intentions of the user might be in such a case, and it isn't obvious to me. I also can't decide whether or not it would be better to err on the side of warning.

@epage epage added A-lints Area: Lints (warnings about flaws in source code) such as unused_mut. A-visibility Area: Visibility / privacy labels Jan 30, 2024
@epage
Copy link
Contributor

epage commented Jan 30, 2024

FYI @matthewjasper

@jieyouxu jieyouxu removed the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Feb 26, 2024
@jieyouxu jieyouxu added the L-exported_private_dependencies Lint: exported_private_dependencies label May 13, 2024
@tgross35
Copy link
Contributor

I have not checked the case in the top post, but some of this has been improved with #135501.

@epage
Copy link
Contributor

epage commented Feb 26, 2025

Thanks @tgross35 !

Stepping through the different cases (with an in-dev cargo tree feature)

Original
$ nargo tree
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.12s
simple v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport)
└── pub_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/pub_dep)
    └── parent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/parent_dep)
        └── grandparent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/grandparent_dep)
$ nargo tree --depth public --workspace
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.12s
grandparent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/grandparent_dep)

parent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/parent_dep)
└── grandparent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/grandparent_dep)

pub_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/pub_dep)
└── parent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/parent_dep) (*)

simple v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport)
$ cargo +nightly check --all
    Checking grandparent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/grandparent_dep)
    Checking parent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/parent_dep)
    Checking pub_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/pub_dep)
    Checking simple v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport)
warning: type `FromPriv` from private dependency 'grandparent_dep' in public interface
 --> src/lib.rs:3:1
  |
3 | pub fn use_priv(_: pub_dep::FromPriv) {}
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(exported_private_dependencies)]` on by default

warning: `simple` (lib) generated 1 warning
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.10s
  • Correctly warn on private dep that re-exported an item from many layers of public deps
Original variant 1
$ git diff
diff --git i/crates/parent_dep/Cargo.toml w/crates/parent_dep/Cargo.toml
index 5f27dc7..a73d7cc 100644
--- i/crates/parent_dep/Cargo.toml
+++ w/crates/parent_dep/Cargo.toml
@@ -6,4 +6,4 @@ version = "0.1.0"
 edition = "2021"

 [dependencies]
-grandparent_dep = { version = "0.1.0", path = "../grandparent_dep", public = true }
+grandparent_dep = { version = "0.1.0", path = "../grandparent_dep", public = false }

$ nargo tree
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.14s
simple v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport)
└── pub_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/pub_dep)
    └── parent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/parent_dep)
        └── grandparent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/grandparent_dep)

$ nargo tree --depth public --workspace
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.12s
grandparent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/grandparent_dep)

parent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/parent_dep)

pub_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/pub_dep)
└── parent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/parent_dep) (*)

simple v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport)

$ cargo +nightly check --all
    Checking grandparent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/grandparent_dep)
    Checking parent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/parent_dep)
warning: type `FromPriv` from private dependency 'grandparent_dep' in public interface
 --> crates/parent_dep/src/lib.rs:4:1
  |
4 | pub fn use_priv(_: grandparent_dep::FromPriv) {}
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(exported_private_dependencies)]` on by default

warning: `parent_dep` (lib) generated 1 warning
    Checking pub_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/pub_dep)
warning: type `FromPriv` from private dependency 'grandparent_dep' in public interface
 --> crates/pub_dep/src/lib.rs:4:1
  |
4 | pub fn use_priv(_: parent_dep::FromPriv) {}
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(exported_private_dependencies)]` on by default

warning: `pub_dep` (lib) generated 1 warning
    Checking simple v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport)
warning: type `FromPriv` from private dependency 'grandparent_dep' in public interface
 --> src/lib.rs:3:1
  |
3 | pub fn use_priv(_: pub_dep::FromPriv) {}
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(exported_private_dependencies)]` on by default

warning: `simple` (lib) generated 1 warning
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.14s
  • Warn on all dependents when one layer of transitive deps is private
Original variant 2
$ git diff
diff --git i/Cargo.toml w/Cargo.toml
index a9012ec..8ac91a5 100644
--- i/Cargo.toml
+++ w/Cargo.toml
@@ -9,4 +9,4 @@ version = "0.1.0"
 edition = "2021"

 [dependencies]
-pub_dep = { version = "0.1.0", path = "./crates/pub_dep" }
+pub_dep = { version = "0.1.0", path = "./crates/pub_dep", public = true }
diff --git i/crates/parent_dep/Cargo.toml w/crates/parent_dep/Cargo.toml
index 5f27dc7..a73d7cc 100644
--- i/crates/parent_dep/Cargo.toml
+++ w/crates/parent_dep/Cargo.toml
@@ -6,4 +6,4 @@ version = "0.1.0"
 edition = "2021"

 [dependencies]
-grandparent_dep = { version = "0.1.0", path = "../grandparent_dep", public = true }
+grandparent_dep = { version = "0.1.0", path = "../grandparent_dep", public = false }

$ nargo tree
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.13s
simple v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport)
└── pub_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/pub_dep)
    └── parent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/parent_dep)
        └── grandparent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/grandparent_dep)

$ nargo tree --depth public --workspace
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.14s
grandparent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/grandparent_dep)

parent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/parent_dep)

pub_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/pub_dep)
└── parent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/parent_dep) (*)

simple v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport)
└── pub_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/pub_dep) (*)

$ cargo +nightly check --all
    Checking grandparent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/grandparent_dep)
    Checking parent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/parent_dep)
warning: type `FromPriv` from private dependency 'grandparent_dep' in public interface
 --> crates/parent_dep/src/lib.rs:4:1
  |
4 | pub fn use_priv(_: grandparent_dep::FromPriv) {}
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(exported_private_dependencies)]` on by default

warning: `parent_dep` (lib) generated 1 warning
    Checking pub_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/pub_dep)
warning: type `FromPriv` from private dependency 'grandparent_dep' in public interface
 --> crates/pub_dep/src/lib.rs:4:1
  |
4 | pub fn use_priv(_: parent_dep::FromPriv) {}
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(exported_private_dependencies)]` on by default

warning: `pub_dep` (lib) generated 1 warning
    Checking simple v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport)
warning: type `FromPriv` from private dependency 'grandparent_dep' in public interface
 --> src/lib.rs:3:1
  |
3 | pub fn use_priv(_: pub_dep::FromPriv) {}
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(exported_private_dependencies)]` on by default

warning: `simple` (lib) generated 1 warning
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.13s
  • Warn on all dependents when one layer of transitive deps is private
Diamond
$ nargo tree --workspace
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.12s
simple v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport)
├── left_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/left_dep)
│   └── priv_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/priv_dep)
└── right_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/right_dep)
    └── priv_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/priv_dep)

$ nargo tree --depth public --workspace
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.11s
left_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/left_dep)
└── priv_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/priv_dep)

priv_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/priv_dep)

right_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/right_dep)
└── priv_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/priv_dep)

simple v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport)

$ cargo +nightly check --all
    Checking priv_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/priv_dep)
    Checking left_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/left_dep)
    Checking right_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/right_dep)
    Checking simple v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport)
warning: type `FromPriv` from private dependency 'priv_dep' in public interface
 --> src/lib.rs:2:5
  |
2 |     pub fn use_priv(_: left_dep::FromPriv) {}
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(exported_private_dependencies)]` on by default

warning: type `FromPriv` from private dependency 'priv_dep' in public interface
 --> src/lib.rs:5:5
  |
5 |     pub fn use_priv(_: right_dep::FromPriv) {}
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: `simple` (lib) generated 2 warnings
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.08s
  • Correctly warn on multiple private deps that re-exported an item from a public dep
Diamond variant 1
$ git diff
diff --git i/crates/left_dep/Cargo.toml w/crates/left_dep/Cargo.toml
index 41f2079..41cd713 100644
--- i/crates/left_dep/Cargo.toml
+++ w/crates/left_dep/Cargo.toml
@@ -6,4 +6,4 @@ version = "0.1.0"
 edition = "2021"

 [dependencies]
-priv_dep = { version = "0.1.0", path = "../priv_dep", public = true }
+priv_dep = { version = "0.1.0", path = "../priv_dep", public = false }

$ nargo tree
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.14s
simple v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport)
├── left_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/left_dep)
│   └── priv_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/priv_dep)
└── right_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/right_dep)
    └── priv_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/priv_dep)

$ nargo tree --depth public --workspace
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.14s
left_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/left_dep)

priv_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/priv_dep)

right_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/right_dep)
└── priv_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/priv_dep)

simple v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport)

$ cargo +nightly check --all
    Checking priv_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/priv_dep)
    Checking right_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/right_dep)
    Checking left_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/left_dep)
warning: type `FromPriv` from private dependency 'priv_dep' in public interface
 --> crates/left_dep/src/lib.rs:3:1
  |
3 | pub fn use_priv(_: priv_dep::FromPriv) {}
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(exported_private_dependencies)]` on by default

warning: `left_dep` (lib) generated 1 warning
    Checking simple v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport)
warning: type `FromPriv` from private dependency 'priv_dep' in public interface
 --> src/lib.rs:2:5
  |
2 |     pub fn use_priv(_: left_dep::FromPriv) {}
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(exported_private_dependencies)]` on by default

warning: type `FromPriv` from private dependency 'priv_dep' in public interface
 --> src/lib.rs:5:5
  |
5 |     pub fn use_priv(_: right_dep::FromPriv) {}
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: `simple` (lib) generated 2 warnings
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.11s
  • Correctly warn on private dep
  • Correctly warn on multiple private deps that re-exported an item from either a public or private path to a dep
Diamond variant 2
$ git diff
diff --git i/Cargo.toml w/Cargo.toml
index c47b24d..d9aeedb 100644
--- i/Cargo.toml
+++ w/Cargo.toml
@@ -9,5 +9,5 @@ version = "0.1.0"
 edition = "2021"

 [dependencies]
-left_dep = { version = "0.1.0", path = "./crates/left_dep" }
-right_dep = { version = "0.1.0", path = "./crates/right_dep" }
+left_dep = { version = "0.1.0", path = "./crates/left_dep", public = true }
+right_dep = { version = "0.1.0", path = "./crates/right_dep", public = true }
diff --git i/crates/left_dep/Cargo.toml w/crates/left_dep/Cargo.toml
index 41f2079..41cd713 100644
--- i/crates/left_dep/Cargo.toml
+++ w/crates/left_dep/Cargo.toml
@@ -6,4 +6,4 @@ version = "0.1.0"
 edition = "2021"

 [dependencies]
-priv_dep = { version = "0.1.0", path = "../priv_dep", public = true }
+priv_dep = { version = "0.1.0", path = "../priv_dep", public = false }

$ nargo tree
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.11s
simple v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport)
├── left_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/left_dep)
│   └── priv_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/priv_dep)
└── right_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/right_dep)
    └── priv_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/priv_dep)

$ nargo tree --depth public --workspace
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.12s
left_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/left_dep)

priv_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/priv_dep)

right_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/right_dep)
└── priv_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/priv_dep)

simple v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport)
├── left_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/left_dep) (*)
└── right_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/right_dep) (*)

$ cargo +nightly check --all
warning: type `FromPriv` from private dependency 'priv_dep' in public interface
 --> crates/left_dep/src/lib.rs:3:1
  |
3 | pub fn use_priv(_: priv_dep::FromPriv) {}
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(exported_private_dependencies)]` on by default

warning: `left_dep` (lib) generated 1 warning
    Checking simple v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.05s
  • Correctly warn on private dep
  • No warning when using an item from a public dep that was re-export from a private dep (despite Original variant 2 doing so)
Direct and transitive
$ nargo tree
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.12s
simple v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport)
├── grandparent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/grandparent_dep)
└── parent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/parent_dep)
    └── grandparent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/grandparent_dep)
$ nargo tree --depth public --workspace
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.12s
grandparent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/grandparent_dep)

parent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/parent_dep)
└── grandparent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/grandparent_dep)

simple v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport)

$ cargo +nightly check --all
    Checking grandparent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/grandparent_dep)
    Checking parent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/parent_dep)
    Checking simple v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport)
warning: type `FromPriv` from private dependency 'grandparent_dep' in public interface
 --> src/lib.rs:2:5
  |
2 |     pub fn use_priv(_: parent_dep::FromPriv) {}
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(exported_private_dependencies)]` on by default

warning: type `FromPriv` from private dependency 'grandparent_dep' in public interface
 --> src/lib.rs:5:5
  |
5 |     pub fn use_priv(_: grandparent_dep::FromPriv) {}
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: `simple` (lib) generated 2 warnings
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.10s
  • Correctly warn on multiple private deps that directly use or use a re-export of an item from a public dep
Direct and transitive variant 1
$ git diff
diff --git i/crates/parent_dep/Cargo.toml w/crates/parent_dep/Cargo.toml
index 5f27dc7..a73d7cc 100644
--- i/crates/parent_dep/Cargo.toml
+++ w/crates/parent_dep/Cargo.toml
@@ -6,4 +6,4 @@ version = "0.1.0"
 edition = "2021"

 [dependencies]
-grandparent_dep = { version = "0.1.0", path = "../grandparent_dep", public = true }
+grandparent_dep = { version = "0.1.0", path = "../grandparent_dep", public = false }

$ nargo tree
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.12s
simple v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport)
├── grandparent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/grandparent_dep)
└── parent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/parent_dep)
    └── grandparent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/grandparent_dep)

$ nargo tree --depth public --workspace
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.12s
grandparent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/grandparent_dep)

parent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/parent_dep)

simple v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport)

$ cargo +nightly check --all
warning: type `FromPriv` from private dependency 'grandparent_dep' in public interface
 --> crates/parent_dep/src/lib.rs:4:1
  |
4 | pub fn use_priv(_: grandparent_dep::FromPriv) {}
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(exported_private_dependencies)]` on by default

warning: `parent_dep` (lib) generated 1 warning
    Checking simple v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport)
warning: type `FromPriv` from private dependency 'grandparent_dep' in public interface
 --> src/lib.rs:2:5
  |
2 |     pub fn use_priv(_: parent_dep::FromPriv) {}
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(exported_private_dependencies)]` on by default

warning: type `FromPriv` from private dependency 'grandparent_dep' in public interface
 --> src/lib.rs:5:5
  |
5 |     pub fn use_priv(_: grandparent_dep::FromPriv) {}
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: `simple` (lib) generated 2 warnings
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.05s
  • Correctly warn on private dep
  • Correctly warn on multiple private deps that directly use or use a re-export of an item from a public dep
Direct and transitive variant 2
$ git diff
diff --git i/Cargo.toml w/Cargo.toml
index ed242c1..55c073c 100644
--- i/Cargo.toml
+++ w/Cargo.toml
@@ -9,5 +9,5 @@ version = "0.1.0"
 edition = "2021"

 [dependencies]
-grandparent_dep = { version = "0.1.0", path = "crates/grandparent_dep" }
-parent_dep = { version = "0.1.0", path = "./crates/parent_dep" }
+grandparent_dep = { version = "0.1.0", path = "crates/grandparent_dep", public = true }
+parent_dep = { version = "0.1.0", path = "./crates/parent_dep", public = true }
diff --git i/crates/parent_dep/Cargo.toml w/crates/parent_dep/Cargo.toml
index 5f27dc7..a73d7cc 100644
--- i/crates/parent_dep/Cargo.toml
+++ w/crates/parent_dep/Cargo.toml
@@ -6,4 +6,4 @@ version = "0.1.0"
 edition = "2021"

 [dependencies]
-grandparent_dep = { version = "0.1.0", path = "../grandparent_dep", public = true }
+grandparent_dep = { version = "0.1.0", path = "../grandparent_dep", public = false }

$ nargo tree
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.12s
simple v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport)
├── grandparent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/grandparent_dep)
└── parent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/parent_dep)
    └── grandparent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/grandparent_dep)

$ nargo tree --depth public --workspace
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.12s
grandparent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/grandparent_dep)

parent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/parent_dep)

simple v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport)
├── grandparent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/grandparent_dep)
└── parent_dep v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport/crates/parent_dep) (*)

$ cargo +nightly check --all
warning: type `FromPriv` from private dependency 'grandparent_dep' in public interface
 --> crates/parent_dep/src/lib.rs:4:1
  |
4 | pub fn use_priv(_: grandparent_dep::FromPriv) {}
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(exported_private_dependencies)]` on by default

warning: `parent_dep` (lib) generated 1 warning
    Checking simple v0.1.0 (/home/epage/src/personal/dump/recursive_pub_reexport)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.04s
  • Correctly warn on private dep
  • No warning when using an item from a public dep that was re-export from a private dep (despite Original variant 2 doing so)

@epage
Copy link
Contributor

epage commented Feb 26, 2025

Diamond variant 2

Direct and transitive variant 2

  • No warning when using an item from a public dep that was re-export from a private dep (despite Original variant 2 doing so)

I'm not sure if this should be a blocker for stabilization. The type is public in your API, just the path used to access it isn't.

Original variant 2

  • Warn on all dependents when one layer of transitive deps is private

I worry this will make adoption of this feature more difficult.

Take cargo -Zminimal-versions. Cargo has been hesitant to stabilize it because someone using it will need to first get their dependency into compliance before they can, rather than being something they can act on all of their own (granted, there is a workaround of no-op deps to force versions higher).

Applying that here, adoption of private dependencies will need to happen from the bottom up unless people workaround non-compliant dependencies by adding a direct public dependency on their transitive dependency (leveraging the "Direct and transitive variant 2" case).

If the lint could make the assumption that everything public in a dependency comes from a public transitive dependency (i.e. only warn if I reference something from a item path that is not public), this might smooth out adoption. Whether a dependency intended for a transitive dependency to be public or not won't be noticeable from the noise until it has adopted public. Once it does, in theory, things should be good. If they do publish with the lint firing, I guess it would help you from accidentally depending on something you shouldn't but that will likely only help in some cases.

@ShoyuVanilla
Copy link
Member

ShoyuVanilla commented Feb 28, 2025

I've been working on this for some days and managed to create a PoC that fixes the remaining part of this issue; the access path things.

I made the following design choices:

  • Warn only when a re-exported item is accessed through a private dependency, and do not warn when accessed through a public dependency.
    • This fixes "No warning when using an item from a public dep that was re-export from a private dep (despite Original variant 2 doing so)"
  • Avoid transitive privacy warnings, i.e. warn iff the "first" layer of access path is a private dependency. As @epage mentioned in the above comment, enforcing transitive privacy would make adoption unnecessarily difficult in transitional state.
    • This fixes "Warn on all dependents when one layer of transitive deps is private"

However, I'm still unsure about some implementation details, so I’ve asked a question on Zulip: Question about tracking crate provenance in PartialRes.

I'd really appreciate any feedback on this

github-merge-queue bot pushed a commit to rust-lang/cargo that referenced this issue Mar 6, 2025
### What does this PR try to resolve?

I was investigating some issues around public dependency lints and
wanted to see the structure of the public dependencies and had the idea
to add this with us having added `--depth workspace`.

See
rust-lang/rust#119428 (comment)
for some example output (comparing `cargo tree` with `cargo tree --depth
public`)

### How should we test and review this PR?

### Additional information
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-lints Area: Lints (warnings about flaws in source code) such as unused_mut. A-visibility Area: Visibility / privacy C-bug Category: This is a bug. F-public_private_dependencies feature: public_private_dependencies L-exported_private_dependencies Lint: exported_private_dependencies T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

10 participants