mirror of
https://github.com/rust-lang/cargo.git
synced 2025-09-28 11:20:36 +00:00
fix: Remove implicit feature removal
Due to problems we ran into with #14016, we're removing implicit features from the 2024 edition to give ourselves more time to design it as we should. I could have added a new flag for this or made an EditionNext but I decided to remove it in the hopes to avoid any path dependency in solving this the next time.
This commit is contained in:
parent
2fd7321ee4
commit
7be5a2146b
@ -24,9 +24,7 @@ use crate::sources::{PathSource, SourceConfigMap, CRATES_IO_INDEX, CRATES_IO_REG
|
|||||||
use crate::util::edit_distance;
|
use crate::util::edit_distance;
|
||||||
use crate::util::errors::{CargoResult, ManifestError};
|
use crate::util::errors::{CargoResult, ManifestError};
|
||||||
use crate::util::interning::InternedString;
|
use crate::util::interning::InternedString;
|
||||||
use crate::util::lints::{
|
use crate::util::lints::{analyze_cargo_lints_table, check_im_a_teapot};
|
||||||
analyze_cargo_lints_table, check_im_a_teapot, check_implicit_features, unused_dependencies,
|
|
||||||
};
|
|
||||||
use crate::util::toml::{read_manifest, InheritableFields};
|
use crate::util::toml::{read_manifest, InheritableFields};
|
||||||
use crate::util::{
|
use crate::util::{
|
||||||
context::CargoResolverConfig, context::ConfigRelativePath, context::IncompatibleRustVersions,
|
context::CargoResolverConfig, context::ConfigRelativePath, context::IncompatibleRustVersions,
|
||||||
@ -1240,8 +1238,6 @@ impl<'gctx> Workspace<'gctx> {
|
|||||||
self.gctx,
|
self.gctx,
|
||||||
)?;
|
)?;
|
||||||
check_im_a_teapot(pkg, &path, &cargo_lints, &mut error_count, self.gctx)?;
|
check_im_a_teapot(pkg, &path, &cargo_lints, &mut error_count, self.gctx)?;
|
||||||
check_implicit_features(pkg, &path, &cargo_lints, &mut error_count, self.gctx)?;
|
|
||||||
unused_dependencies(pkg, &path, &cargo_lints, &mut error_count, self.gctx)?;
|
|
||||||
if error_count > 0 {
|
if error_count > 0 {
|
||||||
Err(crate::util::errors::AlreadyPrintedError::new(anyhow!(
|
Err(crate::util::errors::AlreadyPrintedError::new(anyhow!(
|
||||||
"encountered {error_count} errors(s) while running lints"
|
"encountered {error_count} errors(s) while running lints"
|
||||||
|
@ -55,13 +55,12 @@ use crate::core::compiler::CompileKind;
|
|||||||
use crate::core::compiler::RustcTargetData;
|
use crate::core::compiler::RustcTargetData;
|
||||||
use crate::core::resolver::features::{DiffMap, FeatureOpts, FeatureResolver, FeaturesFor};
|
use crate::core::resolver::features::{DiffMap, FeatureOpts, FeatureResolver, FeaturesFor};
|
||||||
use crate::core::resolver::{HasDevUnits, Resolve, ResolveBehavior};
|
use crate::core::resolver::{HasDevUnits, Resolve, ResolveBehavior};
|
||||||
|
use crate::core::PackageIdSpecQuery as _;
|
||||||
use crate::core::{Edition, MaybePackage, Package, PackageId, Workspace};
|
use crate::core::{Edition, MaybePackage, Package, PackageId, Workspace};
|
||||||
use crate::core::{FeatureValue, PackageIdSpecQuery as _};
|
|
||||||
use crate::ops::resolve::WorkspaceResolve;
|
use crate::ops::resolve::WorkspaceResolve;
|
||||||
use crate::ops::{self, CompileOptions};
|
use crate::ops::{self, CompileOptions};
|
||||||
use crate::util::diagnostic_server::{Message, RustfixDiagnosticServer};
|
use crate::util::diagnostic_server::{Message, RustfixDiagnosticServer};
|
||||||
use crate::util::errors::CargoResult;
|
use crate::util::errors::CargoResult;
|
||||||
use crate::util::interning::InternedString;
|
|
||||||
use crate::util::GlobalContext;
|
use crate::util::GlobalContext;
|
||||||
use crate::util::{existing_vcs_repo, LockServer, LockServerClient};
|
use crate::util::{existing_vcs_repo, LockServer, LockServerClient};
|
||||||
use crate::{drop_eprint, drop_eprintln};
|
use crate::{drop_eprint, drop_eprintln};
|
||||||
@ -287,7 +286,6 @@ fn migrate_manifests(ws: &Workspace<'_>, pkgs: &[&Package]) -> CargoResult<()> {
|
|||||||
fixes += rename_dep_fields_2024(workspace, "dependencies");
|
fixes += rename_dep_fields_2024(workspace, "dependencies");
|
||||||
}
|
}
|
||||||
|
|
||||||
fixes += add_feature_for_unused_deps(pkg, root, ws.gctx());
|
|
||||||
fixes += rename_table(root, "project", "package");
|
fixes += rename_table(root, "project", "package");
|
||||||
if let Some(target) = root.get_mut("lib").and_then(|t| t.as_table_like_mut()) {
|
if let Some(target) = root.get_mut("lib").and_then(|t| t.as_table_like_mut()) {
|
||||||
fixes += rename_target_fields_2024(target);
|
fixes += rename_target_fields_2024(target);
|
||||||
@ -437,79 +435,6 @@ fn rename_table(parent: &mut dyn toml_edit::TableLike, old: &str, new: &str) ->
|
|||||||
1
|
1
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_feature_for_unused_deps(
|
|
||||||
pkg: &Package,
|
|
||||||
parent: &mut dyn toml_edit::TableLike,
|
|
||||||
gctx: &GlobalContext,
|
|
||||||
) -> usize {
|
|
||||||
let manifest = pkg.manifest();
|
|
||||||
|
|
||||||
let activated_opt_deps = manifest
|
|
||||||
.normalized_toml()
|
|
||||||
.features()
|
|
||||||
.map(|map| {
|
|
||||||
map.values()
|
|
||||||
.flatten()
|
|
||||||
.filter_map(|f| match FeatureValue::new(InternedString::new(f)) {
|
|
||||||
FeatureValue::Dep { dep_name } => Some(dep_name.as_str()),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.collect::<HashSet<_>>()
|
|
||||||
})
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
let mut fixes = 0;
|
|
||||||
for dep in manifest.dependencies() {
|
|
||||||
let dep_name_in_toml = dep.name_in_toml();
|
|
||||||
if dep.is_optional() && !activated_opt_deps.contains(dep_name_in_toml.as_str()) {
|
|
||||||
if let Some(features) = parent
|
|
||||||
.entry("features")
|
|
||||||
.or_insert(toml_edit::table())
|
|
||||||
.as_table_like_mut()
|
|
||||||
{
|
|
||||||
let activate_dep = format!("dep:{dep_name_in_toml}");
|
|
||||||
let strong_dep_feature_prefix = format!("{dep_name_in_toml}/");
|
|
||||||
features
|
|
||||||
.entry(dep_name_in_toml.as_str())
|
|
||||||
.or_insert_with(|| {
|
|
||||||
fixes += 1;
|
|
||||||
toml_edit::Item::Value(toml_edit::Value::Array(
|
|
||||||
toml_edit::Array::from_iter([&activate_dep]),
|
|
||||||
))
|
|
||||||
});
|
|
||||||
// Ensure `dep:dep_name` is present for `dep_name/feature_name` since `dep:` is the
|
|
||||||
// only way to guarantee an optional dependency is available for use.
|
|
||||||
//
|
|
||||||
// The way we avoid implicitly creating features in Edition2024 is we remove the
|
|
||||||
// dependency from `normalized_toml` if there is no `dep:` syntax as that is the only
|
|
||||||
// syntax that suppresses the creation of the implicit feature.
|
|
||||||
for (feature_name, activations) in features.iter_mut() {
|
|
||||||
let Some(activations) = activations.as_array_mut() else {
|
|
||||||
let _ = gctx.shell().warn(format_args!("skipping fix of feature `{feature_name}` in package `{}`: unsupported feature schema", pkg.name()));
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
if activations
|
|
||||||
.iter()
|
|
||||||
.any(|a| a.as_str().map(|a| a == activate_dep).unwrap_or(false))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let Some(activate_dep_pos) = activations.iter().position(|a| {
|
|
||||||
a.as_str()
|
|
||||||
.map(|a| a.starts_with(&strong_dep_feature_prefix))
|
|
||||||
.unwrap_or(false)
|
|
||||||
}) else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
fixes += 1;
|
|
||||||
activations.insert(activate_dep_pos, &activate_dep);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fixes
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_resolver_change<'gctx>(
|
fn check_resolver_change<'gctx>(
|
||||||
ws: &Workspace<'gctx>,
|
ws: &Workspace<'gctx>,
|
||||||
target_data: &mut RustcTargetData<'gctx>,
|
target_data: &mut RustcTargetData<'gctx>,
|
||||||
|
@ -1,24 +1,15 @@
|
|||||||
use crate::core::dependency::DepKind;
|
use crate::core::{Edition, Feature, Features, Manifest, Package};
|
||||||
use crate::core::FeatureValue::Dep;
|
|
||||||
use crate::core::{Edition, Feature, FeatureValue, Features, Manifest, Package};
|
|
||||||
use crate::util::interning::InternedString;
|
|
||||||
use crate::{CargoResult, GlobalContext};
|
use crate::{CargoResult, GlobalContext};
|
||||||
use annotate_snippets::{Level, Snippet};
|
use annotate_snippets::{Level, Snippet};
|
||||||
use cargo_util_schemas::manifest::{TomlLintLevel, TomlToolLints};
|
use cargo_util_schemas::manifest::{TomlLintLevel, TomlToolLints};
|
||||||
use pathdiff::diff_paths;
|
use pathdiff::diff_paths;
|
||||||
use std::collections::HashSet;
|
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::ops::Range;
|
use std::ops::Range;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use toml_edit::ImDocument;
|
use toml_edit::ImDocument;
|
||||||
|
|
||||||
const LINT_GROUPS: &[LintGroup] = &[TEST_DUMMY_UNSTABLE];
|
const LINT_GROUPS: &[LintGroup] = &[TEST_DUMMY_UNSTABLE];
|
||||||
pub const LINTS: &[Lint] = &[
|
pub const LINTS: &[Lint] = &[IM_A_TEAPOT, UNKNOWN_LINTS];
|
||||||
IMPLICIT_FEATURES,
|
|
||||||
IM_A_TEAPOT,
|
|
||||||
UNKNOWN_LINTS,
|
|
||||||
UNUSED_OPTIONAL_DEPENDENCY,
|
|
||||||
];
|
|
||||||
|
|
||||||
pub fn analyze_cargo_lints_table(
|
pub fn analyze_cargo_lints_table(
|
||||||
pkg: &Package,
|
pkg: &Package,
|
||||||
@ -487,127 +478,6 @@ pub fn check_im_a_teapot(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
const IMPLICIT_FEATURES: Lint = Lint {
|
|
||||||
name: "implicit_features",
|
|
||||||
desc: "implicit features for optional dependencies is deprecated and will be unavailable in the 2024 edition",
|
|
||||||
groups: &[],
|
|
||||||
default_level: LintLevel::Allow,
|
|
||||||
edition_lint_opts: None,
|
|
||||||
feature_gate: None,
|
|
||||||
docs: Some(r#"
|
|
||||||
### What it does
|
|
||||||
Checks for implicit features for optional dependencies
|
|
||||||
|
|
||||||
### Why it is bad
|
|
||||||
By default, cargo will treat any optional dependency as a [feature]. As of
|
|
||||||
cargo 1.60, these can be disabled by declaring a feature that activates the
|
|
||||||
optional dependency as `dep:<name>` (see [RFC #3143]).
|
|
||||||
|
|
||||||
In the 2024 edition, `cargo` will stop exposing optional dependencies as
|
|
||||||
features implicitly, requiring users to add `foo = ["dep:foo"]` if they
|
|
||||||
still want it exposed.
|
|
||||||
|
|
||||||
For more information, see [RFC #3491]
|
|
||||||
|
|
||||||
### Example
|
|
||||||
```toml
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
bar = { version = "0.1.0", optional = true }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
# No explicit feature activation for `bar`
|
|
||||||
```
|
|
||||||
|
|
||||||
Instead, the dependency should have an explicit feature:
|
|
||||||
```toml
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
bar = { version = "0.1.0", optional = true }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
bar = ["dep:bar"]
|
|
||||||
```
|
|
||||||
|
|
||||||
[feature]: https://doc.rust-lang.org/cargo/reference/features.html
|
|
||||||
[RFC #3143]: https://rust-lang.github.io/rfcs/3143-cargo-weak-namespaced-features.html
|
|
||||||
[RFC #3491]: https://rust-lang.github.io/rfcs/3491-remove-implicit-features.html
|
|
||||||
"#
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn check_implicit_features(
|
|
||||||
pkg: &Package,
|
|
||||||
path: &Path,
|
|
||||||
pkg_lints: &TomlToolLints,
|
|
||||||
error_count: &mut usize,
|
|
||||||
gctx: &GlobalContext,
|
|
||||||
) -> CargoResult<()> {
|
|
||||||
let manifest = pkg.manifest();
|
|
||||||
let edition = manifest.edition();
|
|
||||||
// In Edition 2024+, instead of creating optional features, the dependencies are unused.
|
|
||||||
// See `UNUSED_OPTIONAL_DEPENDENCY`
|
|
||||||
if edition >= Edition::Edition2024 {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let (lint_level, reason) =
|
|
||||||
IMPLICIT_FEATURES.level(pkg_lints, edition, manifest.unstable_features());
|
|
||||||
if lint_level == LintLevel::Allow {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let activated_opt_deps = manifest
|
|
||||||
.normalized_toml()
|
|
||||||
.features()
|
|
||||||
.map(|map| {
|
|
||||||
map.values()
|
|
||||||
.flatten()
|
|
||||||
.filter_map(|f| match FeatureValue::new(InternedString::new(f)) {
|
|
||||||
Dep { dep_name } => Some(dep_name.as_str()),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.collect::<HashSet<_>>()
|
|
||||||
})
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
let mut emitted_source = None;
|
|
||||||
for dep in manifest.dependencies() {
|
|
||||||
let dep_name_in_toml = dep.name_in_toml();
|
|
||||||
if !dep.is_optional() || activated_opt_deps.contains(dep_name_in_toml.as_str()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if lint_level == LintLevel::Forbid || lint_level == LintLevel::Deny {
|
|
||||||
*error_count += 1;
|
|
||||||
}
|
|
||||||
let mut toml_path = vec![dep.kind().kind_table(), dep_name_in_toml.as_str()];
|
|
||||||
let platform = dep.platform().map(|p| p.to_string());
|
|
||||||
if let Some(platform) = platform.as_ref() {
|
|
||||||
toml_path.insert(0, platform);
|
|
||||||
toml_path.insert(0, "target");
|
|
||||||
}
|
|
||||||
let level = lint_level.to_diagnostic_level();
|
|
||||||
let manifest_path = rel_cwd_manifest_path(path, gctx);
|
|
||||||
let mut message = level.title(IMPLICIT_FEATURES.desc).snippet(
|
|
||||||
Snippet::source(manifest.contents())
|
|
||||||
.origin(&manifest_path)
|
|
||||||
.annotation(level.span(get_span(manifest.document(), &toml_path, false).unwrap()))
|
|
||||||
.fold(true),
|
|
||||||
);
|
|
||||||
if emitted_source.is_none() {
|
|
||||||
emitted_source = Some(format!(
|
|
||||||
"`cargo::{}` is set to `{lint_level}` {reason}",
|
|
||||||
IMPLICIT_FEATURES.name
|
|
||||||
));
|
|
||||||
message = message.footer(Level::Note.title(emitted_source.as_ref().unwrap()));
|
|
||||||
}
|
|
||||||
gctx.shell().print_message(message)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
const UNKNOWN_LINTS: Lint = Lint {
|
const UNKNOWN_LINTS: Lint = Lint {
|
||||||
name: "unknown_lints",
|
name: "unknown_lints",
|
||||||
desc: "unknown lint",
|
desc: "unknown lint",
|
||||||
@ -735,151 +605,6 @@ fn output_unknown_lints(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
const UNUSED_OPTIONAL_DEPENDENCY: Lint = Lint {
|
|
||||||
name: "unused_optional_dependency",
|
|
||||||
desc: "unused optional dependency",
|
|
||||||
groups: &[],
|
|
||||||
default_level: LintLevel::Warn,
|
|
||||||
edition_lint_opts: None,
|
|
||||||
feature_gate: None,
|
|
||||||
docs: Some(
|
|
||||||
r#"
|
|
||||||
### What it does
|
|
||||||
Checks for optional dependencies that are not activated by any feature
|
|
||||||
|
|
||||||
### Why it is bad
|
|
||||||
Starting in the 2024 edition, `cargo` no longer implicitly creates features
|
|
||||||
for optional dependencies (see [RFC #3491]). This means that any optional
|
|
||||||
dependency not specified with `"dep:<name>"` in some feature is now unused.
|
|
||||||
This change may be surprising to users who have been using the implicit
|
|
||||||
features `cargo` has been creating for optional dependencies.
|
|
||||||
|
|
||||||
### Example
|
|
||||||
```toml
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
bar = { version = "0.1.0", optional = true }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
# No explicit feature activation for `bar`
|
|
||||||
```
|
|
||||||
|
|
||||||
Instead, the dependency should be removed or activated in a feature:
|
|
||||||
```toml
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
bar = { version = "0.1.0", optional = true }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
bar = ["dep:bar"]
|
|
||||||
```
|
|
||||||
|
|
||||||
[RFC #3491]: https://rust-lang.github.io/rfcs/3491-remove-implicit-features.html
|
|
||||||
"#,
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn unused_dependencies(
|
|
||||||
pkg: &Package,
|
|
||||||
path: &Path,
|
|
||||||
pkg_lints: &TomlToolLints,
|
|
||||||
error_count: &mut usize,
|
|
||||||
gctx: &GlobalContext,
|
|
||||||
) -> CargoResult<()> {
|
|
||||||
let manifest = pkg.manifest();
|
|
||||||
let edition = manifest.edition();
|
|
||||||
// Unused optional dependencies can only exist on edition 2024+
|
|
||||||
if edition < Edition::Edition2024 {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let (lint_level, reason) =
|
|
||||||
UNUSED_OPTIONAL_DEPENDENCY.level(pkg_lints, edition, manifest.unstable_features());
|
|
||||||
if lint_level == LintLevel::Allow {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
let mut emitted_source = None;
|
|
||||||
let original_toml = manifest.original_toml();
|
|
||||||
// Unused dependencies were stripped from the manifest, leaving only the used ones
|
|
||||||
let used_dependencies = manifest
|
|
||||||
.dependencies()
|
|
||||||
.into_iter()
|
|
||||||
.map(|d| d.name_in_toml().to_string())
|
|
||||||
.collect::<HashSet<String>>();
|
|
||||||
let mut orig_deps = vec![
|
|
||||||
(
|
|
||||||
original_toml.dependencies.as_ref(),
|
|
||||||
vec![DepKind::Normal.kind_table()],
|
|
||||||
),
|
|
||||||
(
|
|
||||||
original_toml.dev_dependencies.as_ref(),
|
|
||||||
vec![DepKind::Development.kind_table()],
|
|
||||||
),
|
|
||||||
(
|
|
||||||
original_toml.build_dependencies.as_ref(),
|
|
||||||
vec![DepKind::Build.kind_table()],
|
|
||||||
),
|
|
||||||
];
|
|
||||||
for (name, platform) in original_toml.target.iter().flatten() {
|
|
||||||
orig_deps.push((
|
|
||||||
platform.dependencies.as_ref(),
|
|
||||||
vec!["target", name, DepKind::Normal.kind_table()],
|
|
||||||
));
|
|
||||||
orig_deps.push((
|
|
||||||
platform.dev_dependencies.as_ref(),
|
|
||||||
vec!["target", name, DepKind::Development.kind_table()],
|
|
||||||
));
|
|
||||||
orig_deps.push((
|
|
||||||
platform.build_dependencies.as_ref(),
|
|
||||||
vec!["target", name, DepKind::Normal.kind_table()],
|
|
||||||
));
|
|
||||||
}
|
|
||||||
for (deps, toml_path) in orig_deps {
|
|
||||||
if let Some(deps) = deps {
|
|
||||||
for name in deps.keys() {
|
|
||||||
if !used_dependencies.contains(name.as_str()) {
|
|
||||||
if lint_level == LintLevel::Forbid || lint_level == LintLevel::Deny {
|
|
||||||
*error_count += 1;
|
|
||||||
}
|
|
||||||
let toml_path = toml_path
|
|
||||||
.iter()
|
|
||||||
.map(|s| *s)
|
|
||||||
.chain(std::iter::once(name.as_str()))
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
let level = lint_level.to_diagnostic_level();
|
|
||||||
let manifest_path = rel_cwd_manifest_path(path, gctx);
|
|
||||||
|
|
||||||
let mut message = level.title(UNUSED_OPTIONAL_DEPENDENCY.desc).snippet(
|
|
||||||
Snippet::source(manifest.contents())
|
|
||||||
.origin(&manifest_path)
|
|
||||||
.annotation(level.span(
|
|
||||||
get_span(manifest.document(), toml_path.as_slice(), false).unwrap(),
|
|
||||||
))
|
|
||||||
.fold(true),
|
|
||||||
);
|
|
||||||
if emitted_source.is_none() {
|
|
||||||
emitted_source = Some(format!(
|
|
||||||
"`cargo::{}` is set to `{lint_level}` {reason}",
|
|
||||||
UNUSED_OPTIONAL_DEPENDENCY.name
|
|
||||||
));
|
|
||||||
message =
|
|
||||||
message.footer(Level::Note.title(emitted_source.as_ref().unwrap()));
|
|
||||||
}
|
|
||||||
let help = format!(
|
|
||||||
"remove the dependency or activate it in a feature with `dep:{name}`"
|
|
||||||
);
|
|
||||||
message = message.footer(Level::Help.title(&help));
|
|
||||||
|
|
||||||
gctx.shell().print_message(message)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use annotate_snippets::{Level, Snippet};
|
use annotate_snippets::{Level, Snippet};
|
||||||
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
use std::collections::{BTreeMap, BTreeSet, HashMap};
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
@ -24,7 +24,6 @@ use crate::core::compiler::{CompileKind, CompileTarget};
|
|||||||
use crate::core::dependency::{Artifact, ArtifactTarget, DepKind};
|
use crate::core::dependency::{Artifact, ArtifactTarget, DepKind};
|
||||||
use crate::core::manifest::{ManifestMetadata, TargetSourcePath};
|
use crate::core::manifest::{ManifestMetadata, TargetSourcePath};
|
||||||
use crate::core::resolver::ResolveBehavior;
|
use crate::core::resolver::ResolveBehavior;
|
||||||
use crate::core::FeatureValue::Dep;
|
|
||||||
use crate::core::{find_workspace_root, resolve_relative_path, CliUnstable, FeatureValue};
|
use crate::core::{find_workspace_root, resolve_relative_path, CliUnstable, FeatureValue};
|
||||||
use crate::core::{Dependency, Manifest, Package, PackageId, Summary, Target};
|
use crate::core::{Dependency, Manifest, Package, PackageId, Summary, Target};
|
||||||
use crate::core::{Edition, EitherManifest, Feature, Features, VirtualManifest, Workspace};
|
use crate::core::{Edition, EitherManifest, Feature, Features, VirtualManifest, Workspace};
|
||||||
@ -372,26 +371,11 @@ fn normalize_toml(
|
|||||||
errors,
|
errors,
|
||||||
)?);
|
)?);
|
||||||
|
|
||||||
let activated_opt_deps = normalized_toml
|
|
||||||
.features
|
|
||||||
.as_ref()
|
|
||||||
.map(|map| {
|
|
||||||
map.values()
|
|
||||||
.flatten()
|
|
||||||
.filter_map(|f| match FeatureValue::new(InternedString::new(f)) {
|
|
||||||
Dep { dep_name } => Some(dep_name.as_str()),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.collect::<HashSet<_>>()
|
|
||||||
})
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
normalized_toml.dependencies = normalize_dependencies(
|
normalized_toml.dependencies = normalize_dependencies(
|
||||||
gctx,
|
gctx,
|
||||||
edition,
|
edition,
|
||||||
&features,
|
&features,
|
||||||
original_toml.dependencies.as_ref(),
|
original_toml.dependencies.as_ref(),
|
||||||
&activated_opt_deps,
|
|
||||||
None,
|
None,
|
||||||
&inherit,
|
&inherit,
|
||||||
&workspace_root,
|
&workspace_root,
|
||||||
@ -412,7 +396,6 @@ fn normalize_toml(
|
|||||||
edition,
|
edition,
|
||||||
&features,
|
&features,
|
||||||
original_toml.dev_dependencies(),
|
original_toml.dev_dependencies(),
|
||||||
&activated_opt_deps,
|
|
||||||
Some(DepKind::Development),
|
Some(DepKind::Development),
|
||||||
&inherit,
|
&inherit,
|
||||||
&workspace_root,
|
&workspace_root,
|
||||||
@ -433,7 +416,6 @@ fn normalize_toml(
|
|||||||
edition,
|
edition,
|
||||||
&features,
|
&features,
|
||||||
original_toml.build_dependencies(),
|
original_toml.build_dependencies(),
|
||||||
&activated_opt_deps,
|
|
||||||
Some(DepKind::Build),
|
Some(DepKind::Build),
|
||||||
&inherit,
|
&inherit,
|
||||||
&workspace_root,
|
&workspace_root,
|
||||||
@ -447,7 +429,6 @@ fn normalize_toml(
|
|||||||
edition,
|
edition,
|
||||||
&features,
|
&features,
|
||||||
platform.dependencies.as_ref(),
|
platform.dependencies.as_ref(),
|
||||||
&activated_opt_deps,
|
|
||||||
None,
|
None,
|
||||||
&inherit,
|
&inherit,
|
||||||
&workspace_root,
|
&workspace_root,
|
||||||
@ -468,7 +449,6 @@ fn normalize_toml(
|
|||||||
edition,
|
edition,
|
||||||
&features,
|
&features,
|
||||||
platform.dev_dependencies(),
|
platform.dev_dependencies(),
|
||||||
&activated_opt_deps,
|
|
||||||
Some(DepKind::Development),
|
Some(DepKind::Development),
|
||||||
&inherit,
|
&inherit,
|
||||||
&workspace_root,
|
&workspace_root,
|
||||||
@ -489,7 +469,6 @@ fn normalize_toml(
|
|||||||
edition,
|
edition,
|
||||||
&features,
|
&features,
|
||||||
platform.build_dependencies(),
|
platform.build_dependencies(),
|
||||||
&activated_opt_deps,
|
|
||||||
Some(DepKind::Build),
|
Some(DepKind::Build),
|
||||||
&inherit,
|
&inherit,
|
||||||
&workspace_root,
|
&workspace_root,
|
||||||
@ -756,7 +735,6 @@ fn normalize_dependencies<'a>(
|
|||||||
edition: Edition,
|
edition: Edition,
|
||||||
features: &Features,
|
features: &Features,
|
||||||
orig_deps: Option<&BTreeMap<manifest::PackageName, manifest::InheritableDependency>>,
|
orig_deps: Option<&BTreeMap<manifest::PackageName, manifest::InheritableDependency>>,
|
||||||
activated_opt_deps: &HashSet<&str>,
|
|
||||||
kind: Option<DepKind>,
|
kind: Option<DepKind>,
|
||||||
inherit: &dyn Fn() -> CargoResult<&'a InheritableFields>,
|
inherit: &dyn Fn() -> CargoResult<&'a InheritableFields>,
|
||||||
workspace_root: &dyn Fn() -> CargoResult<&'a Path>,
|
workspace_root: &dyn Fn() -> CargoResult<&'a Path>,
|
||||||
@ -818,18 +796,10 @@ fn normalize_dependencies<'a>(
|
|||||||
.with_context(|| format!("resolving path dependency {name_in_toml}"))?;
|
.with_context(|| format!("resolving path dependency {name_in_toml}"))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the dependency is not optional, it is always used
|
deps.insert(
|
||||||
// if the dependency is optional and activated, it is used
|
name_in_toml.clone(),
|
||||||
// if the dependency is optional and not activated, it is not used
|
manifest::InheritableDependency::Value(resolved.clone()),
|
||||||
let is_dep_activated =
|
);
|
||||||
!resolved.is_optional() || activated_opt_deps.contains(name_in_toml.as_str());
|
|
||||||
// If the edition is less than 2024, we don't need to check for unused optional dependencies
|
|
||||||
if edition < Edition::Edition2024 || is_dep_activated {
|
|
||||||
deps.insert(
|
|
||||||
name_in_toml.clone(),
|
|
||||||
manifest::InheritableDependency::Value(resolved.clone()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(Some(deps))
|
Ok(Some(deps))
|
||||||
}
|
}
|
||||||
|
@ -2,60 +2,10 @@
|
|||||||
|
|
||||||
Note: [Cargo's linting system is unstable](unstable.md#lintscargo) and can only be used on nightly toolchains
|
Note: [Cargo's linting system is unstable](unstable.md#lintscargo) and can only be used on nightly toolchains
|
||||||
|
|
||||||
## Allowed-by-default
|
|
||||||
|
|
||||||
These lints are all set to the 'allow' level by default.
|
|
||||||
- [`implicit_features`](#implicit_features)
|
|
||||||
|
|
||||||
## Warn-by-default
|
## Warn-by-default
|
||||||
|
|
||||||
These lints are all set to the 'warn' level by default.
|
These lints are all set to the 'warn' level by default.
|
||||||
- [`unknown_lints`](#unknown_lints)
|
- [`unknown_lints`](#unknown_lints)
|
||||||
- [`unused_optional_dependency`](#unused_optional_dependency)
|
|
||||||
|
|
||||||
## `implicit_features`
|
|
||||||
Set to `allow` by default
|
|
||||||
|
|
||||||
### What it does
|
|
||||||
Checks for implicit features for optional dependencies
|
|
||||||
|
|
||||||
### Why it is bad
|
|
||||||
By default, cargo will treat any optional dependency as a [feature]. As of
|
|
||||||
cargo 1.60, these can be disabled by declaring a feature that activates the
|
|
||||||
optional dependency as `dep:<name>` (see [RFC #3143]).
|
|
||||||
|
|
||||||
In the 2024 edition, `cargo` will stop exposing optional dependencies as
|
|
||||||
features implicitly, requiring users to add `foo = ["dep:foo"]` if they
|
|
||||||
still want it exposed.
|
|
||||||
|
|
||||||
For more information, see [RFC #3491]
|
|
||||||
|
|
||||||
### Example
|
|
||||||
```toml
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
bar = { version = "0.1.0", optional = true }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
# No explicit feature activation for `bar`
|
|
||||||
```
|
|
||||||
|
|
||||||
Instead, the dependency should have an explicit feature:
|
|
||||||
```toml
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
bar = { version = "0.1.0", optional = true }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
bar = ["dep:bar"]
|
|
||||||
```
|
|
||||||
|
|
||||||
[feature]: https://doc.rust-lang.org/cargo/reference/features.html
|
|
||||||
[RFC #3143]: https://rust-lang.github.io/rfcs/3143-cargo-weak-namespaced-features.html
|
|
||||||
[RFC #3491]: https://rust-lang.github.io/rfcs/3491-remove-implicit-features.html
|
|
||||||
|
|
||||||
|
|
||||||
## `unknown_lints`
|
## `unknown_lints`
|
||||||
Set to `warn` by default
|
Set to `warn` by default
|
||||||
@ -76,41 +26,3 @@ this-lint-does-not-exist = "warn"
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## `unused_optional_dependency`
|
|
||||||
Set to `warn` by default
|
|
||||||
|
|
||||||
### What it does
|
|
||||||
Checks for optional dependencies that are not activated by any feature
|
|
||||||
|
|
||||||
### Why it is bad
|
|
||||||
Starting in the 2024 edition, `cargo` no longer implicitly creates features
|
|
||||||
for optional dependencies (see [RFC #3491]). This means that any optional
|
|
||||||
dependency not specified with `"dep:<name>"` in some feature is now unused.
|
|
||||||
This change may be surprising to users who have been using the implicit
|
|
||||||
features `cargo` has been creating for optional dependencies.
|
|
||||||
|
|
||||||
### Example
|
|
||||||
```toml
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
bar = { version = "0.1.0", optional = true }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
# No explicit feature activation for `bar`
|
|
||||||
```
|
|
||||||
|
|
||||||
Instead, the dependency should be removed or activated in a feature:
|
|
||||||
```toml
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
bar = { version = "0.1.0", optional = true }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
bar = ["dep:bar"]
|
|
||||||
```
|
|
||||||
|
|
||||||
[RFC #3491]: https://rust-lang.github.io/rfcs/3491-remove-implicit-features.html
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1846,63 +1846,6 @@ fn features_option_given_twice() {
|
|||||||
p.cargo("check --features a --features b").run();
|
p.cargo("check --features a --features b").run();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cargo_test(nightly, reason = "edition2024 is not stable")]
|
|
||||||
fn strong_dep_feature_edition2024() {
|
|
||||||
let p = project()
|
|
||||||
.file(
|
|
||||||
"Cargo.toml",
|
|
||||||
r#"
|
|
||||||
cargo-features = ["edition2024"]
|
|
||||||
[package]
|
|
||||||
name = "foo"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
optional_dep = ["optional_dep/foo"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
optional_dep = { path = "optional_dep", optional = true }
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.file(
|
|
||||||
"src/main.rs",
|
|
||||||
r#"
|
|
||||||
fn main() {}
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.file(
|
|
||||||
"optional_dep/Cargo.toml",
|
|
||||||
r#"
|
|
||||||
[package]
|
|
||||||
name = "optional_dep"
|
|
||||||
[features]
|
|
||||||
foo = []
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.file(
|
|
||||||
"optional_dep/src/lib.rs",
|
|
||||||
r#"
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
p.cargo("metadata")
|
|
||||||
.masquerade_as_nightly_cargo(&["edition2024"])
|
|
||||||
.with_status(101)
|
|
||||||
.with_stderr_data(str![[r#"
|
|
||||||
[ERROR] feature `optional_dep` includes `optional_dep/foo`, but `optional_dep` is not a dependency
|
|
||||||
--> Cargo.toml:9:32
|
|
||||||
|
|
|
||||||
9 | optional_dep = ["optional_dep/foo"]
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
|
||||||
[ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml`
|
|
||||||
|
|
||||||
"#]])
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cargo_test]
|
#[cargo_test]
|
||||||
fn multi_multi_features() {
|
fn multi_multi_features() {
|
||||||
let p = project()
|
let p = project()
|
||||||
|
@ -2596,212 +2596,6 @@ a = {path = "a", default-features = false}
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cargo_test]
|
|
||||||
fn add_feature_for_unused_dep() {
|
|
||||||
Package::new("regular-dep", "0.1.0").publish();
|
|
||||||
Package::new("build-dep", "0.1.0").publish();
|
|
||||||
Package::new("target-dep", "0.1.0").publish();
|
|
||||||
let p = project()
|
|
||||||
.file(
|
|
||||||
"Cargo.toml",
|
|
||||||
r#"
|
|
||||||
[package]
|
|
||||||
name = "foo"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
regular-dep = { version = "0.1.0", optional = true }
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
build-dep = { version = "0.1.0", optional = true }
|
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
|
||||||
target-dep = { version = "0.1.0", optional = true }
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.file("src/lib.rs", "")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
p.cargo("fix --edition --allow-no-vcs")
|
|
||||||
.masquerade_as_nightly_cargo(&["edition2024"])
|
|
||||||
.with_stderr_data(str![[r#"
|
|
||||||
[MIGRATING] Cargo.toml from 2021 edition to 2024
|
|
||||||
[FIXED] Cargo.toml (3 fixes)
|
|
||||||
[UPDATING] `dummy-registry` index
|
|
||||||
[LOCKING] 3 packages to latest compatible versions
|
|
||||||
[CHECKING] foo v0.1.0 ([ROOT]/foo)
|
|
||||||
[MIGRATING] src/lib.rs from 2021 edition to 2024
|
|
||||||
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
|
|
||||||
|
|
||||||
"#]])
|
|
||||||
.run();
|
|
||||||
assert_e2e().eq(
|
|
||||||
p.read_file("Cargo.toml"),
|
|
||||||
str![[r#"
|
|
||||||
|
|
||||||
[package]
|
|
||||||
name = "foo"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
regular-dep = { version = "0.1.0", optional = true }
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
build-dep = { version = "0.1.0", optional = true }
|
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
|
||||||
target-dep = { version = "0.1.0", optional = true }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
regular-dep = ["dep:regular-dep"]
|
|
||||||
build-dep = ["dep:build-dep"]
|
|
||||||
target-dep = ["dep:target-dep"]
|
|
||||||
|
|
||||||
"#]],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cargo_test]
|
|
||||||
fn add_feature_for_unused_dep_existing_table() {
|
|
||||||
Package::new("dep", "0.1.0").publish();
|
|
||||||
let p = project()
|
|
||||||
.file(
|
|
||||||
"Cargo.toml",
|
|
||||||
r#"
|
|
||||||
[package]
|
|
||||||
name = "foo"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
dep = { version = "0.1.0", optional = true }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
existing = []
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.file("src/lib.rs", "")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
p.cargo("fix --edition --allow-no-vcs")
|
|
||||||
.masquerade_as_nightly_cargo(&["edition2024"])
|
|
||||||
.with_stderr_data(str![[r#"
|
|
||||||
[MIGRATING] Cargo.toml from 2021 edition to 2024
|
|
||||||
[FIXED] Cargo.toml (1 fix)
|
|
||||||
[UPDATING] `dummy-registry` index
|
|
||||||
[LOCKING] 1 package to latest compatible version
|
|
||||||
[CHECKING] foo v0.1.0 ([ROOT]/foo)
|
|
||||||
[MIGRATING] src/lib.rs from 2021 edition to 2024
|
|
||||||
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
|
|
||||||
|
|
||||||
"#]])
|
|
||||||
.run();
|
|
||||||
assert_e2e().eq(
|
|
||||||
p.read_file("Cargo.toml"),
|
|
||||||
str![[r#"
|
|
||||||
|
|
||||||
[package]
|
|
||||||
name = "foo"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
dep = { version = "0.1.0", optional = true }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
existing = []
|
|
||||||
dep = ["dep:dep"]
|
|
||||||
|
|
||||||
"#]],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cargo_test]
|
|
||||||
fn activate_dep_for_dep_feature() {
|
|
||||||
Package::new("dep-feature", "0.1.0")
|
|
||||||
.feature("a", &[])
|
|
||||||
.feature("b", &[])
|
|
||||||
.publish();
|
|
||||||
Package::new("dep-and-dep-feature", "0.1.0")
|
|
||||||
.feature("a", &[])
|
|
||||||
.feature("b", &[])
|
|
||||||
.publish();
|
|
||||||
Package::new("renamed-feature", "0.1.0")
|
|
||||||
.feature("a", &[])
|
|
||||||
.feature("b", &[])
|
|
||||||
.publish();
|
|
||||||
Package::new("unrelated-feature", "0.1.0")
|
|
||||||
.feature("a", &[])
|
|
||||||
.feature("b", &[])
|
|
||||||
.publish();
|
|
||||||
let p = project()
|
|
||||||
.file(
|
|
||||||
"Cargo.toml",
|
|
||||||
r#"
|
|
||||||
[package]
|
|
||||||
name = "foo"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
dep-feature = { version = "0.1.0", optional = true }
|
|
||||||
dep-and-dep-feature = { version = "0.1.0", optional = true }
|
|
||||||
renamed-feature = { version = "0.1.0", optional = true }
|
|
||||||
unrelated-feature = { version = "0.1.0", optional = true }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
dep-feature = ["dep-feature/a", "dep-feature/b"]
|
|
||||||
dep-and-dep-feature = ["dep:dep-and-dep-feature", "dep-and-dep-feature/a", "dep-and-dep-feature/b"]
|
|
||||||
renamed = ["renamed-feature/a", "renamed-feature/b"]
|
|
||||||
unrelated-feature = []
|
|
||||||
unrelated-dep-feature = ["unrelated-feature/a", "unrelated-feature/b"]
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.file("src/lib.rs", "")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
p.cargo("fix --edition --allow-no-vcs")
|
|
||||||
.masquerade_as_nightly_cargo(&["edition2024"])
|
|
||||||
.with_stderr_data(str![[r#"
|
|
||||||
[MIGRATING] Cargo.toml from 2021 edition to 2024
|
|
||||||
[FIXED] Cargo.toml (4 fixes)
|
|
||||||
[UPDATING] `dummy-registry` index
|
|
||||||
[LOCKING] 4 packages to latest compatible versions
|
|
||||||
[CHECKING] foo v0.1.0 ([ROOT]/foo)
|
|
||||||
[MIGRATING] src/lib.rs from 2021 edition to 2024
|
|
||||||
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
|
|
||||||
|
|
||||||
"#]])
|
|
||||||
.run();
|
|
||||||
assert_e2e().eq(
|
|
||||||
p.read_file("Cargo.toml"),
|
|
||||||
str![[r#"
|
|
||||||
|
|
||||||
[package]
|
|
||||||
name = "foo"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
dep-feature = { version = "0.1.0", optional = true }
|
|
||||||
dep-and-dep-feature = { version = "0.1.0", optional = true }
|
|
||||||
renamed-feature = { version = "0.1.0", optional = true }
|
|
||||||
unrelated-feature = { version = "0.1.0", optional = true }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
dep-feature = [ "dep:dep-feature","dep-feature/a", "dep-feature/b"]
|
|
||||||
dep-and-dep-feature = ["dep:dep-and-dep-feature", "dep-and-dep-feature/a", "dep-and-dep-feature/b"]
|
|
||||||
renamed = [ "dep:renamed-feature","renamed-feature/a", "renamed-feature/b"]
|
|
||||||
unrelated-feature = []
|
|
||||||
unrelated-dep-feature = [ "dep:unrelated-feature","unrelated-feature/a", "unrelated-feature/b"]
|
|
||||||
renamed-feature = ["dep:renamed-feature"]
|
|
||||||
|
|
||||||
"#]],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cargo_test]
|
#[cargo_test]
|
||||||
fn remove_ignored_default_features() {
|
fn remove_ignored_default_features() {
|
||||||
Package::new("dep_simple", "0.1.0").publish();
|
Package::new("dep_simple", "0.1.0").publish();
|
||||||
|
@ -1,136 +0,0 @@
|
|||||||
use cargo_test_support::prelude::*;
|
|
||||||
use cargo_test_support::project;
|
|
||||||
use cargo_test_support::registry::Package;
|
|
||||||
use cargo_test_support::str;
|
|
||||||
|
|
||||||
#[cargo_test]
|
|
||||||
fn default() {
|
|
||||||
Package::new("bar", "0.1.0").publish();
|
|
||||||
let p = project()
|
|
||||||
.file(
|
|
||||||
"Cargo.toml",
|
|
||||||
r#"
|
|
||||||
[package]
|
|
||||||
name = "foo"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
bar = { version = "0.1.0", optional = true }
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.file("src/lib.rs", "")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
p.cargo("check -Zcargo-lints")
|
|
||||||
.masquerade_as_nightly_cargo(&["cargo-lints"])
|
|
||||||
.with_stderr_data(str![[r#"
|
|
||||||
[UPDATING] `dummy-registry` index
|
|
||||||
[LOCKING] 1 package to latest compatible version
|
|
||||||
[CHECKING] foo v0.1.0 ([ROOT]/foo)
|
|
||||||
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
|
|
||||||
|
|
||||||
"#]])
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cargo_test]
|
|
||||||
fn warn() {
|
|
||||||
Package::new("bar", "0.1.0").publish();
|
|
||||||
Package::new("baz", "0.1.0").publish();
|
|
||||||
Package::new("target-dep", "0.1.0").publish();
|
|
||||||
let p = project()
|
|
||||||
.file(
|
|
||||||
"Cargo.toml",
|
|
||||||
r#"
|
|
||||||
[package]
|
|
||||||
name = "foo"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
bar = { version = "0.1.0", optional = true }
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
baz = { version = "0.1.0", optional = true }
|
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
|
||||||
target-dep = { version = "0.1.0", optional = true }
|
|
||||||
|
|
||||||
[lints.cargo]
|
|
||||||
implicit_features = "warn"
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.file("src/lib.rs", "")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
p.cargo("check -Zcargo-lints")
|
|
||||||
.masquerade_as_nightly_cargo(&["cargo-lints"])
|
|
||||||
.with_stderr_data(str![[r#"
|
|
||||||
[WARNING] implicit features for optional dependencies is deprecated and will be unavailable in the 2024 edition
|
|
||||||
--> Cargo.toml:8:1
|
|
||||||
|
|
|
||||||
8 | bar = { version = "0.1.0", optional = true }
|
|
||||||
| ---
|
|
||||||
|
|
|
||||||
= [NOTE] `cargo::implicit_features` is set to `warn` in `[lints]`
|
|
||||||
[WARNING] implicit features for optional dependencies is deprecated and will be unavailable in the 2024 edition
|
|
||||||
--> Cargo.toml:11:1
|
|
||||||
|
|
|
||||||
11 | baz = { version = "0.1.0", optional = true }
|
|
||||||
| ---
|
|
||||||
|
|
|
||||||
[WARNING] implicit features for optional dependencies is deprecated and will be unavailable in the 2024 edition
|
|
||||||
--> Cargo.toml:14:1
|
|
||||||
|
|
|
||||||
14 | target-dep = { version = "0.1.0", optional = true }
|
|
||||||
| ----------
|
|
||||||
|
|
|
||||||
[UPDATING] `dummy-registry` index
|
|
||||||
[LOCKING] 3 packages to latest compatible versions
|
|
||||||
[CHECKING] foo v0.1.0 ([ROOT]/foo)
|
|
||||||
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
|
|
||||||
|
|
||||||
"#]])
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cargo_test(nightly, reason = "edition2024 is not stable")]
|
|
||||||
fn implicit_features_edition_2024() {
|
|
||||||
Package::new("bar", "0.1.0").publish();
|
|
||||||
Package::new("baz", "0.1.0").publish();
|
|
||||||
let p = project()
|
|
||||||
.file(
|
|
||||||
"Cargo.toml",
|
|
||||||
r#"
|
|
||||||
cargo-features = ["edition2024"]
|
|
||||||
[package]
|
|
||||||
name = "foo"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
bar = { version = "0.1.0", optional = true }
|
|
||||||
baz = { version = "0.1.0", optional = true }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
baz = ["dep:baz"]
|
|
||||||
|
|
||||||
[lints.cargo]
|
|
||||||
unused_optional_dependency = "allow"
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.file("src/lib.rs", "")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
p.cargo("check -Zcargo-lints")
|
|
||||||
.masquerade_as_nightly_cargo(&["cargo-lints", "edition2024"])
|
|
||||||
.with_stderr_data(str![[r#"
|
|
||||||
[UPDATING] `dummy-registry` index
|
|
||||||
[LOCKING] 1 package to latest Rust 1.[..] compatible version
|
|
||||||
[CHECKING] foo v0.1.0 ([ROOT]/foo)
|
|
||||||
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
|
|
||||||
|
|
||||||
"#]])
|
|
||||||
.run();
|
|
||||||
}
|
|
@ -4,10 +4,8 @@ use cargo_test_support::registry::Package;
|
|||||||
use cargo_test_support::str;
|
use cargo_test_support::str;
|
||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
mod implicit_features;
|
|
||||||
mod inherited;
|
mod inherited;
|
||||||
mod unknown_lints;
|
mod unknown_lints;
|
||||||
mod unused_optional_dependencies;
|
|
||||||
mod warning;
|
mod warning;
|
||||||
|
|
||||||
#[cargo_test]
|
#[cargo_test]
|
||||||
@ -177,16 +175,19 @@ fn cap_lints() {
|
|||||||
.file(
|
.file(
|
||||||
"Cargo.toml",
|
"Cargo.toml",
|
||||||
r#"
|
r#"
|
||||||
|
cargo-features = ["test-dummy-unstable"]
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "bar"
|
name = "bar"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
im-a-teapot = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
baz = { version = "0.1.0", optional = true }
|
baz = { version = "0.1.0", optional = true }
|
||||||
|
|
||||||
[lints.cargo]
|
[lints.cargo]
|
||||||
implicit_features = "warn"
|
im_a_teapot = "warn"
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.file("src/lib.rs", "")
|
.file("src/lib.rs", "")
|
||||||
@ -202,9 +203,6 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bar = "0.1.0"
|
bar = "0.1.0"
|
||||||
|
|
||||||
[lints.cargo]
|
|
||||||
implicit_features = "warn"
|
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.file("src/lib.rs", "")
|
.file("src/lib.rs", "")
|
||||||
|
@ -1,349 +0,0 @@
|
|||||||
use cargo_test_support::prelude::*;
|
|
||||||
use cargo_test_support::project;
|
|
||||||
use cargo_test_support::registry::Package;
|
|
||||||
use cargo_test_support::str;
|
|
||||||
|
|
||||||
#[cargo_test(nightly, reason = "edition2024 is not stable")]
|
|
||||||
fn default() {
|
|
||||||
Package::new("bar", "0.1.0").publish();
|
|
||||||
Package::new("baz", "0.1.0").publish();
|
|
||||||
Package::new("target-dep", "0.1.0").publish();
|
|
||||||
let p = project()
|
|
||||||
.file(
|
|
||||||
"Cargo.toml",
|
|
||||||
r#"
|
|
||||||
cargo-features = ["edition2024"]
|
|
||||||
[package]
|
|
||||||
name = "foo"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
bar = { version = "0.1.0", optional = true }
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
baz = { version = "0.1.0", optional = true }
|
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
|
||||||
target-dep = { version = "0.1.0", optional = true }
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.file("src/lib.rs", "")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
p.cargo("check -Zcargo-lints")
|
|
||||||
.masquerade_as_nightly_cargo(&["cargo-lints", "edition2024"])
|
|
||||||
.with_stderr_data(str![[r#"
|
|
||||||
[WARNING] unused optional dependency
|
|
||||||
--> Cargo.toml:9:1
|
|
||||||
|
|
|
||||||
9 | bar = { version = "0.1.0", optional = true }
|
|
||||||
| ---
|
|
||||||
|
|
|
||||||
= [NOTE] `cargo::unused_optional_dependency` is set to `warn` by default
|
|
||||||
= [HELP] remove the dependency or activate it in a feature with `dep:bar`
|
|
||||||
[WARNING] unused optional dependency
|
|
||||||
--> Cargo.toml:12:1
|
|
||||||
|
|
|
||||||
12 | baz = { version = "0.1.0", optional = true }
|
|
||||||
| ---
|
|
||||||
|
|
|
||||||
= [HELP] remove the dependency or activate it in a feature with `dep:baz`
|
|
||||||
[WARNING] unused optional dependency
|
|
||||||
--> Cargo.toml:15:1
|
|
||||||
|
|
|
||||||
15 | target-dep = { version = "0.1.0", optional = true }
|
|
||||||
| ----------
|
|
||||||
|
|
|
||||||
= [HELP] remove the dependency or activate it in a feature with `dep:target-dep`
|
|
||||||
[CHECKING] foo v0.1.0 ([ROOT]/foo)
|
|
||||||
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
|
|
||||||
|
|
||||||
"#]])
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cargo_test]
|
|
||||||
fn edition_2021() {
|
|
||||||
Package::new("bar", "0.1.0").publish();
|
|
||||||
let p = project()
|
|
||||||
.file(
|
|
||||||
"Cargo.toml",
|
|
||||||
r#"
|
|
||||||
[package]
|
|
||||||
name = "foo"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
bar = { version = "0.1.0", optional = true }
|
|
||||||
|
|
||||||
[lints.cargo]
|
|
||||||
implicit_features = "allow"
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.file("src/lib.rs", "")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
p.cargo("check -Zcargo-lints")
|
|
||||||
.masquerade_as_nightly_cargo(&["cargo-lints"])
|
|
||||||
.with_stderr_data(str![[r#"
|
|
||||||
[UPDATING] `dummy-registry` index
|
|
||||||
[LOCKING] 1 package to latest compatible version
|
|
||||||
[CHECKING] foo v0.1.0 ([ROOT]/foo)
|
|
||||||
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
|
|
||||||
|
|
||||||
"#]])
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cargo_test(nightly, reason = "edition2024 is not stable")]
|
|
||||||
fn renamed_deps() {
|
|
||||||
Package::new("bar", "0.1.0").publish();
|
|
||||||
Package::new("bar", "0.2.0").publish();
|
|
||||||
Package::new("target-dep", "0.1.0").publish();
|
|
||||||
let p = project()
|
|
||||||
.file(
|
|
||||||
"Cargo.toml",
|
|
||||||
r#"
|
|
||||||
cargo-features = ["edition2024"]
|
|
||||||
[package]
|
|
||||||
name = "foo"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
bar = { version = "0.1.0", optional = true }
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
baz = { version = "0.2.0", package = "bar", optional = true }
|
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
|
||||||
target-dep = { version = "0.1.0", optional = true }
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.file("src/lib.rs", "")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
p.cargo("check -Zcargo-lints")
|
|
||||||
.masquerade_as_nightly_cargo(&["cargo-lints", "edition2024"])
|
|
||||||
.with_stderr_data(str![[r#"
|
|
||||||
[WARNING] unused optional dependency
|
|
||||||
--> Cargo.toml:9:1
|
|
||||||
|
|
|
||||||
9 | bar = { version = "0.1.0", optional = true }
|
|
||||||
| ---
|
|
||||||
|
|
|
||||||
= [NOTE] `cargo::unused_optional_dependency` is set to `warn` by default
|
|
||||||
= [HELP] remove the dependency or activate it in a feature with `dep:bar`
|
|
||||||
[WARNING] unused optional dependency
|
|
||||||
--> Cargo.toml:12:1
|
|
||||||
|
|
|
||||||
12 | baz = { version = "0.2.0", package = "bar", optional = true }
|
|
||||||
| ---
|
|
||||||
|
|
|
||||||
= [HELP] remove the dependency or activate it in a feature with `dep:baz`
|
|
||||||
[WARNING] unused optional dependency
|
|
||||||
--> Cargo.toml:15:1
|
|
||||||
|
|
|
||||||
15 | target-dep = { version = "0.1.0", optional = true }
|
|
||||||
| ----------
|
|
||||||
|
|
|
||||||
= [HELP] remove the dependency or activate it in a feature with `dep:target-dep`
|
|
||||||
[CHECKING] foo v0.1.0 ([ROOT]/foo)
|
|
||||||
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
|
|
||||||
|
|
||||||
"#]])
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cargo_test(nightly, reason = "edition2024 is not stable")]
|
|
||||||
fn shadowed_optional_dep_is_unused_in_2024() {
|
|
||||||
Package::new("optional-dep", "0.1.0").publish();
|
|
||||||
let p = project()
|
|
||||||
.file(
|
|
||||||
"Cargo.toml",
|
|
||||||
r#"
|
|
||||||
cargo-features = ["edition2024"]
|
|
||||||
[package]
|
|
||||||
name = "foo"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
optional-dep = { version = "0.1.0", optional = true }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
optional-dep = []
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.file("src/lib.rs", "")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
p.cargo("check -Zcargo-lints")
|
|
||||||
.masquerade_as_nightly_cargo(&["cargo-lints", "edition2024"])
|
|
||||||
.with_stderr_data(str![[r#"
|
|
||||||
[WARNING] unused optional dependency
|
|
||||||
--> Cargo.toml:9:1
|
|
||||||
|
|
|
||||||
9 | optional-dep = { version = "0.1.0", optional = true }
|
|
||||||
| ------------
|
|
||||||
|
|
|
||||||
= [NOTE] `cargo::unused_optional_dependency` is set to `warn` by default
|
|
||||||
= [HELP] remove the dependency or activate it in a feature with `dep:optional-dep`
|
|
||||||
[CHECKING] foo v0.1.0 ([ROOT]/foo)
|
|
||||||
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
|
|
||||||
|
|
||||||
"#]])
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cargo_test(nightly, reason = "edition2024 is not stable")]
|
|
||||||
fn inactive_weak_optional_dep() {
|
|
||||||
Package::new("dep_name", "0.1.0")
|
|
||||||
.feature("dep_feature", &[])
|
|
||||||
.publish();
|
|
||||||
|
|
||||||
// `dep_name`` is included as a weak optional dependency throught speficying the `dep_name?/dep_feature` in feature table.
|
|
||||||
// In edition2024, `dep_name` need to be add `dep:dep_name` to feature table to activate it.
|
|
||||||
|
|
||||||
// This test explain the conclusion mentioned above
|
|
||||||
let p = project()
|
|
||||||
.file(
|
|
||||||
"Cargo.toml",
|
|
||||||
r#"
|
|
||||||
cargo-features = ["edition2024"]
|
|
||||||
[package]
|
|
||||||
name = "foo"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
dep_name = { version = "0.1.0", optional = true }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
foo_feature = ["dep:dep_name", "dep_name?/dep_feature"]
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.file("src/lib.rs", "")
|
|
||||||
.build();
|
|
||||||
p.cargo("check -Zcargo-lints")
|
|
||||||
.masquerade_as_nightly_cargo(&["cargo-lints", "edition2024"])
|
|
||||||
.run();
|
|
||||||
|
|
||||||
// This test proves no regression when dep_name isn't included
|
|
||||||
let p = project()
|
|
||||||
.file(
|
|
||||||
"Cargo.toml",
|
|
||||||
r#"
|
|
||||||
cargo-features = ["edition2024"]
|
|
||||||
[package]
|
|
||||||
name = "foo"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
|
|
||||||
[features]
|
|
||||||
foo_feature = ["dep_name?/dep_feature"]
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.file("src/lib.rs", "")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
p.cargo("check -Zcargo-lints")
|
|
||||||
.masquerade_as_nightly_cargo(&["cargo-lints", "edition2024"])
|
|
||||||
.with_status(101)
|
|
||||||
.with_stderr_data(str![[r#"
|
|
||||||
[ERROR] feature `foo_feature` includes `dep_name?/dep_feature`, but `dep_name` is not a dependency
|
|
||||||
--> Cargo.toml:11:27
|
|
||||||
|
|
|
||||||
11 | foo_feature = ["dep_name?/dep_feature"]
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
|
||||||
[ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml`
|
|
||||||
|
|
||||||
"#]])
|
|
||||||
.run();
|
|
||||||
|
|
||||||
// Ensure that a weak dependency feature requires the existence of a `dep:` feature in edition 2024.
|
|
||||||
let p = project()
|
|
||||||
.file(
|
|
||||||
"Cargo.toml",
|
|
||||||
r#"
|
|
||||||
cargo-features = ["edition2024"]
|
|
||||||
[package]
|
|
||||||
name = "foo"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
dep_name = { version = "0.1.0", optional = true }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
foo_feature = ["dep_name?/dep_feature"]
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.file("src/lib.rs", "")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
p.cargo("check -Zcargo-lints")
|
|
||||||
.masquerade_as_nightly_cargo(&["cargo-lints", "edition2024"])
|
|
||||||
.with_status(101)
|
|
||||||
.with_stderr_data(str![[r#"
|
|
||||||
[ERROR] feature `foo_feature` includes `dep_name?/dep_feature`, but `dep_name` is not a dependency
|
|
||||||
--> Cargo.toml:12:31
|
|
||||||
|
|
|
||||||
9 | dep_name = { version = "0.1.0", optional = true }
|
|
||||||
| -------- `dep_name` is an unused optional dependency since no feature enables it
|
|
||||||
10 |
|
|
||||||
11 | [features]
|
|
||||||
12 | foo_feature = ["dep_name?/dep_feature"]
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
|
||||||
= [HELP] enable the dependency with `dep:dep_name`
|
|
||||||
[ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml`
|
|
||||||
|
|
||||||
"#]])
|
|
||||||
.run();
|
|
||||||
// Check target.'cfg(unix)'.dependencies can work
|
|
||||||
let p = project()
|
|
||||||
.file(
|
|
||||||
"Cargo.toml",
|
|
||||||
r#"
|
|
||||||
cargo-features = ["edition2024"]
|
|
||||||
[package]
|
|
||||||
name = "foo"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
|
||||||
dep_name = { version = "0.1.0", optional = true }
|
|
||||||
|
|
||||||
[features]
|
|
||||||
foo_feature = ["dep_name?/dep_feature"]
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.file("src/lib.rs", "")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
p.cargo("check -Zcargo-lints")
|
|
||||||
.masquerade_as_nightly_cargo(&["cargo-lints", "edition2024"])
|
|
||||||
.with_status(101)
|
|
||||||
.with_stderr_data(str![[r#"
|
|
||||||
[ERROR] feature `foo_feature` includes `dep_name?/dep_feature`, but `dep_name` is not a dependency
|
|
||||||
--> Cargo.toml:12:27
|
|
||||||
|
|
|
||||||
9 | dep_name = { version = "0.1.0", optional = true }
|
|
||||||
| -------- `dep_name` is an unused optional dependency since no feature enables it
|
|
||||||
10 |
|
|
||||||
11 | [features]
|
|
||||||
12 | foo_feature = ["dep_name?/dep_feature"]
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
|
||||||
= [HELP] enable the dependency with `dep:dep_name`
|
|
||||||
[ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml`
|
|
||||||
|
|
||||||
"#]])
|
|
||||||
.run();
|
|
||||||
}
|
|
@ -3499,57 +3499,6 @@ fn versionless_package() {
|
|||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cargo_test(nightly, reason = "edition2024 is not stable")]
|
|
||||||
fn unused_deps_edition_2024() {
|
|
||||||
let registry = RegistryBuilder::new().http_api().http_index().build();
|
|
||||||
|
|
||||||
let p = project()
|
|
||||||
.file(
|
|
||||||
"Cargo.toml",
|
|
||||||
r#"
|
|
||||||
cargo-features = ["edition2024"]
|
|
||||||
[package]
|
|
||||||
name = "foo"
|
|
||||||
version = "0.0.1"
|
|
||||||
authors = []
|
|
||||||
license = "MIT"
|
|
||||||
description = "foo"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
bar = { version = "0.1.0", optional = true }
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
baz = { version = "0.1.0", optional = true }
|
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
|
||||||
target-dep = { version = "0.1.0", optional = true }
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.file("src/main.rs", "")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
p.cargo("publish --no-verify")
|
|
||||||
.masquerade_as_nightly_cargo(&["edition2024"])
|
|
||||||
.replace_crates_io(registry.index_url())
|
|
||||||
.with_stderr_data(str![[r#"
|
|
||||||
[UPDATING] crates.io index
|
|
||||||
[WARNING] manifest has no documentation, homepage or repository.
|
|
||||||
See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info.
|
|
||||||
[PACKAGING] foo v0.0.1 ([ROOT]/foo)
|
|
||||||
[PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
|
|
||||||
[UPLOADING] foo v0.0.1 ([ROOT]/foo)
|
|
||||||
[UPLOADED] foo v0.0.1 to registry `crates-io`
|
|
||||||
[NOTE] waiting for `foo v0.0.1` to be available at registry `crates-io`.
|
|
||||||
You may press ctrl-c to skip waiting; the crate should be available shortly.
|
|
||||||
[PUBLISHED] foo v0.0.1 at registry `crates-io`
|
|
||||||
|
|
||||||
"#]])
|
|
||||||
.run();
|
|
||||||
|
|
||||||
validate_upload_foo();
|
|
||||||
}
|
|
||||||
|
|
||||||
// A workspace with three projects that depend on one another (level1 -> level2 -> level3).
|
// A workspace with three projects that depend on one another (level1 -> level2 -> level3).
|
||||||
// level1 is a binary package, to test lockfile generation.
|
// level1 is a binary package, to test lockfile generation.
|
||||||
fn workspace_with_local_deps_project() -> Project {
|
fn workspace_with_local_deps_project() -> Project {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user