diff --git a/src/cargo/core/workspace.rs b/src/cargo/core/workspace.rs index d4871fef2035..ad4ecc3e25f6 100644 --- a/src/cargo/core/workspace.rs +++ b/src/cargo/core/workspace.rs @@ -24,9 +24,7 @@ use crate::sources::{PathSource, SourceConfigMap, CRATES_IO_INDEX, CRATES_IO_REG use crate::util::edit_distance; use crate::util::errors::{CargoResult, ManifestError}; use crate::util::interning::InternedString; -use crate::util::lints::{ - analyze_cargo_lints_table, check_im_a_teapot, check_implicit_features, unused_dependencies, -}; +use crate::util::lints::{analyze_cargo_lints_table, check_im_a_teapot}; use crate::util::toml::{read_manifest, InheritableFields}; use crate::util::{ context::CargoResolverConfig, context::ConfigRelativePath, context::IncompatibleRustVersions, @@ -1240,8 +1238,6 @@ impl<'gctx> Workspace<'gctx> { 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 { Err(crate::util::errors::AlreadyPrintedError::new(anyhow!( "encountered {error_count} errors(s) while running lints" diff --git a/src/cargo/ops/fix.rs b/src/cargo/ops/fix.rs index de17527745e6..ba3c9f2666ca 100644 --- a/src/cargo/ops/fix.rs +++ b/src/cargo/ops/fix.rs @@ -55,13 +55,12 @@ use crate::core::compiler::CompileKind; use crate::core::compiler::RustcTargetData; use crate::core::resolver::features::{DiffMap, FeatureOpts, FeatureResolver, FeaturesFor}; use crate::core::resolver::{HasDevUnits, Resolve, ResolveBehavior}; +use crate::core::PackageIdSpecQuery as _; use crate::core::{Edition, MaybePackage, Package, PackageId, Workspace}; -use crate::core::{FeatureValue, PackageIdSpecQuery as _}; use crate::ops::resolve::WorkspaceResolve; use crate::ops::{self, CompileOptions}; use crate::util::diagnostic_server::{Message, RustfixDiagnosticServer}; use crate::util::errors::CargoResult; -use crate::util::interning::InternedString; use crate::util::GlobalContext; use crate::util::{existing_vcs_repo, LockServer, LockServerClient}; 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 += add_feature_for_unused_deps(pkg, root, ws.gctx()); fixes += rename_table(root, "project", "package"); if let Some(target) = root.get_mut("lib").and_then(|t| t.as_table_like_mut()) { fixes += rename_target_fields_2024(target); @@ -437,79 +435,6 @@ fn rename_table(parent: &mut dyn toml_edit::TableLike, old: &str, new: &str) -> 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::>() - }) - .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>( ws: &Workspace<'gctx>, target_data: &mut RustcTargetData<'gctx>, diff --git a/src/cargo/util/lints.rs b/src/cargo/util/lints.rs index 4d1b5b6de88d..99531bfbd7ba 100644 --- a/src/cargo/util/lints.rs +++ b/src/cargo/util/lints.rs @@ -1,24 +1,15 @@ -use crate::core::dependency::DepKind; -use crate::core::FeatureValue::Dep; -use crate::core::{Edition, Feature, FeatureValue, Features, Manifest, Package}; -use crate::util::interning::InternedString; +use crate::core::{Edition, Feature, Features, Manifest, Package}; use crate::{CargoResult, GlobalContext}; use annotate_snippets::{Level, Snippet}; use cargo_util_schemas::manifest::{TomlLintLevel, TomlToolLints}; use pathdiff::diff_paths; -use std::collections::HashSet; use std::fmt::Display; use std::ops::Range; use std::path::Path; use toml_edit::ImDocument; const LINT_GROUPS: &[LintGroup] = &[TEST_DUMMY_UNSTABLE]; -pub const LINTS: &[Lint] = &[ - IMPLICIT_FEATURES, - IM_A_TEAPOT, - UNKNOWN_LINTS, - UNUSED_OPTIONAL_DEPENDENCY, -]; +pub const LINTS: &[Lint] = &[IM_A_TEAPOT, UNKNOWN_LINTS]; pub fn analyze_cargo_lints_table( pkg: &Package, @@ -487,127 +478,6 @@ pub fn check_im_a_teapot( 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:` (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::>() - }) - .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 { name: "unknown_lints", desc: "unknown lint", @@ -735,151 +605,6 @@ fn output_unknown_lints( 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:"` 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::>(); - 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::>(); - 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)] mod tests { use itertools::Itertools; diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index 54bd34d5ef83..f4cd2ec7bacd 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -1,5 +1,5 @@ use annotate_snippets::{Level, Snippet}; -use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; +use std::collections::{BTreeMap, BTreeSet, HashMap}; use std::ffi::OsStr; use std::path::{Path, PathBuf}; use std::rc::Rc; @@ -24,7 +24,6 @@ use crate::core::compiler::{CompileKind, CompileTarget}; use crate::core::dependency::{Artifact, ArtifactTarget, DepKind}; use crate::core::manifest::{ManifestMetadata, TargetSourcePath}; use crate::core::resolver::ResolveBehavior; -use crate::core::FeatureValue::Dep; use crate::core::{find_workspace_root, resolve_relative_path, CliUnstable, FeatureValue}; use crate::core::{Dependency, Manifest, Package, PackageId, Summary, Target}; use crate::core::{Edition, EitherManifest, Feature, Features, VirtualManifest, Workspace}; @@ -372,26 +371,11 @@ fn normalize_toml( 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::>() - }) - .unwrap_or_default(); - normalized_toml.dependencies = normalize_dependencies( gctx, edition, &features, original_toml.dependencies.as_ref(), - &activated_opt_deps, None, &inherit, &workspace_root, @@ -412,7 +396,6 @@ fn normalize_toml( edition, &features, original_toml.dev_dependencies(), - &activated_opt_deps, Some(DepKind::Development), &inherit, &workspace_root, @@ -433,7 +416,6 @@ fn normalize_toml( edition, &features, original_toml.build_dependencies(), - &activated_opt_deps, Some(DepKind::Build), &inherit, &workspace_root, @@ -447,7 +429,6 @@ fn normalize_toml( edition, &features, platform.dependencies.as_ref(), - &activated_opt_deps, None, &inherit, &workspace_root, @@ -468,7 +449,6 @@ fn normalize_toml( edition, &features, platform.dev_dependencies(), - &activated_opt_deps, Some(DepKind::Development), &inherit, &workspace_root, @@ -489,7 +469,6 @@ fn normalize_toml( edition, &features, platform.build_dependencies(), - &activated_opt_deps, Some(DepKind::Build), &inherit, &workspace_root, @@ -756,7 +735,6 @@ fn normalize_dependencies<'a>( edition: Edition, features: &Features, orig_deps: Option<&BTreeMap>, - activated_opt_deps: &HashSet<&str>, kind: Option, inherit: &dyn Fn() -> CargoResult<&'a InheritableFields>, workspace_root: &dyn Fn() -> CargoResult<&'a Path>, @@ -818,18 +796,10 @@ fn normalize_dependencies<'a>( .with_context(|| format!("resolving path dependency {name_in_toml}"))?; } - // if the dependency is not optional, it is always used - // if the dependency is optional and activated, it is used - // if the dependency is optional and not activated, it is not used - 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()), - ); - } + deps.insert( + name_in_toml.clone(), + manifest::InheritableDependency::Value(resolved.clone()), + ); } Ok(Some(deps)) } diff --git a/tests/testsuite/features.rs b/tests/testsuite/features.rs index 4d1d5429ce55..94a69cc6e113 100644 --- a/tests/testsuite/features.rs +++ b/tests/testsuite/features.rs @@ -1846,63 +1846,6 @@ fn features_option_given_twice() { 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] fn multi_multi_features() { let p = project() diff --git a/tests/testsuite/fix.rs b/tests/testsuite/fix.rs index aade6ca8bdb7..e98060a914ef 100644 --- a/tests/testsuite/fix.rs +++ b/tests/testsuite/fix.rs @@ -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] fn remove_ignored_default_features() { Package::new("dep_simple", "0.1.0").publish(); diff --git a/tests/testsuite/lints/implicit_features.rs b/tests/testsuite/lints/implicit_features.rs deleted file mode 100644 index 95cd747d4c57..000000000000 --- a/tests/testsuite/lints/implicit_features.rs +++ /dev/null @@ -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(); -} diff --git a/tests/testsuite/lints/mod.rs b/tests/testsuite/lints/mod.rs index 8b9eabca09fe..70dd35265931 100644 --- a/tests/testsuite/lints/mod.rs +++ b/tests/testsuite/lints/mod.rs @@ -4,10 +4,8 @@ use cargo_test_support::registry::Package; use cargo_test_support::str; mod error; -mod implicit_features; mod inherited; mod unknown_lints; -mod unused_optional_dependencies; mod warning; #[cargo_test] @@ -177,16 +175,19 @@ fn cap_lints() { .file( "Cargo.toml", r#" +cargo-features = ["test-dummy-unstable"] + [package] name = "bar" version = "0.1.0" edition = "2021" +im-a-teapot = true [dependencies] baz = { version = "0.1.0", optional = true } [lints.cargo] -implicit_features = "warn" +im_a_teapot = "warn" "#, ) .file("src/lib.rs", "") @@ -202,9 +203,6 @@ edition = "2021" [dependencies] bar = "0.1.0" - -[lints.cargo] -implicit_features = "warn" "#, ) .file("src/lib.rs", "") diff --git a/tests/testsuite/lints/unused_optional_dependencies.rs b/tests/testsuite/lints/unused_optional_dependencies.rs deleted file mode 100644 index 43547dbe159e..000000000000 --- a/tests/testsuite/lints/unused_optional_dependencies.rs +++ /dev/null @@ -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(); -} diff --git a/tests/testsuite/publish.rs b/tests/testsuite/publish.rs index 3fd31cf9b19c..82b689e034e4 100644 --- a/tests/testsuite/publish.rs +++ b/tests/testsuite/publish.rs @@ -3499,57 +3499,6 @@ fn versionless_package() { .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). // level1 is a binary package, to test lockfile generation. fn workspace_with_local_deps_project() -> Project {