summaryrefslogtreecommitdiffstats
path: root/src/tools/cargo/tests/testsuite/features_namespaced.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/cargo/tests/testsuite/features_namespaced.rs')
-rw-r--r--src/tools/cargo/tests/testsuite/features_namespaced.rs1215
1 files changed, 1215 insertions, 0 deletions
diff --git a/src/tools/cargo/tests/testsuite/features_namespaced.rs b/src/tools/cargo/tests/testsuite/features_namespaced.rs
new file mode 100644
index 000000000..8ec2fc2e3
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/features_namespaced.rs
@@ -0,0 +1,1215 @@
+//! Tests for namespaced features.
+
+use super::features2::switch_to_resolver_2;
+use cargo_test_support::registry::{Dependency, Package, RegistryBuilder};
+use cargo_test_support::{project, publish};
+
+#[cargo_test]
+fn dependency_with_crate_syntax() {
+ // Registry dependency uses dep: syntax.
+ Package::new("baz", "1.0.0").publish();
+ Package::new("bar", "1.0.0")
+ .add_dep(Dependency::new("baz", "1.0").optional(true))
+ .feature("feat", &["dep:baz"])
+ .publish();
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ bar = {version="1.0", features=["feat"]}
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("check")
+ .with_stderr(
+ "\
+[UPDATING] [..]
+[DOWNLOADING] crates ...
+[DOWNLOADED] [..]
+[DOWNLOADED] [..]
+[CHECKING] baz v1.0.0
+[CHECKING] bar v1.0.0
+[CHECKING] foo v0.1.0 [..]
+[FINISHED] [..]
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn namespaced_invalid_feature() {
+ // Specifies a feature that doesn't exist.
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [features]
+ bar = ["baz"]
+ "#,
+ )
+ .file("src/main.rs", "")
+ .build();
+
+ p.cargo("check")
+ .with_status(101)
+ .with_stderr(
+ "\
+[ERROR] failed to parse manifest at `[..]`
+
+Caused by:
+ feature `bar` includes `baz` which is neither a dependency nor another feature
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn namespaced_invalid_dependency() {
+ // Specifies a dep:name that doesn't exist.
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [features]
+ bar = ["dep:baz"]
+ "#,
+ )
+ .file("src/main.rs", "")
+ .build();
+
+ p.cargo("check")
+ .with_status(101)
+ .with_stderr(
+ "\
+[ERROR] failed to parse manifest at `[..]`
+
+Caused by:
+ feature `bar` includes `dep:baz`, but `baz` is not listed as a dependency
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn namespaced_non_optional_dependency() {
+ // Specifies a dep:name for a dependency that is not optional.
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [features]
+ bar = ["dep:baz"]
+
+ [dependencies]
+ baz = "0.1"
+ "#,
+ )
+ .file("src/main.rs", "")
+ .build();
+
+ p.cargo("check")
+
+ .with_status(101)
+ .with_stderr(
+ "\
+[ERROR] failed to parse manifest at `[..]`
+
+Caused by:
+ feature `bar` includes `dep:baz`, but `baz` is not an optional dependency
+ A non-optional dependency of the same name is defined; consider adding `optional = true` to its definition.
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn namespaced_implicit_feature() {
+ // Backwards-compatible with old syntax.
+ Package::new("baz", "0.1.0").publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [features]
+ bar = ["baz"]
+
+ [dependencies]
+ baz = { version = "0.1", optional = true }
+ "#,
+ )
+ .file("src/main.rs", "fn main() {}")
+ .build();
+
+ p.cargo("check")
+ .with_stderr(
+ "\
+[UPDATING] [..]
+[CHECKING] foo v0.0.1 [..]
+[FINISHED] [..]
+",
+ )
+ .run();
+ p.cargo("check --features baz")
+ .with_stderr(
+ "\
+[DOWNLOADING] crates ...
+[DOWNLOADED] baz v0.1.0 [..]
+[CHECKING] baz v0.1.0
+[CHECKING] foo v0.0.1 [..]
+[FINISHED] [..]
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn namespaced_shadowed_dep() {
+ // An optional dependency is not listed in the features table, and its
+ // implicit feature is overridden.
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [features]
+ baz = []
+
+ [dependencies]
+ baz = { version = "0.1", optional = true }
+ "#,
+ )
+ .file("src/main.rs", "fn main() {}")
+ .build();
+
+ p.cargo("check")
+ .with_status(101)
+ .with_stderr(
+ "\
+[ERROR] failed to parse manifest at `[..]`
+
+Caused by:
+ optional dependency `baz` is not included in any feature
+ Make sure that `dep:baz` is included in one of features in the [features] table.
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn namespaced_shadowed_non_optional() {
+ // Able to specify a feature with the same name as a required dependency.
+ Package::new("baz", "0.1.0").publish();
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [features]
+ baz = []
+
+ [dependencies]
+ baz = "0.1"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("check").run();
+}
+
+#[cargo_test]
+fn namespaced_implicit_non_optional() {
+ // Includes a non-optional dependency in [features] table.
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [features]
+ bar = ["baz"]
+
+ [dependencies]
+ baz = "0.1"
+ "#,
+ )
+ .file("src/main.rs", "fn main() {}")
+ .build();
+
+ p.cargo("check").with_status(101).with_stderr(
+ "\
+[ERROR] failed to parse manifest at `[..]`
+
+Caused by:
+ feature `bar` includes `baz`, but `baz` is not an optional dependency
+ A non-optional dependency of the same name is defined; consider adding `optional = true` to its definition.
+",
+ ).run();
+}
+
+#[cargo_test]
+fn namespaced_same_name() {
+ // Explicitly listing an optional dependency in the [features] table.
+ Package::new("baz", "0.1.0").publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [features]
+ baz = ["dep:baz"]
+
+ [dependencies]
+ baz = { version = "0.1", optional = true }
+ "#,
+ )
+ .file(
+ "src/main.rs",
+ r#"
+ fn main() {
+ if cfg!(feature="baz") { println!("baz"); }
+ }
+ "#,
+ )
+ .build();
+
+ p.cargo("run")
+ .with_stderr(
+ "\
+[UPDATING] [..]
+[COMPILING] foo v0.0.1 [..]
+[FINISHED] [..]
+[RUNNING] [..]
+",
+ )
+ .with_stdout("")
+ .run();
+
+ p.cargo("run --features baz")
+ .with_stderr(
+ "\
+[DOWNLOADING] crates ...
+[DOWNLOADED] baz v0.1.0 [..]
+[COMPILING] baz v0.1.0
+[COMPILING] foo v0.0.1 [..]
+[FINISHED] [..]
+[RUNNING] [..]
+",
+ )
+ .with_stdout("baz")
+ .run();
+}
+
+#[cargo_test]
+fn no_implicit_feature() {
+ // Using `dep:` will not create an implicit feature.
+ Package::new("regex", "1.0.0").publish();
+ Package::new("lazy_static", "1.0.0").publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ regex = { version = "1.0", optional = true }
+ lazy_static = { version = "1.0", optional = true }
+
+ [features]
+ regex = ["dep:regex", "dep:lazy_static"]
+ "#,
+ )
+ .file(
+ "src/main.rs",
+ r#"
+ fn main() {
+ if cfg!(feature = "regex") { println!("regex"); }
+ if cfg!(feature = "lazy_static") { println!("lazy_static"); }
+ }
+ "#,
+ )
+ .build();
+
+ p.cargo("run")
+ .with_stderr(
+ "\
+[UPDATING] [..]
+[COMPILING] foo v0.1.0 [..]
+[FINISHED] [..]
+[RUNNING] `target/debug/foo[EXE]`
+",
+ )
+ .with_stdout("")
+ .run();
+
+ p.cargo("run --features regex")
+ .with_stderr_unordered(
+ "\
+[DOWNLOADING] crates ...
+[DOWNLOADED] regex v1.0.0 [..]
+[DOWNLOADED] lazy_static v1.0.0 [..]
+[COMPILING] regex v1.0.0
+[COMPILING] lazy_static v1.0.0
+[COMPILING] foo v0.1.0 [..]
+[FINISHED] [..]
+[RUNNING] `target/debug/foo[EXE]`
+",
+ )
+ .with_stdout("regex")
+ .run();
+
+ p.cargo("run --features lazy_static")
+ .with_stderr(
+ "\
+[ERROR] Package `foo v0.1.0 [..]` does not have feature `lazy_static`. \
+It has an optional dependency with that name, but that dependency uses the \"dep:\" \
+syntax in the features table, so it does not have an implicit feature with that name.
+",
+ )
+ .with_status(101)
+ .run();
+}
+
+#[cargo_test]
+fn crate_syntax_bad_name() {
+ // "dep:bar" = []
+ Package::new("bar", "1.0.0").publish();
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ bar = { version="1.0", optional=true }
+
+ [features]
+ "dep:bar" = []
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("check --features dep:bar")
+ .with_status(101)
+ .with_stderr(
+ "\
+[ERROR] failed to parse manifest at [..]/foo/Cargo.toml`
+
+Caused by:
+ feature named `dep:bar` is not allowed to start with `dep:`
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn crate_syntax_in_dep() {
+ // features = ["dep:baz"]
+ Package::new("baz", "1.0.0").publish();
+ Package::new("bar", "1.0.0")
+ .add_dep(Dependency::new("baz", "1.0").optional(true))
+ .publish();
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ bar = { version = "1.0", features = ["dep:baz"] }
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("check")
+ .with_status(101)
+ .with_stderr(
+ "\
+error: failed to parse manifest at `[CWD]/Cargo.toml`
+
+Caused by:
+ feature `dep:baz` in dependency `bar` is not allowed to use explicit `dep:` syntax
+ If you want to enable [..]
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn crate_syntax_cli() {
+ // --features dep:bar
+ Package::new("bar", "1.0.0").publish();
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ bar = { version = "1.0", optional=true }
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("check --features dep:bar")
+ .with_status(101)
+ .with_stderr(
+ "\
+[ERROR] feature `dep:bar` is not allowed to use explicit `dep:` syntax
+",
+ )
+ .run();
+
+ switch_to_resolver_2(&p);
+ p.cargo("check --features dep:bar")
+ .with_status(101)
+ .with_stderr(
+ "\
+[ERROR] feature `dep:bar` is not allowed to use explicit `dep:` syntax
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn crate_required_features() {
+ // required-features = ["dep:bar"]
+ Package::new("bar", "1.0.0").publish();
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ bar = { version = "1.0", optional=true }
+
+ [[bin]]
+ name = "foo"
+ required-features = ["dep:bar"]
+ "#,
+ )
+ .file("src/main.rs", "fn main() {}")
+ .build();
+
+ p.cargo("check")
+ .with_status(101)
+ .with_stderr(
+ "\
+[UPDATING] [..]
+[ERROR] invalid feature `dep:bar` in required-features of target `foo`: \
+`dep:` prefixed feature values are not allowed in required-features
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn json_exposed() {
+ // Checks that the implicit dep: values are exposed in JSON.
+ Package::new("bar", "1.0.0").publish();
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ bar = { version = "1.0", optional=true }
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("metadata --no-deps")
+ .with_json(
+ r#"
+ {
+ "packages": [
+ {
+ "name": "foo",
+ "version": "0.1.0",
+ "id": "foo 0.1.0 [..]",
+ "license": null,
+ "license_file": null,
+ "description": null,
+ "homepage": null,
+ "documentation": null,
+ "source": null,
+ "dependencies": "{...}",
+ "targets": "{...}",
+ "features": {
+ "bar": ["dep:bar"]
+ },
+ "manifest_path": "[..]foo/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [],
+ "categories": [],
+ "default_run": null,
+ "keywords": [],
+ "readme": null,
+ "repository": null,
+ "rust_version": null,
+ "edition": "2015",
+ "links": null
+ }
+ ],
+ "workspace_members": "{...}",
+ "resolve": null,
+ "target_directory": "[..]foo/target",
+ "version": 1,
+ "workspace_root": "[..]foo",
+ "metadata": null
+ }
+ "#,
+ )
+ .run();
+}
+
+#[cargo_test]
+fn crate_feature_with_explicit() {
+ // crate_name/feat_name syntax where crate_name already has a feature defined.
+ // NOTE: I don't know if this is actually ideal behavior.
+ Package::new("bar", "1.0.0")
+ .feature("bar_feat", &[])
+ .file(
+ "src/lib.rs",
+ r#"
+ #[cfg(not(feature="bar_feat"))]
+ compile_error!("bar_feat is not enabled");
+ "#,
+ )
+ .publish();
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ bar = { version="1.0", optional = true }
+
+ [features]
+ f1 = ["bar/bar_feat"]
+ bar = ["dep:bar", "f2"]
+ f2 = []
+ "#,
+ )
+ .file(
+ "src/lib.rs",
+ r#"
+ #[cfg(not(feature="bar"))]
+ compile_error!("bar should be enabled");
+
+ #[cfg(not(feature="f2"))]
+ compile_error!("f2 should be enabled");
+ "#,
+ )
+ .build();
+
+ p.cargo("check --features f1")
+ .with_stderr(
+ "\
+[UPDATING] [..]
+[DOWNLOADING] crates ...
+[DOWNLOADED] bar v1.0.0 [..]
+[CHECKING] bar v1.0.0
+[CHECKING] foo v0.1.0 [..]
+[FINISHED] [..]
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn optional_explicit_without_crate() {
+ // "feat" syntax when there is no implicit "feat" feature because it is
+ // explicitly listed elsewhere.
+ Package::new("bar", "1.0.0").publish();
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ bar = { version = "1.0", optional = true }
+
+ [features]
+ feat1 = ["dep:bar"]
+ feat2 = ["bar"]
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("check")
+ .with_status(101)
+ .with_stderr(
+ "\
+[ERROR] failed to parse manifest at [..]
+
+Caused by:
+ feature `feat2` includes `bar`, but `bar` is an optional dependency without an implicit feature
+ Use `dep:bar` to enable the dependency.
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn tree() {
+ Package::new("baz", "1.0.0").publish();
+ Package::new("bar", "1.0.0")
+ .add_dep(Dependency::new("baz", "1.0").optional(true))
+ .feature("feat1", &["dep:baz"])
+ .feature("feat2", &[])
+ .publish();
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ bar = { version = "1.0", features = ["feat1"], optional=true }
+
+ [features]
+ a = ["bar/feat2"]
+ bar = ["dep:bar"]
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("tree -e features")
+ .with_stdout("foo v0.1.0 ([ROOT]/foo)")
+ .run();
+
+ p.cargo("tree -e features --features a")
+ .with_stdout(
+ "\
+foo v0.1.0 ([ROOT]/foo)
+├── bar feature \"default\"
+│ └── bar v1.0.0
+│ └── baz feature \"default\"
+│ └── baz v1.0.0
+└── bar feature \"feat1\"
+ └── bar v1.0.0 (*)
+",
+ )
+ .run();
+
+ p.cargo("tree -e features --features a -i bar")
+ .with_stdout(
+ "\
+bar v1.0.0
+├── bar feature \"default\"
+│ └── foo v0.1.0 ([ROOT]/foo)
+│ ├── foo feature \"a\" (command-line)
+│ ├── foo feature \"bar\"
+│ │ └── foo feature \"a\" (command-line)
+│ └── foo feature \"default\" (command-line)
+├── bar feature \"feat1\"
+│ └── foo v0.1.0 ([ROOT]/foo) (*)
+└── bar feature \"feat2\"
+ └── foo feature \"a\" (command-line)
+",
+ )
+ .run();
+
+ p.cargo("tree -e features --features bar")
+ .with_stdout(
+ "\
+foo v0.1.0 ([ROOT]/foo)
+├── bar feature \"default\"
+│ └── bar v1.0.0
+│ └── baz feature \"default\"
+│ └── baz v1.0.0
+└── bar feature \"feat1\"
+ └── bar v1.0.0 (*)
+",
+ )
+ .run();
+
+ p.cargo("tree -e features --features bar -i bar")
+ .with_stdout(
+ "\
+bar v1.0.0
+├── bar feature \"default\"
+│ └── foo v0.1.0 ([ROOT]/foo)
+│ ├── foo feature \"bar\" (command-line)
+│ └── foo feature \"default\" (command-line)
+└── bar feature \"feat1\"
+ └── foo v0.1.0 ([ROOT]/foo) (*)
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn tree_no_implicit() {
+ // tree without an implicit feature
+ Package::new("bar", "1.0.0").publish();
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ bar = { version = "1.0", optional=true }
+
+ [features]
+ a = ["dep:bar"]
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("tree -e features")
+ .with_stdout("foo v0.1.0 ([ROOT]/foo)")
+ .run();
+
+ p.cargo("tree -e features --all-features")
+ .with_stdout(
+ "\
+foo v0.1.0 ([ROOT]/foo)
+└── bar feature \"default\"
+ └── bar v1.0.0
+",
+ )
+ .run();
+
+ p.cargo("tree -e features -i bar --all-features")
+ .with_stdout(
+ "\
+bar v1.0.0
+└── bar feature \"default\"
+ └── foo v0.1.0 ([ROOT]/foo)
+ ├── foo feature \"a\" (command-line)
+ └── foo feature \"default\" (command-line)
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn publish_no_implicit() {
+ let registry = RegistryBuilder::new().http_api().http_index().build();
+
+ // Does not include implicit features or dep: syntax on publish.
+ Package::new("opt-dep1", "1.0.0").publish();
+ Package::new("opt-dep2", "1.0.0").publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+ description = "foo"
+ license = "MIT"
+ homepage = "https://example.com/"
+
+ [dependencies]
+ opt-dep1 = { version = "1.0", optional = true }
+ opt-dep2 = { version = "1.0", optional = true }
+
+ [features]
+ feat = ["opt-dep1"]
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("publish --no-verify")
+ .replace_crates_io(registry.index_url())
+ .with_stderr(
+ "\
+[UPDATING] [..]
+[PACKAGING] foo v0.1.0 [..]
+[PACKAGED] [..]
+[UPLOADING] foo v0.1.0 [..]
+[UPLOADED] foo v0.1.0 [..]
+note: Waiting [..]
+You may press ctrl-c [..]
+[PUBLISHED] foo v0.1.0 [..]
+",
+ )
+ .run();
+
+ publish::validate_upload_with_contents(
+ r#"
+ {
+ "authors": [],
+ "badges": {},
+ "categories": [],
+ "deps": [
+ {
+ "default_features": true,
+ "features": [],
+ "kind": "normal",
+ "name": "opt-dep1",
+ "optional": true,
+ "target": null,
+ "version_req": "^1.0"
+ },
+ {
+ "default_features": true,
+ "features": [],
+ "kind": "normal",
+ "name": "opt-dep2",
+ "optional": true,
+ "target": null,
+ "version_req": "^1.0"
+ }
+ ],
+ "description": "foo",
+ "documentation": null,
+ "features": {
+ "feat": ["opt-dep1"]
+ },
+ "homepage": "https://example.com/",
+ "keywords": [],
+ "license": "MIT",
+ "license_file": null,
+ "links": null,
+ "name": "foo",
+ "readme": null,
+ "readme_file": null,
+ "repository": null,
+ "vers": "0.1.0"
+ }
+ "#,
+ "foo-0.1.0.crate",
+ &["Cargo.toml", "Cargo.toml.orig", "src/lib.rs"],
+ &[(
+ "Cargo.toml",
+ &format!(
+ r#"{}
+[package]
+name = "foo"
+version = "0.1.0"
+description = "foo"
+homepage = "https://example.com/"
+license = "MIT"
+
+[dependencies.opt-dep1]
+version = "1.0"
+optional = true
+
+[dependencies.opt-dep2]
+version = "1.0"
+optional = true
+
+[features]
+feat = ["opt-dep1"]
+"#,
+ cargo::core::package::MANIFEST_PREAMBLE
+ ),
+ )],
+ );
+}
+
+#[cargo_test]
+fn publish() {
+ let registry = RegistryBuilder::new().http_api().http_index().build();
+
+ // Publish behavior with explicit dep: syntax.
+ Package::new("bar", "1.0.0").publish();
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+ description = "foo"
+ license = "MIT"
+ homepage = "https://example.com/"
+
+ [dependencies]
+ bar = { version = "1.0", optional = true }
+
+ [features]
+ feat1 = []
+ feat2 = ["dep:bar"]
+ feat3 = ["feat2"]
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("publish")
+ .replace_crates_io(registry.index_url())
+ .with_stderr(
+ "\
+[UPDATING] [..]
+[PACKAGING] foo v0.1.0 [..]
+[VERIFYING] foo v0.1.0 [..]
+[UPDATING] [..]
+[COMPILING] foo v0.1.0 [..]
+[FINISHED] [..]
+[PACKAGED] [..]
+[UPLOADING] foo v0.1.0 [..]
+[UPLOADED] foo v0.1.0 [..]
+note: Waiting [..]
+You may press ctrl-c [..]
+[PUBLISHED] foo v0.1.0 [..]
+",
+ )
+ .run();
+
+ publish::validate_upload_with_contents(
+ r#"
+ {
+ "authors": [],
+ "badges": {},
+ "categories": [],
+ "deps": [
+ {
+ "default_features": true,
+ "features": [],
+ "kind": "normal",
+ "name": "bar",
+ "optional": true,
+ "target": null,
+ "version_req": "^1.0"
+ }
+ ],
+ "description": "foo",
+ "documentation": null,
+ "features": {
+ "feat1": [],
+ "feat2": ["dep:bar"],
+ "feat3": ["feat2"]
+ },
+ "homepage": "https://example.com/",
+ "keywords": [],
+ "license": "MIT",
+ "license_file": null,
+ "links": null,
+ "name": "foo",
+ "readme": null,
+ "readme_file": null,
+ "repository": null,
+ "vers": "0.1.0"
+ }
+ "#,
+ "foo-0.1.0.crate",
+ &["Cargo.toml", "Cargo.toml.orig", "src/lib.rs"],
+ &[(
+ "Cargo.toml",
+ &format!(
+ r#"{}
+[package]
+name = "foo"
+version = "0.1.0"
+description = "foo"
+homepage = "https://example.com/"
+license = "MIT"
+
+[dependencies.bar]
+version = "1.0"
+optional = true
+
+[features]
+feat1 = []
+feat2 = ["dep:bar"]
+feat3 = ["feat2"]
+"#,
+ cargo::core::package::MANIFEST_PREAMBLE
+ ),
+ )],
+ );
+}
+
+#[cargo_test]
+fn namespaced_feature_together() {
+ // Check for an error when `dep:` is used with `/`
+ Package::new("bar", "1.0.0")
+ .feature("bar-feat", &[])
+ .publish();
+
+ // Non-optional shouldn't have extra err.
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ bar = "1.0"
+
+ [features]
+ f1 = ["dep:bar/bar-feat"]
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+ p.cargo("check")
+ .with_status(101)
+ .with_stderr(
+ "\
+error: failed to parse manifest at `[ROOT]/foo/Cargo.toml`
+
+Caused by:
+ feature `f1` includes `dep:bar/bar-feat` with both `dep:` and `/`
+ To fix this, remove the `dep:` prefix.
+",
+ )
+ .run();
+
+ // Weak dependency shouldn't have extra err.
+ p.change_file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ bar = {version = "1.0", optional = true }
+
+ [features]
+ f1 = ["dep:bar?/bar-feat"]
+ "#,
+ );
+ p.cargo("check")
+ .with_status(101)
+ .with_stderr(
+ "\
+error: failed to parse manifest at `[ROOT]/foo/Cargo.toml`
+
+Caused by:
+ feature `f1` includes `dep:bar?/bar-feat` with both `dep:` and `/`
+ To fix this, remove the `dep:` prefix.
+",
+ )
+ .run();
+
+ // If dep: is already specified, shouldn't have extra err.
+ p.change_file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ bar = {version = "1.0", optional = true }
+
+ [features]
+ f1 = ["dep:bar", "dep:bar/bar-feat"]
+ "#,
+ );
+ p.cargo("check")
+ .with_status(101)
+ .with_stderr(
+ "\
+error: failed to parse manifest at `[ROOT]/foo/Cargo.toml`
+
+Caused by:
+ feature `f1` includes `dep:bar/bar-feat` with both `dep:` and `/`
+ To fix this, remove the `dep:` prefix.
+",
+ )
+ .run();
+
+ // Only when the other 3 cases aren't true should it give some extra help.
+ p.change_file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ bar = {version = "1.0", optional = true }
+
+ [features]
+ f1 = ["dep:bar/bar-feat"]
+ "#,
+ );
+ p.cargo("check")
+ .with_status(101)
+ .with_stderr(
+ "\
+error: failed to parse manifest at `[ROOT]/foo/Cargo.toml`
+
+Caused by:
+ feature `f1` includes `dep:bar/bar-feat` with both `dep:` and `/`
+ To fix this, remove the `dep:` prefix.
+ If the intent is to avoid creating an implicit feature `bar` for an optional \
+ dependency, then consider replacing this with two values:
+ \"dep:bar\", \"bar/bar-feat\"
+",
+ )
+ .run();
+}