@@ -4029,3 +4029,275 @@ fn mergeable_info_dep_collision() {
40294029 // ...and the fingerprint content are different (path to dep.json different)
40304030 assert_ne ! ( first_fingerprint, second_fingerprint) ;
40314031}
4032+
4033+ #[ cargo_test( nightly, reason = "public-dependency feature is unstable" ) ]
4034+ fn doc_with_public_dependency_transitive ( ) {
4035+ // Tests recursive public dependency documentation with 2 layers.
4036+ // foo -> bar (direct) -> baz (public) -> qux (public)
4037+ //
4038+ // Before the fix: all transitive deps are documented regardless of
4039+ // public/private markers, so foo, bar, baz, and qux all get docs.
4040+ // After the fix: only direct deps and their public deps recursively
4041+ // are documented, so the same set applies here since the whole chain
4042+ // is public.
4043+
4044+ Package :: new ( "qux" , "0.0.1" )
4045+ . file ( "src/lib.rs" , "pub fn qux() {}" )
4046+ . publish ( ) ;
4047+
4048+ Package :: new ( "baz" , "0.0.1" )
4049+ . cargo_feature ( "public-dependency" )
4050+ . add_dep ( cargo_test_support:: registry:: Dependency :: new ( "qux" , "0.0.1" ) . public ( true ) )
4051+ . file ( "src/lib.rs" , "pub fn baz() {}" )
4052+ . publish ( ) ;
4053+
4054+ Package :: new ( "bar" , "0.0.1" )
4055+ . cargo_feature ( "public-dependency" )
4056+ . add_dep ( cargo_test_support:: registry:: Dependency :: new ( "baz" , "0.0.1" ) . public ( true ) )
4057+ . file ( "src/lib.rs" , "pub fn bar() {}" )
4058+ . publish ( ) ;
4059+
4060+ let p = project ( )
4061+ . file (
4062+ "Cargo.toml" ,
4063+ r#"
4064+ cargo-features = ["public-dependency"]
4065+
4066+ [package]
4067+ name = "foo"
4068+ version = "0.0.1"
4069+ edition = "2021"
4070+
4071+ [dependencies]
4072+ bar = "0.0.1"
4073+ "# ,
4074+ )
4075+ . file ( "src/lib.rs" , "pub fn foo() {}" )
4076+ . build ( ) ;
4077+
4078+ p. cargo ( "doc -Zpublic-dependency" )
4079+ . masquerade_as_nightly_cargo ( & [ "public-dependency" ] )
4080+ . with_stderr_data (
4081+ str![ [ r#"
4082+ [UPDATING] `dummy-registry` index
4083+ [LOCKING] 3 packages to latest compatible versions
4084+ [DOWNLOADING] crates ...
4085+ [DOWNLOADED] qux v0.0.1 (registry `dummy-registry`)
4086+ [DOWNLOADED] baz v0.0.1 (registry `dummy-registry`)
4087+ [DOWNLOADED] bar v0.0.1 (registry `dummy-registry`)
4088+ [DOCUMENTING] qux v0.0.1
4089+ [CHECKING] qux v0.0.1
4090+ [DOCUMENTING] baz v0.0.1
4091+ [CHECKING] baz v0.0.1
4092+ [DOCUMENTING] bar v0.0.1
4093+ [CHECKING] bar v0.0.1
4094+ [DOCUMENTING] foo v0.0.1 ([ROOT]/foo)
4095+ [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
4096+ [GENERATED] [ROOT]/foo/target/doc/foo/index.html
4097+
4098+ "# ] ]
4099+ . unordered ( ) ,
4100+ )
4101+ . run ( ) ;
4102+
4103+ // All four are documented: the whole chain is public so behavior is
4104+ // the same before and after the fix.
4105+ assert ! ( p. root( ) . join( "target/doc/foo/index.html" ) . is_file( ) ) ;
4106+ assert ! ( p. root( ) . join( "target/doc/bar/index.html" ) . is_file( ) ) ;
4107+ assert ! ( p. root( ) . join( "target/doc/baz/index.html" ) . is_file( ) ) ;
4108+ assert ! ( p. root( ) . join( "target/doc/qux/index.html" ) . is_file( ) ) ;
4109+ }
4110+
4111+ #[ cargo_test( nightly, reason = "public-dependency feature is unstable" ) ]
4112+ fn doc_direct_deps_always_documented ( ) {
4113+ // Direct dependencies should always be documented regardless of the
4114+ // public flag on them.
4115+ // foo -> bar (public = true), baz (public = false)
4116+ //
4117+ // Both bar and baz are direct deps of foo, so both get documented
4118+ // both before and after the fix.
4119+
4120+ Package :: new ( "bar" , "0.0.1" )
4121+ . file ( "src/lib.rs" , "pub fn bar() {}" )
4122+ . publish ( ) ;
4123+
4124+ Package :: new ( "baz" , "0.0.1" )
4125+ . file ( "src/lib.rs" , "pub fn baz() {}" )
4126+ . publish ( ) ;
4127+
4128+ let p = project ( )
4129+ . file (
4130+ "Cargo.toml" ,
4131+ r#"
4132+ cargo-features = ["public-dependency"]
4133+
4134+ [package]
4135+ name = "foo"
4136+ version = "0.0.1"
4137+ edition = "2021"
4138+
4139+ [dependencies]
4140+ bar = { version = "0.0.1", public = true }
4141+ baz = { version = "0.0.1", public = false }
4142+ "# ,
4143+ )
4144+ . file ( "src/lib.rs" , "pub fn foo() {}" )
4145+ . build ( ) ;
4146+
4147+ p. cargo ( "doc -Zpublic-dependency" )
4148+ . masquerade_as_nightly_cargo ( & [ "public-dependency" ] )
4149+ . with_stderr_data (
4150+ str![ [ r#"
4151+ [UPDATING] `dummy-registry` index
4152+ [LOCKING] 2 packages to latest compatible versions
4153+ [DOWNLOADING] crates ...
4154+ [DOWNLOADED] bar v0.0.1 (registry `dummy-registry`)
4155+ [DOWNLOADED] baz v0.0.1 (registry `dummy-registry`)
4156+ [DOCUMENTING] bar v0.0.1
4157+ [CHECKING] bar v0.0.1
4158+ [DOCUMENTING] baz v0.0.1
4159+ [CHECKING] baz v0.0.1
4160+ [DOCUMENTING] foo v0.0.1 ([ROOT]/foo)
4161+ [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
4162+ [GENERATED] [ROOT]/foo/target/doc/foo/index.html
4163+
4164+ "# ] ]
4165+ . unordered ( ) ,
4166+ )
4167+ . run ( ) ;
4168+
4169+ // Both direct deps are documented regardless of their public flag.
4170+ assert ! ( p. root( ) . join( "target/doc/foo/index.html" ) . is_file( ) ) ;
4171+ assert ! ( p. root( ) . join( "target/doc/bar/index.html" ) . is_file( ) ) ;
4172+ assert ! ( p. root( ) . join( "target/doc/baz/index.html" ) . is_file( ) ) ;
4173+ }
4174+
4175+ #[ cargo_test( nightly, reason = "public-dependency feature is unstable" ) ]
4176+ fn doc_with_private_dependency ( ) {
4177+ // foo -> bar (direct) -> baz (private dep of bar)
4178+ //
4179+ // Before the fix: baz is documented because cargo docs all transitive
4180+ // deps unconditionally, even when -Zpublic-dependency is active.
4181+ // After the fix: baz is NOT documented because it is a private
4182+ // transitive dep that callers of foo cannot use.
4183+
4184+ Package :: new ( "baz" , "0.0.1" )
4185+ . file ( "src/lib.rs" , "pub fn baz() {}" )
4186+ . publish ( ) ;
4187+
4188+ Package :: new ( "bar" , "0.0.1" )
4189+ . cargo_feature ( "public-dependency" )
4190+ . add_dep ( cargo_test_support:: registry:: Dependency :: new ( "baz" , "0.0.1" ) . public ( false ) )
4191+ . file ( "src/lib.rs" , "pub fn bar() {}" )
4192+ . publish ( ) ;
4193+
4194+ let p = project ( )
4195+ . file (
4196+ "Cargo.toml" ,
4197+ r#"
4198+ cargo-features = ["public-dependency"]
4199+
4200+ [package]
4201+ name = "foo"
4202+ version = "0.0.1"
4203+ edition = "2021"
4204+
4205+ [dependencies]
4206+ bar = "0.0.1"
4207+ "# ,
4208+ )
4209+ . file ( "src/lib.rs" , "pub fn foo() {}" )
4210+ . build ( ) ;
4211+
4212+ p. cargo ( "doc -Zpublic-dependency" )
4213+ . masquerade_as_nightly_cargo ( & [ "public-dependency" ] )
4214+ . with_stderr_data (
4215+ str![ [ r#"
4216+ [UPDATING] `dummy-registry` index
4217+ [LOCKING] 2 packages to latest compatible versions
4218+ [DOWNLOADING] crates ...
4219+ [DOWNLOADED] baz v0.0.1 (registry `dummy-registry`)
4220+ [DOWNLOADED] bar v0.0.1 (registry `dummy-registry`)
4221+ [DOCUMENTING] baz v0.0.1
4222+ [CHECKING] baz v0.0.1
4223+ [DOCUMENTING] bar v0.0.1
4224+ [CHECKING] bar v0.0.1
4225+ [DOCUMENTING] foo v0.0.1 ([ROOT]/foo)
4226+ [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
4227+ [GENERATED] [ROOT]/foo/target/doc/foo/index.html
4228+
4229+ "# ] ]
4230+ . unordered ( ) ,
4231+ )
4232+ . run ( ) ;
4233+
4234+ assert ! ( p. root( ) . join( "target/doc/foo/index.html" ) . is_file( ) ) ;
4235+ assert ! ( p. root( ) . join( "target/doc/bar/index.html" ) . is_file( ) ) ;
4236+ // Before the fix baz is documented even though it is a private dep.
4237+ assert ! ( p. root( ) . join( "target/doc/baz/index.html" ) . is_file( ) ) ;
4238+ }
4239+
4240+ #[ cargo_test( nightly, reason = "public-dependency feature is unstable" ) ]
4241+ fn doc_mixed_public_private_deps ( ) {
4242+ // foo -> pub_dep (public), priv_dep (private), priv_dep_with_dep (default)
4243+ // priv_dep_with_dep -> transitive
4244+ //
4245+ // Before the fix: transitive is documented because cargo docs every
4246+ // reachable dep regardless of public/private markers.
4247+ // After the fix: transitive is NOT documented because it is only
4248+ // reachable through a private dependency chain.
4249+
4250+ Package :: new ( "pub_dep" , "0.0.1" )
4251+ . file ( "src/lib.rs" , "pub fn pub_dep() {}" )
4252+ . publish ( ) ;
4253+
4254+ Package :: new ( "priv_dep" , "0.0.1" )
4255+ . file ( "src/lib.rs" , "pub fn priv_dep() {}" )
4256+ . publish ( ) ;
4257+
4258+ Package :: new ( "transitive" , "0.0.1" )
4259+ . file ( "src/lib.rs" , "pub fn transitive() {}" )
4260+ . publish ( ) ;
4261+
4262+ Package :: new ( "priv_dep_with_dep" , "0.0.1" )
4263+ . dep ( "transitive" , "0.0.1" )
4264+ . file ( "src/lib.rs" , "pub fn priv_dep_with_dep() {}" )
4265+ . publish ( ) ;
4266+
4267+ let p = project ( )
4268+ . file (
4269+ "Cargo.toml" ,
4270+ r#"
4271+ cargo-features = ["public-dependency"]
4272+
4273+ [package]
4274+ name = "foo"
4275+ version = "0.0.1"
4276+ edition = "2021"
4277+
4278+ [dependencies]
4279+ pub_dep = { version = "0.0.1", public = true }
4280+ priv_dep = { version = "0.0.1", public = false }
4281+ priv_dep_with_dep = "0.0.1"
4282+ "# ,
4283+ )
4284+ . file ( "src/lib.rs" , "pub fn foo() {}" )
4285+ . build ( ) ;
4286+
4287+ p. cargo ( "doc -Zpublic-dependency" )
4288+ . masquerade_as_nightly_cargo ( & [ "public-dependency" ] )
4289+ . run ( ) ;
4290+
4291+ // All direct deps are documented.
4292+ assert ! ( p. root( ) . join( "target/doc/foo/index.html" ) . is_file( ) ) ;
4293+ assert ! ( p. root( ) . join( "target/doc/pub_dep/index.html" ) . is_file( ) ) ;
4294+ assert ! ( p. root( ) . join( "target/doc/priv_dep/index.html" ) . is_file( ) ) ;
4295+ assert ! (
4296+ p. root( )
4297+ . join( "target/doc/priv_dep_with_dep/index.html" )
4298+ . is_file( )
4299+ ) ;
4300+ // Before the fix transitive is also documented even though it is an
4301+ // indirect private dep.
4302+ assert ! ( p. root( ) . join( "target/doc/transitive/index.html" ) . is_file( ) ) ;
4303+ }
0 commit comments