From d8b9b992ddae34e0b22bc871c40110a07bbc7feb Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 21 Nov 2023 19:56:04 -0600 Subject: [PATCH 01/15] refactor(toml): Inline TomlPackage::to_package_id --- src/cargo/util/toml/mod.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index 419e55ae4f9..b991d472b93 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -508,11 +508,12 @@ impl schema::TomlManifest { package.version = version.clone().map(schema::InheritableField::Value); - let pkgid = package.to_package_id( - source_id, + let pkgid = PackageId::pure( + package.name.as_str().into(), version .clone() .unwrap_or_else(|| semver::Version::new(0, 0, 0)), + source_id, ); let edition = if let Some(edition) = package.edition.clone() { @@ -1545,12 +1546,6 @@ impl InheritableFields { } } -impl schema::TomlPackage { - fn to_package_id(&self, source_id: SourceId, version: semver::Version) -> PackageId { - PackageId::pure(self.name.as_str().into(), version, source_id) - } -} - impl schema::InheritableField { fn inherit_with<'a>( self, From a774d8b025d17a82fe76ff7588e9cbce50817965 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 21 Nov 2023 19:57:48 -0600 Subject: [PATCH 02/15] refactor(toml): Inline TomlLintLevel::flag --- src/cargo/util/toml/mod.rs | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index b991d472b93..2a5b29b8b1d 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -1331,7 +1331,13 @@ fn lints_to_rustflags(lints: &schema::TomlLints) -> Vec { .iter() .flat_map(|(tool, lints)| { lints.iter().map(move |(name, config)| { - let flag = config.level().flag(); + let flag = match config.level() { + schema::TomlLintLevel::Forbid => "--forbid", + schema::TomlLintLevel::Deny => "--deny", + schema::TomlLintLevel::Warn => "--warn", + schema::TomlLintLevel::Allow => "--allow", + }; + let option = if tool == "rust" { format!("{flag}={name}") } else { @@ -2261,17 +2267,6 @@ impl schema::InheritableLints { } } -impl schema::TomlLintLevel { - fn flag(&self) -> &'static str { - match self { - Self::Forbid => "--forbid", - Self::Deny => "--deny", - Self::Warn => "--warn", - Self::Allow => "--allow", - } - } -} - pub trait ResolveToPath { fn resolve(&self, config: &Config) -> PathBuf; } From b4489e34502d9cbd2be9a56da59a3c43f9779d87 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 21 Nov 2023 19:59:11 -0600 Subject: [PATCH 03/15] refactor(toml): Inline TomlDetailedDependency::update_optional --- src/cargo/util/toml/mod.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index 2a5b29b8b1d..a227c554bea 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -1657,7 +1657,7 @@ impl schema::TomlInheritedDependency { d.public = Some(public); } d.add_features(self.features.clone()); - d.update_optional(self.optional); + d.optional = self.optional; schema::TomlDependency::Detailed(d) } } @@ -1726,10 +1726,6 @@ impl schema::TomlDetailedDependency { }; } - fn update_optional(&mut self, optional: Option) { - self.optional = optional; - } - fn resolve_path( &mut self, name: &str, From 97c9a17e710eb48dbdad1420c9fd9d3680ddee05 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 21 Nov 2023 20:00:23 -0600 Subject: [PATCH 04/15] refactor(toml): Inline TomlDetailedDependency::add_features --- src/cargo/util/toml/mod.rs | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index a227c554bea..2b1c2173d60 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -1656,7 +1656,17 @@ impl schema::TomlInheritedDependency { if let Some(public) = self.public { d.public = Some(public); } - d.add_features(self.features.clone()); + d.features = match (d.features.clone(), self.features.clone()) { + (Some(dep_feat), Some(inherit_feat)) => Some( + dep_feat + .into_iter() + .chain(inherit_feat) + .collect::>(), + ), + (Some(dep_fet), None) => Some(dep_fet), + (None, Some(inherit_feat)) => Some(inherit_feat), + (None, None) => None, + }; d.optional = self.optional; schema::TomlDependency::Detailed(d) } @@ -1712,20 +1722,6 @@ impl schema::TomlDependency

{ } impl schema::TomlDetailedDependency { - fn add_features(&mut self, features: Option>) { - self.features = match (self.features.clone(), features.clone()) { - (Some(dep_feat), Some(inherit_feat)) => Some( - dep_feat - .into_iter() - .chain(inherit_feat) - .collect::>(), - ), - (Some(dep_fet), None) => Some(dep_fet), - (None, Some(inherit_feat)) => Some(inherit_feat), - (None, None) => None, - }; - } - fn resolve_path( &mut self, name: &str, From 89bfc4de522a904ea0fffc1c97808dac687e2963 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 21 Nov 2023 20:02:44 -0600 Subject: [PATCH 05/15] refactor(toml): Inline TomlDetailedDependency::resolve_path --- src/cargo/util/toml/mod.rs | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index 2b1c2173d60..0e213be2cd9 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -1514,7 +1514,14 @@ impl InheritableFields { }; let mut dep = dep.clone(); if let schema::TomlDependency::Detailed(detailed) = &mut dep { - detailed.resolve_path(name, self.ws_root(), package_root)?; + if let Some(rel_path) = &detailed.path { + detailed.path = Some(resolve_relative_path( + name, + self.ws_root(), + package_root, + rel_path, + )?); + } } Ok(dep) } @@ -1721,25 +1728,6 @@ impl schema::TomlDependency

{ } } -impl schema::TomlDetailedDependency { - fn resolve_path( - &mut self, - name: &str, - root_path: &Path, - package_root: &Path, - ) -> CargoResult<()> { - if let Some(rel_path) = &self.path { - self.path = Some(resolve_relative_path( - name, - root_path, - package_root, - rel_path, - )?) - } - Ok(()) - } -} - impl schema::TomlDetailedDependency

{ fn to_dependency( &self, From 6e187f8fbd313ffee7d55c7e216f002d46b9ed8d Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 9 Nov 2023 10:54:11 -0600 Subject: [PATCH 06/15] refactor(toml): Make profile validation free methods --- src/cargo/core/profiles.rs | 26 ++-- src/cargo/util/toml/mod.rs | 290 +++++++++++++++++++------------------ 2 files changed, 160 insertions(+), 156 deletions(-) diff --git a/src/cargo/core/profiles.rs b/src/cargo/core/profiles.rs index ec53dbae59e..f0ecb663ec6 100644 --- a/src/cargo/core/profiles.rs +++ b/src/cargo/core/profiles.rs @@ -32,6 +32,7 @@ use crate::util::toml::schema::TomlTrimPathsValue; use crate::util::toml::schema::{ ProfilePackageSpec, StringOrBool, TomlDebugInfo, TomlProfile, TomlProfiles, }; +use crate::util::toml::validate_profile; use crate::util::{closest_msg, config, CargoResult, Config}; use anyhow::{bail, Context as _}; use std::collections::{BTreeMap, HashMap, HashSet}; @@ -1235,20 +1236,19 @@ fn get_config_profile(ws: &Workspace<'_>, name: &str) -> CargoResult schema::TomlDetailedDependency

{ } } -impl schema::TomlProfiles { - /// Checks syntax validity and unstable feature gate for each profile. - /// - /// It's a bit unfortunate both `-Z` flags and `cargo-features` are required, - /// because profiles can now be set in either `Cargo.toml` or `config.toml`. - fn validate( - &self, - cli_unstable: &CliUnstable, - features: &Features, - warnings: &mut Vec, - ) -> CargoResult<()> { - for (name, profile) in &self.0 { - profile.validate(name, cli_unstable, features, warnings)?; - } - Ok(()) +/// Checks syntax validity and unstable feature gate for each profile. +/// +/// It's a bit unfortunate both `-Z` flags and `cargo-features` are required, +/// because profiles can now be set in either `Cargo.toml` or `config.toml`. +fn validate_profiles( + profiles: &schema::TomlProfiles, + cli_unstable: &CliUnstable, + features: &Features, + warnings: &mut Vec, +) -> CargoResult<()> { + for (name, profile) in &profiles.0 { + validate_profile(profile, name, cli_unstable, features, warnings)?; } + Ok(()) } -impl schema::TomlProfile { - /// Checks stytax validity and unstable feature gate for a given profile. - pub fn validate( - &self, - name: &str, - cli_unstable: &CliUnstable, - features: &Features, - warnings: &mut Vec, - ) -> CargoResult<()> { - self.validate_profile(name, cli_unstable, features)?; - if let Some(ref profile) = self.build_override { - profile.validate_override("build-override")?; - profile.validate_profile(&format!("{name}.build-override"), cli_unstable, features)?; - } - if let Some(ref packages) = self.package { - for (override_name, profile) in packages { - profile.validate_override("package")?; - profile.validate_profile( - &format!("{name}.package.{override_name}"), - cli_unstable, - features, - )?; - } +/// Checks stytax validity and unstable feature gate for a given profile. +pub fn validate_profile( + root: &schema::TomlProfile, + name: &str, + cli_unstable: &CliUnstable, + features: &Features, + warnings: &mut Vec, +) -> CargoResult<()> { + validate_profile_layer(root, name, cli_unstable, features)?; + if let Some(ref profile) = root.build_override { + validate_profile_override(profile, "build-override")?; + validate_profile_layer( + profile, + &format!("{name}.build-override"), + cli_unstable, + features, + )?; + } + if let Some(ref packages) = root.package { + for (override_name, profile) in packages { + validate_profile_override(profile, "package")?; + validate_profile_layer( + profile, + &format!("{name}.package.{override_name}"), + cli_unstable, + features, + )?; } + } - // Profile name validation - restricted_names::validate_profile_name(name)?; + // Profile name validation + restricted_names::validate_profile_name(name)?; - if let Some(dir_name) = &self.dir_name { - // This is disabled for now, as we would like to stabilize named - // profiles without this, and then decide in the future if it is - // needed. This helps simplify the UI a little. - bail!( - "dir-name=\"{}\" in profile `{}` is not currently allowed, \ + if let Some(dir_name) = &root.dir_name { + // This is disabled for now, as we would like to stabilize named + // profiles without this, and then decide in the future if it is + // needed. This helps simplify the UI a little. + bail!( + "dir-name=\"{}\" in profile `{}` is not currently allowed, \ directory names are tied to the profile name for custom profiles", - dir_name, - name - ); - } + dir_name, + name + ); + } - // `inherits` validation - if matches!(self.inherits.as_deref(), Some("debug")) { - bail!( - "profile.{}.inherits=\"debug\" should be profile.{}.inherits=\"dev\"", - name, - name - ); - } + // `inherits` validation + if matches!(root.inherits.as_deref(), Some("debug")) { + bail!( + "profile.{}.inherits=\"debug\" should be profile.{}.inherits=\"dev\"", + name, + name + ); + } - match name { - "doc" => { - warnings.push("profile `doc` is deprecated and has no effect".to_string()); - } - "test" | "bench" => { - if self.panic.is_some() { - warnings.push(format!("`panic` setting is ignored for `{}` profile", name)) - } + match name { + "doc" => { + warnings.push("profile `doc` is deprecated and has no effect".to_string()); + } + "test" | "bench" => { + if root.panic.is_some() { + warnings.push(format!("`panic` setting is ignored for `{}` profile", name)) } - _ => {} } + _ => {} + } - if let Some(panic) = &self.panic { - if panic != "unwind" && panic != "abort" { - bail!( - "`panic` setting of `{}` is not a valid setting, \ + if let Some(panic) = &root.panic { + if panic != "unwind" && panic != "abort" { + bail!( + "`panic` setting of `{}` is not a valid setting, \ must be `unwind` or `abort`", - panic - ); - } + panic + ); } + } - if let Some(schema::StringOrBool::String(arg)) = &self.lto { - if arg == "true" || arg == "false" { - bail!( - "`lto` setting of string `\"{arg}\"` for `{name}` profile is not \ + if let Some(schema::StringOrBool::String(arg)) = &root.lto { + if arg == "true" || arg == "false" { + bail!( + "`lto` setting of string `\"{arg}\"` for `{name}` profile is not \ a valid setting, must be a boolean (`true`/`false`) or a string \ (`\"thin\"`/`\"fat\"`/`\"off\"`) or omitted.", - ); - } + ); } - - Ok(()) } - /// Validates a profile. - /// - /// This is a shallow check, which is reused for the profile itself and any overrides. - fn validate_profile( - &self, - name: &str, - cli_unstable: &CliUnstable, - features: &Features, - ) -> CargoResult<()> { - if let Some(codegen_backend) = &self.codegen_backend { - match ( - features.require(Feature::codegen_backend()), - cli_unstable.codegen_backend, - ) { - (Err(e), false) => return Err(e), - _ => {} - } + Ok(()) +} - if codegen_backend.contains(|c: char| !c.is_ascii_alphanumeric() && c != '_') { - bail!( - "`profile.{}.codegen-backend` setting of `{}` is not a valid backend name.", - name, - codegen_backend, - ); - } - } - if self.rustflags.is_some() { - match ( - features.require(Feature::profile_rustflags()), - cli_unstable.profile_rustflags, - ) { - (Err(e), false) => return Err(e), - _ => {} - } - } - if self.trim_paths.is_some() { - match ( - features.require(Feature::trim_paths()), - cli_unstable.trim_paths, - ) { - (Err(e), false) => return Err(e), - _ => {} - } +/// Validates a profile. +/// +/// This is a shallow check, which is reused for the profile itself and any overrides. +fn validate_profile_layer( + profile: &schema::TomlProfile, + name: &str, + cli_unstable: &CliUnstable, + features: &Features, +) -> CargoResult<()> { + if let Some(codegen_backend) = &profile.codegen_backend { + match ( + features.require(Feature::codegen_backend()), + cli_unstable.codegen_backend, + ) { + (Err(e), false) => return Err(e), + _ => {} } - Ok(()) - } - /// Validation that is specific to an override. - fn validate_override(&self, which: &str) -> CargoResult<()> { - if self.package.is_some() { - bail!("package-specific profiles cannot be nested"); - } - if self.build_override.is_some() { - bail!("build-override profiles cannot be nested"); - } - if self.panic.is_some() { - bail!("`panic` may not be specified in a `{}` profile", which) + if codegen_backend.contains(|c: char| !c.is_ascii_alphanumeric() && c != '_') { + bail!( + "`profile.{}.codegen-backend` setting of `{}` is not a valid backend name.", + name, + codegen_backend, + ); } - if self.lto.is_some() { - bail!("`lto` may not be specified in a `{}` profile", which) + } + if profile.rustflags.is_some() { + match ( + features.require(Feature::profile_rustflags()), + cli_unstable.profile_rustflags, + ) { + (Err(e), false) => return Err(e), + _ => {} } - if self.rpath.is_some() { - bail!("`rpath` may not be specified in a `{}` profile", which) + } + if profile.trim_paths.is_some() { + match ( + features.require(Feature::trim_paths()), + cli_unstable.trim_paths, + ) { + (Err(e), false) => return Err(e), + _ => {} } - Ok(()) } + Ok(()) +} +/// Validation that is specific to an override. +fn validate_profile_override(profile: &schema::TomlProfile, which: &str) -> CargoResult<()> { + if profile.package.is_some() { + bail!("package-specific profiles cannot be nested"); + } + if profile.build_override.is_some() { + bail!("build-override profiles cannot be nested"); + } + if profile.panic.is_some() { + bail!("`panic` may not be specified in a `{}` profile", which) + } + if profile.lto.is_some() { + bail!("`lto` may not be specified in a `{}` profile", which) + } + if profile.rpath.is_some() { + bail!("`rpath` may not be specified in a `{}` profile", which) + } + Ok(()) +} + +impl schema::TomlProfile { /// Overwrite self's values with the given profile. pub fn merge(&mut self, profile: &schema::TomlProfile) { if let Some(v) = &profile.opt_level { From 1ed0efcd1f15f98dbecfcbbb5aa4adea13dac840 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Sat, 18 Nov 2023 21:56:38 -0600 Subject: [PATCH 07/15] refactor(toml): Move Profile layering to be part of schema API --- src/cargo/util/toml/mod.rs | 92 ----------------------------------- src/cargo/util/toml/schema.rs | 92 +++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 92 deletions(-) diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index e76221e55af..c3be4bde526 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -2141,98 +2141,6 @@ fn validate_profile_override(profile: &schema::TomlProfile, which: &str) -> Carg Ok(()) } -impl schema::TomlProfile { - /// Overwrite self's values with the given profile. - pub fn merge(&mut self, profile: &schema::TomlProfile) { - if let Some(v) = &profile.opt_level { - self.opt_level = Some(v.clone()); - } - - if let Some(v) = &profile.lto { - self.lto = Some(v.clone()); - } - - if let Some(v) = &profile.codegen_backend { - self.codegen_backend = Some(v.clone()); - } - - if let Some(v) = profile.codegen_units { - self.codegen_units = Some(v); - } - - if let Some(v) = profile.debug { - self.debug = Some(v); - } - - if let Some(v) = profile.debug_assertions { - self.debug_assertions = Some(v); - } - - if let Some(v) = &profile.split_debuginfo { - self.split_debuginfo = Some(v.clone()); - } - - if let Some(v) = profile.rpath { - self.rpath = Some(v); - } - - if let Some(v) = &profile.panic { - self.panic = Some(v.clone()); - } - - if let Some(v) = profile.overflow_checks { - self.overflow_checks = Some(v); - } - - if let Some(v) = profile.incremental { - self.incremental = Some(v); - } - - if let Some(v) = &profile.rustflags { - self.rustflags = Some(v.clone()); - } - - if let Some(other_package) = &profile.package { - match &mut self.package { - Some(self_package) => { - for (spec, other_pkg_profile) in other_package { - match self_package.get_mut(spec) { - Some(p) => p.merge(other_pkg_profile), - None => { - self_package.insert(spec.clone(), other_pkg_profile.clone()); - } - } - } - } - None => self.package = Some(other_package.clone()), - } - } - - if let Some(other_bo) = &profile.build_override { - match &mut self.build_override { - Some(self_bo) => self_bo.merge(other_bo), - None => self.build_override = Some(other_bo.clone()), - } - } - - if let Some(v) = &profile.inherits { - self.inherits = Some(v.clone()); - } - - if let Some(v) = &profile.dir_name { - self.dir_name = Some(v.clone()); - } - - if let Some(v) = &profile.strip { - self.strip = Some(v.clone()); - } - - if let Some(v) = &profile.trim_paths { - self.trim_paths = Some(v.clone()) - } - } -} - impl schema::InheritableLints { fn inherit_with<'a>( self, diff --git a/src/cargo/util/toml/schema.rs b/src/cargo/util/toml/schema.rs index 96114606c9c..90cc9a84499 100644 --- a/src/cargo/util/toml/schema.rs +++ b/src/cargo/util/toml/schema.rs @@ -670,6 +670,98 @@ pub struct TomlProfile { pub trim_paths: Option, } +impl TomlProfile { + /// Overwrite self's values with the given profile. + pub fn merge(&mut self, profile: &Self) { + if let Some(v) = &profile.opt_level { + self.opt_level = Some(v.clone()); + } + + if let Some(v) = &profile.lto { + self.lto = Some(v.clone()); + } + + if let Some(v) = &profile.codegen_backend { + self.codegen_backend = Some(v.clone()); + } + + if let Some(v) = profile.codegen_units { + self.codegen_units = Some(v); + } + + if let Some(v) = profile.debug { + self.debug = Some(v); + } + + if let Some(v) = profile.debug_assertions { + self.debug_assertions = Some(v); + } + + if let Some(v) = &profile.split_debuginfo { + self.split_debuginfo = Some(v.clone()); + } + + if let Some(v) = profile.rpath { + self.rpath = Some(v); + } + + if let Some(v) = &profile.panic { + self.panic = Some(v.clone()); + } + + if let Some(v) = profile.overflow_checks { + self.overflow_checks = Some(v); + } + + if let Some(v) = profile.incremental { + self.incremental = Some(v); + } + + if let Some(v) = &profile.rustflags { + self.rustflags = Some(v.clone()); + } + + if let Some(other_package) = &profile.package { + match &mut self.package { + Some(self_package) => { + for (spec, other_pkg_profile) in other_package { + match self_package.get_mut(spec) { + Some(p) => p.merge(other_pkg_profile), + None => { + self_package.insert(spec.clone(), other_pkg_profile.clone()); + } + } + } + } + None => self.package = Some(other_package.clone()), + } + } + + if let Some(other_bo) = &profile.build_override { + match &mut self.build_override { + Some(self_bo) => self_bo.merge(other_bo), + None => self.build_override = Some(other_bo.clone()), + } + } + + if let Some(v) = &profile.inherits { + self.inherits = Some(v.clone()); + } + + if let Some(v) = &profile.dir_name { + self.dir_name = Some(v.clone()); + } + + if let Some(v) = &profile.strip { + self.strip = Some(v.clone()); + } + + if let Some(v) = &profile.trim_paths { + self.trim_paths = Some(v.clone()) + } + } +} + #[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)] pub enum ProfilePackageSpec { Spec(PackageIdSpec), From 3cb313002f9e508bed1c87415d32fa777c12f0b5 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 21 Nov 2023 20:16:37 -0600 Subject: [PATCH 08/15] refactor(toml): Group inheritance code --- src/cargo/util/toml/mod.rs | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index c3be4bde526..63d91a795f6 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -1576,6 +1576,24 @@ impl schema::InheritableField { } } +impl schema::InheritableLints { + fn inherit_with<'a>( + self, + get_ws_inheritable: impl FnOnce() -> CargoResult, + ) -> CargoResult { + if self.workspace { + if !self.lints.is_empty() { + anyhow::bail!("cannot override `workspace.lints` in `lints`, either remove the overrides or `lints.workspace = true` and manually specify the lints"); + } + get_ws_inheritable().with_context(|| { + "error inheriting `lints` from workspace root manifest's `workspace.lints`" + }) + } else { + Ok(self.lints) + } + } +} + impl schema::InheritableDependency { fn inherit_with<'a>( self, @@ -2141,24 +2159,6 @@ fn validate_profile_override(profile: &schema::TomlProfile, which: &str) -> Carg Ok(()) } -impl schema::InheritableLints { - fn inherit_with<'a>( - self, - get_ws_inheritable: impl FnOnce() -> CargoResult, - ) -> CargoResult { - if self.workspace { - if !self.lints.is_empty() { - anyhow::bail!("cannot override `workspace.lints` in `lints`, either remove the overrides or `lints.workspace = true` and manually specify the lints"); - } - get_ws_inheritable().with_context(|| { - "error inheriting `lints` from workspace root manifest's `workspace.lints`" - }) - } else { - Ok(self.lints) - } - } -} - pub trait ResolveToPath { fn resolve(&self, config: &Config) -> PathBuf; } From 3047a41be38ab24d3a02394148e800ed143f6929 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 21 Nov 2023 20:24:27 -0600 Subject: [PATCH 09/15] refactor(toml): Make InheritableField::inherit_with a free function --- src/cargo/util/toml/mod.rs | 59 ++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index 63d91a795f6..5ddd5eee66b 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -503,7 +503,7 @@ impl schema::TomlManifest { let version = package .version .clone() - .map(|version| version.inherit_with("version", || inherit()?.version())) + .map(|version| field_inherit_with(version, "version", || inherit()?.version())) .transpose()?; package.version = version.clone().map(schema::InheritableField::Value); @@ -517,8 +517,7 @@ impl schema::TomlManifest { ); let edition = if let Some(edition) = package.edition.clone() { - let edition: Edition = edition - .inherit_with("edition", || inherit()?.edition())? + let edition: Edition = field_inherit_with(edition, "edition", || inherit()?.edition())? .parse() .with_context(|| "failed to parse the `edition` key")?; package.edition = Some(schema::InheritableField::Value(edition.to_string())); @@ -543,9 +542,9 @@ impl schema::TomlManifest { } let rust_version = if let Some(rust_version) = &package.rust_version { - let rust_version = rust_version - .clone() - .inherit_with("rust_version", || inherit()?.rust_version())?; + let rust_version = field_inherit_with(rust_version.clone(), "rust_version", || { + inherit()?.rust_version() + })?; let req = rust_version.to_caret_req(); if let Some(first_version) = edition.first_version() { let unsupported = @@ -800,13 +799,13 @@ impl schema::TomlManifest { let exclude = package .exclude .clone() - .map(|mw| mw.inherit_with("exclude", || inherit()?.exclude())) + .map(|mw| field_inherit_with(mw, "exclude", || inherit()?.exclude())) .transpose()? .unwrap_or_default(); let include = package .include .clone() - .map(|mw| mw.inherit_with("include", || inherit()?.include())) + .map(|mw| field_inherit_with(mw, "include", || inherit()?.include())) .transpose()? .unwrap_or_default(); let empty_features = BTreeMap::new(); @@ -833,70 +832,72 @@ impl schema::TomlManifest { description: package .description .clone() - .map(|mw| mw.inherit_with("description", || inherit()?.description())) + .map(|mw| field_inherit_with(mw, "description", || inherit()?.description())) .transpose()?, homepage: package .homepage .clone() - .map(|mw| mw.inherit_with("homepage", || inherit()?.homepage())) + .map(|mw| field_inherit_with(mw, "homepage", || inherit()?.homepage())) .transpose()?, documentation: package .documentation .clone() - .map(|mw| mw.inherit_with("documentation", || inherit()?.documentation())) + .map(|mw| field_inherit_with(mw, "documentation", || inherit()?.documentation())) .transpose()?, readme: readme_for_package( package_root, package .readme .clone() - .map(|mw| mw.inherit_with("readme", || inherit()?.readme(package_root))) + .map(|mw| field_inherit_with(mw, "readme", || inherit()?.readme(package_root))) .transpose()? .as_ref(), ), authors: package .authors .clone() - .map(|mw| mw.inherit_with("authors", || inherit()?.authors())) + .map(|mw| field_inherit_with(mw, "authors", || inherit()?.authors())) .transpose()? .unwrap_or_default(), license: package .license .clone() - .map(|mw| mw.inherit_with("license", || inherit()?.license())) + .map(|mw| field_inherit_with(mw, "license", || inherit()?.license())) .transpose()?, license_file: package .license_file .clone() - .map(|mw| mw.inherit_with("license", || inherit()?.license_file(package_root))) + .map(|mw| { + field_inherit_with(mw, "license", || inherit()?.license_file(package_root)) + }) .transpose()?, repository: package .repository .clone() - .map(|mw| mw.inherit_with("repository", || inherit()?.repository())) + .map(|mw| field_inherit_with(mw, "repository", || inherit()?.repository())) .transpose()?, keywords: package .keywords .clone() - .map(|mw| mw.inherit_with("keywords", || inherit()?.keywords())) + .map(|mw| field_inherit_with(mw, "keywords", || inherit()?.keywords())) .transpose()? .unwrap_or_default(), categories: package .categories .clone() - .map(|mw| mw.inherit_with("categories", || inherit()?.categories())) + .map(|mw| field_inherit_with(mw, "categories", || inherit()?.categories())) .transpose()? .unwrap_or_default(), badges: me .badges .clone() - .map(|mw| mw.inherit_with("badges", || inherit()?.badges())) + .map(|mw| field_inherit_with(mw, "badges", || inherit()?.badges())) .transpose()? .unwrap_or_default(), links: package.links.clone(), rust_version: package .rust_version - .map(|mw| mw.inherit_with("rust-version", || inherit()?.rust_version())) + .map(|mw| field_inherit_with(mw, "rust-version", || inherit()?.rust_version())) .transpose()?, }; package.description = metadata @@ -958,9 +959,7 @@ impl schema::TomlManifest { } let publish = package.publish.clone().map(|publish| { - publish - .inherit_with("publish", || inherit()?.publish()) - .unwrap() + field_inherit_with(publish, "publish", || inherit()?.publish()).unwrap() }); package.publish = publish.clone().map(|p| schema::InheritableField::Value(p)); @@ -1559,13 +1558,12 @@ impl InheritableFields { } } -impl schema::InheritableField { - fn inherit_with<'a>( - self, - label: &str, - get_ws_inheritable: impl FnOnce() -> CargoResult, - ) -> CargoResult { - match self { +fn field_inherit_with<'a, T>( + field: schema::InheritableField, + label: &str, + get_ws_inheritable: impl FnOnce() -> CargoResult, +) -> CargoResult { + match field { schema::InheritableField::Value(value) => Ok(value), schema::InheritableField::Inherit(_) => get_ws_inheritable().with_context(|| { format!( @@ -1573,7 +1571,6 @@ impl schema::InheritableField { ) }), } - } } impl schema::InheritableLints { From e48befe04efe0d4551bb7226a26089a37f438608 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 21 Nov 2023 20:26:34 -0600 Subject: [PATCH 10/15] refactor(toml): Make InheritableLints::inherit_with a free function --- src/cargo/util/toml/mod.rs | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index 5ddd5eee66b..a38fc2daa8d 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -718,7 +718,7 @@ impl schema::TomlManifest { let lints = me .lints .clone() - .map(|mw| mw.inherit_with(|| inherit()?.lints())) + .map(|mw| lints_inherit_with(mw, || inherit()?.lints())) .transpose()?; let lints = verify_lints(lints)?; let default = schema::TomlLints::default(); @@ -1573,21 +1573,19 @@ fn field_inherit_with<'a, T>( } } -impl schema::InheritableLints { - fn inherit_with<'a>( - self, - get_ws_inheritable: impl FnOnce() -> CargoResult, - ) -> CargoResult { - if self.workspace { - if !self.lints.is_empty() { - anyhow::bail!("cannot override `workspace.lints` in `lints`, either remove the overrides or `lints.workspace = true` and manually specify the lints"); - } - get_ws_inheritable().with_context(|| { - "error inheriting `lints` from workspace root manifest's `workspace.lints`" - }) - } else { - Ok(self.lints) +fn lints_inherit_with( + lints: schema::InheritableLints, + get_ws_inheritable: impl FnOnce() -> CargoResult, +) -> CargoResult { + if lints.workspace { + if !lints.lints.is_empty() { + anyhow::bail!("cannot override `workspace.lints` in `lints`, either remove the overrides or `lints.workspace = true` and manually specify the lints"); } + get_ws_inheritable().with_context(|| { + "error inheriting `lints` from workspace root manifest's `workspace.lints`" + }) + } else { + Ok(lints.lints) } } From d32e1f944827b145161893fedbaafc028e187d0f Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 23 Nov 2023 20:55:48 -0600 Subject: [PATCH 11/15] refactor(toml): Make InheritableDependency::inhert_with a free function --- src/cargo/util/toml/mod.rs | 175 ++++++++++++++++++------------------- 1 file changed, 87 insertions(+), 88 deletions(-) diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index a38fc2daa8d..dd310707d65 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -661,7 +661,7 @@ impl schema::TomlManifest { let mut deps: BTreeMap = BTreeMap::new(); for (n, v) in dependencies.iter() { - let resolved = v.clone().inherit_with(n, inheritable, cx)?; + let resolved = dependency_inherit_with(v.clone(), n, inheritable, cx)?; let dep = resolved.to_dependency(n, cx, kind)?; let name_in_toml = dep.name_in_toml().as_str(); validate_package_name(name_in_toml, "dependency name", "")?; @@ -1589,110 +1589,109 @@ fn lints_inherit_with( } } -impl schema::InheritableDependency { - fn inherit_with<'a>( - self, - name: &str, - inheritable: impl FnOnce() -> CargoResult<&'a InheritableFields>, - cx: &mut Context<'_, '_>, - ) -> CargoResult { - match self { +fn dependency_inherit_with<'a>( + dependency: schema::InheritableDependency, + name: &str, + inheritable: impl FnOnce() -> CargoResult<&'a InheritableFields>, + cx: &mut Context<'_, '_>, +) -> CargoResult { + match dependency { schema::InheritableDependency::Value(value) => Ok(value), schema::InheritableDependency::Inherit(w) => { - w.inherit_with(name, inheritable, cx).with_context(|| { + inner_dependency_inherit_with(w, name, inheritable, cx).with_context(|| { format!( "error inheriting `{name}` from workspace root manifest's `workspace.dependencies.{name}`", ) }) } } - } } -impl schema::TomlInheritedDependency { - fn inherit_with<'a>( - &self, - name: &str, - inheritable: impl FnOnce() -> CargoResult<&'a InheritableFields>, - cx: &mut Context<'_, '_>, - ) -> CargoResult { - fn default_features_msg(label: &str, ws_def_feat: Option, cx: &mut Context<'_, '_>) { - let ws_def_feat = match ws_def_feat { - Some(true) => "true", - Some(false) => "false", - None => "not specified", - }; - cx.warnings.push(format!( - "`default-features` is ignored for {label}, since `default-features` was \ +fn inner_dependency_inherit_with<'a>( + dependency: schema::TomlInheritedDependency, + name: &str, + inheritable: impl FnOnce() -> CargoResult<&'a InheritableFields>, + cx: &mut Context<'_, '_>, +) -> CargoResult { + fn default_features_msg(label: &str, ws_def_feat: Option, cx: &mut Context<'_, '_>) { + let ws_def_feat = match ws_def_feat { + Some(true) => "true", + Some(false) => "false", + None => "not specified", + }; + cx.warnings.push(format!( + "`default-features` is ignored for {label}, since `default-features` was \ {ws_def_feat} for `workspace.dependencies.{label}`, \ this could become a hard error in the future" - )) - } - if self.default_features.is_some() && self.default_features2.is_some() { - warn_on_deprecated("default-features", name, "dependency", cx.warnings); - } - inheritable()?.get_dependency(name, cx.root).map(|d| { - match d { - schema::TomlDependency::Simple(s) => { - if let Some(false) = self.default_features() { - default_features_msg(name, None, cx); - } - if self.optional.is_some() || self.features.is_some() || self.public.is_some() { - schema::TomlDependency::Detailed(schema::TomlDetailedDependency { - version: Some(s), - optional: self.optional, - features: self.features.clone(), - public: self.public, - ..Default::default() - }) - } else { - schema::TomlDependency::Simple(s) - } + )) + } + if dependency.default_features.is_some() && dependency.default_features2.is_some() { + warn_on_deprecated("default-features", name, "dependency", cx.warnings); + } + inheritable()?.get_dependency(name, cx.root).map(|d| { + match d { + schema::TomlDependency::Simple(s) => { + if let Some(false) = dependency.default_features() { + default_features_msg(name, None, cx); } - schema::TomlDependency::Detailed(d) => { - let mut d = d.clone(); - match (self.default_features(), d.default_features()) { - // member: default-features = true and - // workspace: default-features = false should turn on - // default-features - (Some(true), Some(false)) => { - d.default_features = Some(true); - } - // member: default-features = false and - // workspace: default-features = true should ignore member - // default-features - (Some(false), Some(true)) => { - default_features_msg(name, Some(true), cx); - } - // member: default-features = false and - // workspace: dep = "1.0" should ignore member default-features - (Some(false), None) => { - default_features_msg(name, None, cx); - } - _ => {} + if dependency.optional.is_some() + || dependency.features.is_some() + || dependency.public.is_some() + { + schema::TomlDependency::Detailed(schema::TomlDetailedDependency { + version: Some(s), + optional: dependency.optional, + features: dependency.features.clone(), + public: dependency.public, + ..Default::default() + }) + } else { + schema::TomlDependency::Simple(s) + } + } + schema::TomlDependency::Detailed(d) => { + let mut d = d.clone(); + match (dependency.default_features(), d.default_features()) { + // member: default-features = true and + // workspace: default-features = false should turn on + // default-features + (Some(true), Some(false)) => { + d.default_features = Some(true); + } + // member: default-features = false and + // workspace: default-features = true should ignore member + // default-features + (Some(false), Some(true)) => { + default_features_msg(name, Some(true), cx); } - // Inherit the workspace configuration for `public` unless - // it's explicitly specified for this dependency. - if let Some(public) = self.public { - d.public = Some(public); + // member: default-features = false and + // workspace: dep = "1.0" should ignore member default-features + (Some(false), None) => { + default_features_msg(name, None, cx); } - d.features = match (d.features.clone(), self.features.clone()) { - (Some(dep_feat), Some(inherit_feat)) => Some( - dep_feat - .into_iter() - .chain(inherit_feat) - .collect::>(), - ), - (Some(dep_fet), None) => Some(dep_fet), - (None, Some(inherit_feat)) => Some(inherit_feat), - (None, None) => None, - }; - d.optional = self.optional; - schema::TomlDependency::Detailed(d) + _ => {} + } + // Inherit the workspace configuration for `public` unless + // it's explicitly specified for this dependency. + if let Some(public) = dependency.public { + d.public = Some(public); } + d.features = match (d.features.clone(), dependency.features.clone()) { + (Some(dep_feat), Some(inherit_feat)) => Some( + dep_feat + .into_iter() + .chain(inherit_feat) + .collect::>(), + ), + (Some(dep_fet), None) => Some(dep_fet), + (None, Some(inherit_feat)) => Some(inherit_feat), + (None, None) => None, + }; + d.optional = dependency.optional; + schema::TomlDependency::Detailed(d) } - }) - } + } + }) } impl schema::TomlDependency

{ From 94395d4b4509a03c316fe5da25851e71fa565fdb Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 29 Nov 2023 15:38:36 -0600 Subject: [PATCH 12/15] refactor(toml): Make Detailed Dep's to_dependency a free function --- src/cargo/util/toml/mod.rs | 396 +++++++++++++++++++------------------ 1 file changed, 200 insertions(+), 196 deletions(-) diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index dd310707d65..428a0cd2596 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -1730,255 +1730,259 @@ impl schema::TomlDependency

{ kind: Option, ) -> CargoResult { match *self { - schema::TomlDependency::Simple(ref version) => schema::TomlDetailedDependency::

{ - version: Some(version.clone()), - ..Default::default() + schema::TomlDependency::Simple(ref version) => detailed_dep_to_dependency( + &schema::TomlDetailedDependency::

{ + version: Some(version.clone()), + ..Default::default() + }, + name, + cx, + kind, + ), + schema::TomlDependency::Detailed(ref details) => { + detailed_dep_to_dependency(details, name, cx, kind) } - .to_dependency(name, cx, kind), - schema::TomlDependency::Detailed(ref details) => details.to_dependency(name, cx, kind), } } } -impl schema::TomlDetailedDependency

{ - fn to_dependency( - &self, - name_in_toml: &str, - cx: &mut Context<'_, '_>, - kind: Option, - ) -> CargoResult { - if self.version.is_none() && self.path.is_none() && self.git.is_none() { - let msg = format!( - "dependency ({}) specified without \ +fn detailed_dep_to_dependency( + orig: &schema::TomlDetailedDependency

, + name_in_toml: &str, + cx: &mut Context<'_, '_>, + kind: Option, +) -> CargoResult { + if orig.version.is_none() && orig.path.is_none() && orig.git.is_none() { + let msg = format!( + "dependency ({}) specified without \ providing a local path, Git repository, version, or \ workspace dependency to use. This will be considered an \ error in future versions", - name_in_toml - ); - cx.warnings.push(msg); - } + name_in_toml + ); + cx.warnings.push(msg); + } - if let Some(version) = &self.version { - if version.contains('+') { - cx.warnings.push(format!( - "version requirement `{}` for dependency `{}` \ + if let Some(version) = &orig.version { + if version.contains('+') { + cx.warnings.push(format!( + "version requirement `{}` for dependency `{}` \ includes semver metadata which will be ignored, removing the \ metadata is recommended to avoid confusion", - version, name_in_toml - )); - } + version, name_in_toml + )); } + } - if self.git.is_none() { - let git_only_keys = [ - (&self.branch, "branch"), - (&self.tag, "tag"), - (&self.rev, "rev"), - ]; + if orig.git.is_none() { + let git_only_keys = [ + (&orig.branch, "branch"), + (&orig.tag, "tag"), + (&orig.rev, "rev"), + ]; - for &(key, key_name) in &git_only_keys { - if key.is_some() { - bail!( - "key `{}` is ignored for dependency ({}).", - key_name, - name_in_toml - ); - } + for &(key, key_name) in &git_only_keys { + if key.is_some() { + bail!( + "key `{}` is ignored for dependency ({}).", + key_name, + name_in_toml + ); } } + } - // Early detection of potentially misused feature syntax - // instead of generating a "feature not found" error. - if let Some(features) = &self.features { - for feature in features { - if feature.contains('/') { - bail!( - "feature `{}` in dependency `{}` is not allowed to contain slashes\n\ + // Early detection of potentially misused feature syntax + // instead of generating a "feature not found" error. + if let Some(features) = &orig.features { + for feature in features { + if feature.contains('/') { + bail!( + "feature `{}` in dependency `{}` is not allowed to contain slashes\n\ If you want to enable features of a transitive dependency, \ the direct dependency needs to re-export those features from \ the `[features]` table.", - feature, - name_in_toml - ); - } - if feature.starts_with("dep:") { - bail!( - "feature `{}` in dependency `{}` is not allowed to use explicit \ + feature, + name_in_toml + ); + } + if feature.starts_with("dep:") { + bail!( + "feature `{}` in dependency `{}` is not allowed to use explicit \ `dep:` syntax\n\ If you want to enable an optional dependency, specify the name \ of the optional dependency without the `dep:` prefix, or specify \ a feature from the dependency's `[features]` table that enables \ the optional dependency.", - feature, - name_in_toml - ); - } + feature, + name_in_toml + ); } } + } - let new_source_id = match ( - self.git.as_ref(), - self.path.as_ref(), - self.registry.as_ref(), - self.registry_index.as_ref(), - ) { - (Some(_), _, Some(_), _) | (Some(_), _, _, Some(_)) => bail!( - "dependency ({}) specification is ambiguous. \ + let new_source_id = match ( + orig.git.as_ref(), + orig.path.as_ref(), + orig.registry.as_ref(), + orig.registry_index.as_ref(), + ) { + (Some(_), _, Some(_), _) | (Some(_), _, _, Some(_)) => bail!( + "dependency ({}) specification is ambiguous. \ Only one of `git` or `registry` is allowed.", - name_in_toml - ), - (_, _, Some(_), Some(_)) => bail!( - "dependency ({}) specification is ambiguous. \ + name_in_toml + ), + (_, _, Some(_), Some(_)) => bail!( + "dependency ({}) specification is ambiguous. \ Only one of `registry` or `registry-index` is allowed.", - name_in_toml - ), - (Some(git), maybe_path, _, _) => { - if maybe_path.is_some() { - bail!( - "dependency ({}) specification is ambiguous. \ + name_in_toml + ), + (Some(git), maybe_path, _, _) => { + if maybe_path.is_some() { + bail!( + "dependency ({}) specification is ambiguous. \ Only one of `git` or `path` is allowed.", - name_in_toml - ); - } + name_in_toml + ); + } - let n_details = [&self.branch, &self.tag, &self.rev] - .iter() - .filter(|d| d.is_some()) - .count(); + let n_details = [&orig.branch, &orig.tag, &orig.rev] + .iter() + .filter(|d| d.is_some()) + .count(); - if n_details > 1 { - bail!( - "dependency ({}) specification is ambiguous. \ + if n_details > 1 { + bail!( + "dependency ({}) specification is ambiguous. \ Only one of `branch`, `tag` or `rev` is allowed.", - name_in_toml - ); - } + name_in_toml + ); + } - let reference = self - .branch - .clone() - .map(GitReference::Branch) - .or_else(|| self.tag.clone().map(GitReference::Tag)) - .or_else(|| self.rev.clone().map(GitReference::Rev)) - .unwrap_or(GitReference::DefaultBranch); - let loc = git.into_url()?; - - if let Some(fragment) = loc.fragment() { - let msg = format!( - "URL fragment `#{}` in git URL is ignored for dependency ({}). \ + let reference = orig + .branch + .clone() + .map(GitReference::Branch) + .or_else(|| orig.tag.clone().map(GitReference::Tag)) + .or_else(|| orig.rev.clone().map(GitReference::Rev)) + .unwrap_or(GitReference::DefaultBranch); + let loc = git.into_url()?; + + if let Some(fragment) = loc.fragment() { + let msg = format!( + "URL fragment `#{}` in git URL is ignored for dependency ({}). \ If you were trying to specify a specific git revision, \ use `rev = \"{}\"` in the dependency declaration.", - fragment, name_in_toml, fragment - ); - cx.warnings.push(msg) - } - - SourceId::for_git(&loc, reference)? - } - (None, Some(path), _, _) => { - let path = path.resolve(cx.config); - cx.nested_paths.push(path.clone()); - // If the source ID for the package we're parsing is a path - // source, then we normalize the path here to get rid of - // components like `..`. - // - // The purpose of this is to get a canonical ID for the package - // that we're depending on to ensure that builds of this package - // always end up hashing to the same value no matter where it's - // built from. - if cx.source_id.is_path() { - let path = cx.root.join(path); - let path = paths::normalize_path(&path); - SourceId::for_path(&path)? - } else { - cx.source_id - } - } - (None, None, Some(registry), None) => SourceId::alt_registry(cx.config, registry)?, - (None, None, None, Some(registry_index)) => { - let url = registry_index.into_url()?; - SourceId::for_registry(&url)? + fragment, name_in_toml, fragment + ); + cx.warnings.push(msg) } - (None, None, None, None) => SourceId::crates_io(cx.config)?, - }; - - let (pkg_name, explicit_name_in_toml) = match self.package { - Some(ref s) => (&s[..], Some(name_in_toml)), - None => (name_in_toml, None), - }; - let version = self.version.as_deref(); - let mut dep = Dependency::parse(pkg_name, version, new_source_id)?; - if self.default_features.is_some() && self.default_features2.is_some() { - warn_on_deprecated("default-features", name_in_toml, "dependency", cx.warnings); - } - dep.set_features(self.features.iter().flatten()) - .set_default_features(self.default_features().unwrap_or(true)) - .set_optional(self.optional.unwrap_or(false)) - .set_platform(cx.platform.clone()); - if let Some(registry) = &self.registry { - let registry_id = SourceId::alt_registry(cx.config, registry)?; - dep.set_registry_id(registry_id); + SourceId::for_git(&loc, reference)? + } + (None, Some(path), _, _) => { + let path = path.resolve(cx.config); + cx.nested_paths.push(path.clone()); + // If the source ID for the package we're parsing is a path + // source, then we normalize the path here to get rid of + // components like `..`. + // + // The purpose of this is to get a canonical ID for the package + // that we're depending on to ensure that builds of this package + // always end up hashing to the same value no matter where it's + // built from. + if cx.source_id.is_path() { + let path = cx.root.join(path); + let path = paths::normalize_path(&path); + SourceId::for_path(&path)? + } else { + cx.source_id + } } - if let Some(registry_index) = &self.registry_index { + (None, None, Some(registry), None) => SourceId::alt_registry(cx.config, registry)?, + (None, None, None, Some(registry_index)) => { let url = registry_index.into_url()?; - let registry_id = SourceId::for_registry(&url)?; - dep.set_registry_id(registry_id); + SourceId::for_registry(&url)? } + (None, None, None, None) => SourceId::crates_io(cx.config)?, + }; - if let Some(kind) = kind { - dep.set_kind(kind); - } - if let Some(name_in_toml) = explicit_name_in_toml { - dep.set_explicit_name_in_toml(name_in_toml); - } + let (pkg_name, explicit_name_in_toml) = match orig.package { + Some(ref s) => (&s[..], Some(name_in_toml)), + None => (name_in_toml, None), + }; - if let Some(p) = self.public { - cx.features.require(Feature::public_dependency())?; + let version = orig.version.as_deref(); + let mut dep = Dependency::parse(pkg_name, version, new_source_id)?; + if orig.default_features.is_some() && orig.default_features2.is_some() { + warn_on_deprecated("default-features", name_in_toml, "dependency", cx.warnings); + } + dep.set_features(orig.features.iter().flatten()) + .set_default_features(orig.default_features().unwrap_or(true)) + .set_optional(orig.optional.unwrap_or(false)) + .set_platform(cx.platform.clone()); + if let Some(registry) = &orig.registry { + let registry_id = SourceId::alt_registry(cx.config, registry)?; + dep.set_registry_id(registry_id); + } + if let Some(registry_index) = &orig.registry_index { + let url = registry_index.into_url()?; + let registry_id = SourceId::for_registry(&url)?; + dep.set_registry_id(registry_id); + } - if dep.kind() != DepKind::Normal { - bail!("'public' specifier can only be used on regular dependencies, not {:?} dependencies", dep.kind()); - } + if let Some(kind) = kind { + dep.set_kind(kind); + } + if let Some(name_in_toml) = explicit_name_in_toml { + dep.set_explicit_name_in_toml(name_in_toml); + } - dep.set_public(p); + if let Some(p) = orig.public { + cx.features.require(Feature::public_dependency())?; + + if dep.kind() != DepKind::Normal { + bail!("'public' specifier can only be used on regular dependencies, not {:?} dependencies", dep.kind()); } - if let (Some(artifact), is_lib, target) = ( - self.artifact.as_ref(), - self.lib.unwrap_or(false), - self.target.as_deref(), - ) { - if cx.config.cli_unstable().bindeps { - let artifact = Artifact::parse(&artifact.0, is_lib, target)?; - if dep.kind() != DepKind::Build - && artifact.target() == Some(ArtifactTarget::BuildDependencyAssumeTarget) - { - bail!( - r#"`target = "target"` in normal- or dev-dependencies has no effect ({})"#, - name_in_toml - ); - } - dep.set_artifact(artifact) - } else { - bail!("`artifact = …` requires `-Z bindeps` ({})", name_in_toml); - } - } else if self.lib.is_some() || self.target.is_some() { - for (is_set, specifier) in [ - (self.lib.is_some(), "lib"), - (self.target.is_some(), "target"), - ] { - if !is_set { - continue; - } + dep.set_public(p); + } + + if let (Some(artifact), is_lib, target) = ( + orig.artifact.as_ref(), + orig.lib.unwrap_or(false), + orig.target.as_deref(), + ) { + if cx.config.cli_unstable().bindeps { + let artifact = Artifact::parse(&artifact.0, is_lib, target)?; + if dep.kind() != DepKind::Build + && artifact.target() == Some(ArtifactTarget::BuildDependencyAssumeTarget) + { bail!( - "'{}' specifier cannot be used without an 'artifact = …' value ({})", - specifier, + r#"`target = "target"` in normal- or dev-dependencies has no effect ({})"#, name_in_toml - ) + ); } + dep.set_artifact(artifact) + } else { + bail!("`artifact = …` requires `-Z bindeps` ({})", name_in_toml); + } + } else if orig.lib.is_some() || orig.target.is_some() { + for (is_set, specifier) in [ + (orig.lib.is_some(), "lib"), + (orig.target.is_some(), "target"), + ] { + if !is_set { + continue; + } + bail!( + "'{}' specifier cannot be used without an 'artifact = …' value ({})", + specifier, + name_in_toml + ) } - Ok(dep) } + Ok(dep) } /// Checks syntax validity and unstable feature gate for each profile. From 8ff20c7b3c6a3edac6f7a5d1b38b3fbebc9fb421 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Wed, 29 Nov 2023 15:41:57 -0600 Subject: [PATCH 13/15] refactor(toml): Make Dep's to_dependency a free function --- src/cargo/util/toml/mod.rs | 47 +++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index 428a0cd2596..7f054a7c06a 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -662,7 +662,7 @@ impl schema::TomlManifest { let mut deps: BTreeMap = BTreeMap::new(); for (n, v) in dependencies.iter() { let resolved = dependency_inherit_with(v.clone(), n, inheritable, cx)?; - let dep = resolved.to_dependency(n, cx, kind)?; + let dep = dep_to_dependency(&resolved, n, cx, kind)?; let name_in_toml = dep.name_in_toml().as_str(); validate_package_name(name_in_toml, "dependency name", "")?; let kind_name = match kind { @@ -1228,7 +1228,7 @@ impl schema::TomlManifest { ); } - let mut dep = replacement.to_dependency(spec.name(), cx, None)?; + let mut dep = dep_to_dependency(replacement, spec.name(), cx, None)?; let version = spec.version().ok_or_else(|| { anyhow!( "replacements must specify a version \ @@ -1274,7 +1274,7 @@ impl schema::TomlManifest { dep.unused_keys(), &mut cx.warnings, ); - dep.to_dependency(name, cx, None) + dep_to_dependency(dep, name, cx, None) }) .collect::>>()?, ); @@ -1707,7 +1707,8 @@ impl schema::TomlDependency

{ features: &Features, kind: Option, ) -> CargoResult { - self.to_dependency( + dep_to_dependency( + self, name, &mut Context { deps: &mut Vec::new(), @@ -1722,26 +1723,26 @@ impl schema::TomlDependency

{ kind, ) } +} - fn to_dependency( - &self, - name: &str, - cx: &mut Context<'_, '_>, - kind: Option, - ) -> CargoResult { - match *self { - schema::TomlDependency::Simple(ref version) => detailed_dep_to_dependency( - &schema::TomlDetailedDependency::

{ - version: Some(version.clone()), - ..Default::default() - }, - name, - cx, - kind, - ), - schema::TomlDependency::Detailed(ref details) => { - detailed_dep_to_dependency(details, name, cx, kind) - } +fn dep_to_dependency( + orig: &schema::TomlDependency

, + name: &str, + cx: &mut Context<'_, '_>, + kind: Option, +) -> CargoResult { + match *orig { + schema::TomlDependency::Simple(ref version) => detailed_dep_to_dependency( + &schema::TomlDetailedDependency::

{ + version: Some(version.clone()), + ..Default::default() + }, + name, + cx, + kind, + ), + schema::TomlDependency::Detailed(ref details) => { + detailed_dep_to_dependency(details, name, cx, kind) } } } From 89f8f5c77a5d9398be728cc8aacf78bc57504ba9 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 30 Nov 2023 10:43:24 -0600 Subject: [PATCH 14/15] refactor(toml): Make Dep's to_dependency_split a free function --- src/cargo/core/workspace.rs | 3 +- src/cargo/util/toml/mod.rs | 56 ++++++++++++++++++------------------- 2 files changed, 29 insertions(+), 30 deletions(-) diff --git a/src/cargo/core/workspace.rs b/src/cargo/core/workspace.rs index ead026f493d..21740cce862 100644 --- a/src/cargo/core/workspace.rs +++ b/src/cargo/core/workspace.rs @@ -437,7 +437,8 @@ impl<'cfg> Workspace<'cfg> { url, deps.iter() .map(|(name, dep)| { - dep.to_dependency_split( + crate::util::toml::to_dependency( + dep, name, source, &mut nested_paths, diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index 7f054a7c06a..a66ed07fca8 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -1694,35 +1694,33 @@ fn inner_dependency_inherit_with<'a>( }) } -impl schema::TomlDependency

{ - pub(crate) fn to_dependency_split( - &self, - name: &str, - source_id: SourceId, - nested_paths: &mut Vec, - config: &Config, - warnings: &mut Vec, - platform: Option, - root: &Path, - features: &Features, - kind: Option, - ) -> CargoResult { - dep_to_dependency( - self, - name, - &mut Context { - deps: &mut Vec::new(), - source_id, - nested_paths, - config, - warnings, - platform, - root, - features, - }, - kind, - ) - } +pub(crate) fn to_dependency( + dep: &schema::TomlDependency

, + name: &str, + source_id: SourceId, + nested_paths: &mut Vec, + config: &Config, + warnings: &mut Vec, + platform: Option, + root: &Path, + features: &Features, + kind: Option, +) -> CargoResult { + dep_to_dependency( + dep, + name, + &mut Context { + deps: &mut Vec::new(), + source_id, + nested_paths, + config, + warnings, + platform, + root, + features, + }, + kind, + ) } fn dep_to_dependency( From 9bb7c97b31918000baa9732c542013941d234e52 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Thu, 30 Nov 2023 10:49:33 -0600 Subject: [PATCH 15/15] refactor(toml): Make rest of TomlManifest logic free functions --- src/cargo/core/features.rs | 4 +- src/cargo/core/package.rs | 6 +- src/cargo/ops/cargo_package.rs | 9 +- src/cargo/util/toml/mod.rs | 1949 ++++++++++++++++---------------- 4 files changed, 979 insertions(+), 989 deletions(-) diff --git a/src/cargo/core/features.rs b/src/cargo/core/features.rs index 2ce4a57c072..4f5b069ffb8 100644 --- a/src/cargo/core/features.rs +++ b/src/cargo/core/features.rs @@ -168,7 +168,7 @@ pub const SEE_CHANNELS: &str = /// - Update [`CLI_VALUES`] to include the new edition. /// - Set [`LATEST_UNSTABLE`] to Some with the new edition. /// - Add an unstable feature to the [`features!`] macro invocation below for the new edition. -/// - Gate on that new feature in [`TomlManifest::to_real_manifest`]. +/// - Gate on that new feature in [`toml::to_real_manifest`]. /// - Update the shell completion files. /// - Update any failing tests (hopefully there are very few). /// - Update unstable.md to add a new section for this new edition (see [this example]). @@ -195,7 +195,7 @@ pub const SEE_CHANNELS: &str = /// [`LATEST_STABLE`]: Edition::LATEST_STABLE /// [this example]: https://github.com/rust-lang/cargo/blob/3ebb5f15a940810f250b68821149387af583a79e/src/doc/src/reference/unstable.md?plain=1#L1238-L1264 /// [`is_stable`]: Edition::is_stable -/// [`TomlManifest::to_real_manifest`]: crate::util::toml::schema::TomlManifest::to_real_manifest +/// [`toml::to_real_manifest`]: crate::util::toml::to_real_manifest /// [`features!`]: macro.features.html #[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, Eq, PartialEq, Serialize, Deserialize)] pub enum Edition { diff --git a/src/cargo/core/package.rs b/src/cargo/core/package.rs index d87f81036b1..7a4f5e07037 100644 --- a/src/cargo/core/package.rs +++ b/src/cargo/core/package.rs @@ -31,6 +31,7 @@ use crate::util::network::http::http_handle_and_timeout; use crate::util::network::http::HttpTimeout; use crate::util::network::retry::{Retry, RetryResult}; use crate::util::network::sleep::SleepTracker; +use crate::util::toml::prepare_for_publish; use crate::util::RustVersion; use crate::util::{self, internal, Config, Progress, ProgressStyle}; @@ -197,10 +198,7 @@ impl Package { } pub fn to_registry_toml(&self, ws: &Workspace<'_>) -> CargoResult { - let manifest = self - .manifest() - .original() - .prepare_for_publish(ws, self.root())?; + let manifest = prepare_for_publish(self.manifest().original(), ws, self.root())?; let toml = toml::to_string_pretty(&manifest)?; Ok(format!("{}\n{}", MANIFEST_PREAMBLE, toml)) } diff --git a/src/cargo/ops/cargo_package.rs b/src/cargo/ops/cargo_package.rs index d837aacdc75..2ff0187feed 100644 --- a/src/cargo/ops/cargo_package.rs +++ b/src/cargo/ops/cargo_package.rs @@ -16,7 +16,7 @@ use crate::sources::PathSource; use crate::util::cache_lock::CacheLockMode; use crate::util::config::JobsConfig; use crate::util::errors::CargoResult; -use crate::util::toml::schema::TomlManifest; +use crate::util::toml::{prepare_for_publish, to_real_manifest}; use crate::util::{self, human_readable_bytes, restricted_names, Config, FileLock}; use crate::{drop_println, ops}; use anyhow::Context as _; @@ -454,14 +454,11 @@ fn build_lock(ws: &Workspace<'_>, orig_pkg: &Package) -> CargoResult { let orig_resolve = ops::load_pkg_lockfile(ws)?; // Convert Package -> TomlManifest -> Manifest -> Package - let toml_manifest = orig_pkg - .manifest() - .original() - .prepare_for_publish(ws, orig_pkg.root())?; + let toml_manifest = prepare_for_publish(orig_pkg.manifest().original(), ws, orig_pkg.root())?; let package_root = orig_pkg.root(); let source_id = orig_pkg.package_id().source_id(); let (manifest, _nested_paths) = - TomlManifest::to_real_manifest(toml_manifest, false, source_id, package_root, config)?; + to_real_manifest(toml_manifest, false, source_id, package_root, config)?; let new_pkg = Package::new(manifest, orig_pkg.manifest_path()); let max_rust_version = new_pkg.rust_version().cloned(); diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index a66ed07fca8..f15838adae5 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -127,13 +127,8 @@ fn read_manifest_from_str( } } return if manifest.project.is_some() || manifest.package.is_some() { - let (mut manifest, paths) = schema::TomlManifest::to_real_manifest( - manifest, - embedded, - source_id, - package_root, - config, - )?; + let (mut manifest, paths) = + to_real_manifest(manifest, embedded, source_id, package_root, config)?; add_unused(manifest.warnings_mut()); if manifest.targets().iter().all(|t| t.is_custom_build()) { bail!( @@ -144,8 +139,7 @@ fn read_manifest_from_str( } Ok((EitherManifest::Real(manifest), paths)) } else { - let (mut m, paths) = - schema::TomlManifest::to_virtual_manifest(manifest, source_id, package_root, config)?; + let (mut m, paths) = to_virtual_manifest(manifest, source_id, package_root, config)?; add_unused(m.warnings_mut()); Ok((EitherManifest::Virtual(m), paths)) }; @@ -185,886 +179,880 @@ fn warn_on_deprecated(new_path: &str, name: &str, kind: &str, warnings: &mut Vec )) } -impl schema::TomlManifest { - /// Prepares the manifest for publishing. - // - Path and git components of dependency specifications are removed. - // - License path is updated to point within the package. - pub fn prepare_for_publish( - &self, - ws: &Workspace<'_>, - package_root: &Path, - ) -> CargoResult { - let config = ws.config(); - let mut package = self.package().unwrap().clone(); - package.workspace = None; - let current_resolver = package - .resolver - .as_ref() - .map(|r| ResolveBehavior::from_manifest(r)) - .unwrap_or_else(|| { - package - .edition - .as_ref() - .and_then(|e| e.as_value()) - .map(|e| Edition::from_str(e)) - .unwrap_or(Ok(Edition::Edition2015)) - .map(|e| e.default_resolve_behavior()) - })?; - if ws.resolve_behavior() != current_resolver { - // This ensures the published crate if built as a root (e.g. `cargo install`) will - // use the same resolver behavior it was tested with in the workspace. - // To avoid forcing a higher MSRV we don't explicitly set this if it would implicitly - // result in the same thing. - package.resolver = Some(ws.resolve_behavior().to_manifest()); - } - if let Some(license_file) = &package.license_file { - let license_file = license_file - .as_value() - .context("license file should have been resolved before `prepare_for_publish()`")?; - let license_path = Path::new(&license_file); - let abs_license_path = paths::normalize_path(&package_root.join(license_path)); - if abs_license_path.strip_prefix(package_root).is_err() { - // This path points outside of the package root. `cargo package` - // will copy it into the root, so adjust the path to this location. - package.license_file = Some(schema::InheritableField::Value( - license_path - .file_name() - .unwrap() - .to_str() - .unwrap() - .to_string(), - )); - } +/// Prepares the manifest for publishing. +// - Path and git components of dependency specifications are removed. +// - License path is updated to point within the package. +pub fn prepare_for_publish( + me: &schema::TomlManifest, + ws: &Workspace<'_>, + package_root: &Path, +) -> CargoResult { + let config = ws.config(); + let mut package = me.package().unwrap().clone(); + package.workspace = None; + let current_resolver = package + .resolver + .as_ref() + .map(|r| ResolveBehavior::from_manifest(r)) + .unwrap_or_else(|| { + package + .edition + .as_ref() + .and_then(|e| e.as_value()) + .map(|e| Edition::from_str(e)) + .unwrap_or(Ok(Edition::Edition2015)) + .map(|e| e.default_resolve_behavior()) + })?; + if ws.resolve_behavior() != current_resolver { + // This ensures the published crate if built as a root (e.g. `cargo install`) will + // use the same resolver behavior it was tested with in the workspace. + // To avoid forcing a higher MSRV we don't explicitly set this if it would implicitly + // result in the same thing. + package.resolver = Some(ws.resolve_behavior().to_manifest()); + } + if let Some(license_file) = &package.license_file { + let license_file = license_file + .as_value() + .context("license file should have been resolved before `prepare_for_publish()`")?; + let license_path = Path::new(&license_file); + let abs_license_path = paths::normalize_path(&package_root.join(license_path)); + if abs_license_path.strip_prefix(package_root).is_err() { + // This path points outside of the package root. `cargo package` + // will copy it into the root, so adjust the path to this location. + package.license_file = Some(schema::InheritableField::Value( + license_path + .file_name() + .unwrap() + .to_str() + .unwrap() + .to_string(), + )); } + } - if let Some(readme) = &package.readme { - let readme = readme - .as_value() - .context("readme should have been resolved before `prepare_for_publish()`")?; - match readme { - schema::StringOrBool::String(readme) => { - let readme_path = Path::new(&readme); - let abs_readme_path = paths::normalize_path(&package_root.join(readme_path)); - if abs_readme_path.strip_prefix(package_root).is_err() { - // This path points outside of the package root. `cargo package` - // will copy it into the root, so adjust the path to this location. - package.readme = Some(schema::InheritableField::Value( - schema::StringOrBool::String( - readme_path - .file_name() - .unwrap() - .to_str() - .unwrap() - .to_string(), - ), - )); - } + if let Some(readme) = &package.readme { + let readme = readme + .as_value() + .context("readme should have been resolved before `prepare_for_publish()`")?; + match readme { + schema::StringOrBool::String(readme) => { + let readme_path = Path::new(&readme); + let abs_readme_path = paths::normalize_path(&package_root.join(readme_path)); + if abs_readme_path.strip_prefix(package_root).is_err() { + // This path points outside of the package root. `cargo package` + // will copy it into the root, so adjust the path to this location. + package.readme = Some(schema::InheritableField::Value( + schema::StringOrBool::String( + readme_path + .file_name() + .unwrap() + .to_str() + .unwrap() + .to_string(), + ), + )); } - schema::StringOrBool::Bool(_) => {} } + schema::StringOrBool::Bool(_) => {} } - let all = |_d: &schema::TomlDependency| true; - return Ok(schema::TomlManifest { - package: Some(package), - project: None, - profile: self.profile.clone(), - lib: self.lib.clone(), - bin: self.bin.clone(), - example: self.example.clone(), - test: self.test.clone(), - bench: self.bench.clone(), - dependencies: map_deps(config, self.dependencies.as_ref(), all)?, - dev_dependencies: map_deps( - config, - self.dev_dependencies(), - schema::TomlDependency::is_version_specified, - )?, - dev_dependencies2: None, - build_dependencies: map_deps(config, self.build_dependencies(), all)?, - build_dependencies2: None, - features: self.features.clone(), - target: match self.target.as_ref().map(|target_map| { - target_map - .iter() - .map(|(k, v)| { - Ok(( - k.clone(), - schema::TomlPlatform { - dependencies: map_deps(config, v.dependencies.as_ref(), all)?, - dev_dependencies: map_deps( - config, - v.dev_dependencies(), - schema::TomlDependency::is_version_specified, - )?, - dev_dependencies2: None, - build_dependencies: map_deps(config, v.build_dependencies(), all)?, - build_dependencies2: None, - }, - )) - }) - .collect() - }) { - Some(Ok(v)) => Some(v), - Some(Err(e)) => return Err(e), - None => None, - }, - replace: None, - patch: None, - workspace: None, - badges: self.badges.clone(), - cargo_features: self.cargo_features.clone(), - lints: self.lints.clone(), - }); - - fn map_deps( - config: &Config, - deps: Option<&BTreeMap>, - filter: impl Fn(&schema::TomlDependency) -> bool, - ) -> CargoResult>> { - let Some(deps) = deps else { return Ok(None) }; - let deps = deps + } + let all = |_d: &schema::TomlDependency| true; + return Ok(schema::TomlManifest { + package: Some(package), + project: None, + profile: me.profile.clone(), + lib: me.lib.clone(), + bin: me.bin.clone(), + example: me.example.clone(), + test: me.test.clone(), + bench: me.bench.clone(), + dependencies: map_deps(config, me.dependencies.as_ref(), all)?, + dev_dependencies: map_deps( + config, + me.dev_dependencies(), + schema::TomlDependency::is_version_specified, + )?, + dev_dependencies2: None, + build_dependencies: map_deps(config, me.build_dependencies(), all)?, + build_dependencies2: None, + features: me.features.clone(), + target: match me.target.as_ref().map(|target_map| { + target_map .iter() - .filter(|(_k, v)| { - if let schema::InheritableDependency::Value(def) = v { - filter(def) - } else { - false - } + .map(|(k, v)| { + Ok(( + k.clone(), + schema::TomlPlatform { + dependencies: map_deps(config, v.dependencies.as_ref(), all)?, + dev_dependencies: map_deps( + config, + v.dev_dependencies(), + schema::TomlDependency::is_version_specified, + )?, + dev_dependencies2: None, + build_dependencies: map_deps(config, v.build_dependencies(), all)?, + build_dependencies2: None, + }, + )) }) - .map(|(k, v)| Ok((k.clone(), map_dependency(config, v)?))) - .collect::>>()?; - Ok(Some(deps)) - } - - fn map_dependency( - config: &Config, - dep: &schema::InheritableDependency, - ) -> CargoResult { - let dep = match dep { - schema::InheritableDependency::Value(schema::TomlDependency::Detailed(d)) => { - let mut d = d.clone(); - // Path dependencies become crates.io deps. - d.path.take(); - // Same with git dependencies. - d.git.take(); - d.branch.take(); - d.tag.take(); - d.rev.take(); - // registry specifications are elaborated to the index URL - if let Some(registry) = d.registry.take() { - d.registry_index = Some(config.get_registry_index(®istry)?.to_string()); - } - Ok(d) - } - schema::InheritableDependency::Value(schema::TomlDependency::Simple(s)) => { - Ok(schema::TomlDetailedDependency { - version: Some(s.clone()), - ..Default::default() - }) + .collect() + }) { + Some(Ok(v)) => Some(v), + Some(Err(e)) => return Err(e), + None => None, + }, + replace: None, + patch: None, + workspace: None, + badges: me.badges.clone(), + cargo_features: me.cargo_features.clone(), + lints: me.lints.clone(), + }); + + fn map_deps( + config: &Config, + deps: Option<&BTreeMap>, + filter: impl Fn(&schema::TomlDependency) -> bool, + ) -> CargoResult>> { + let Some(deps) = deps else { return Ok(None) }; + let deps = deps + .iter() + .filter(|(_k, v)| { + if let schema::InheritableDependency::Value(def) = v { + filter(def) + } else { + false } - _ => unreachable!(), - }; - dep.map(schema::TomlDependency::Detailed) - .map(schema::InheritableDependency::Value) - } + }) + .map(|(k, v)| Ok((k.clone(), map_dependency(config, v)?))) + .collect::>>()?; + Ok(Some(deps)) } - pub fn to_real_manifest( - me: schema::TomlManifest, - embedded: bool, - source_id: SourceId, - package_root: &Path, + fn map_dependency( config: &Config, - ) -> CargoResult<(Manifest, Vec)> { - fn get_ws( - config: &Config, - resolved_path: &Path, - workspace_config: &WorkspaceConfig, - ) -> CargoResult { - match workspace_config { - WorkspaceConfig::Root(root) => Ok(root.inheritable().clone()), - WorkspaceConfig::Member { - root: Some(ref path_to_root), - } => { - let path = resolved_path - .parent() - .unwrap() - .join(path_to_root) - .join("Cargo.toml"); - let root_path = paths::normalize_path(&path); - inheritable_from_path(config, root_path) + dep: &schema::InheritableDependency, + ) -> CargoResult { + let dep = match dep { + schema::InheritableDependency::Value(schema::TomlDependency::Detailed(d)) => { + let mut d = d.clone(); + // Path dependencies become crates.io deps. + d.path.take(); + // Same with git dependencies. + d.git.take(); + d.branch.take(); + d.tag.take(); + d.rev.take(); + // registry specifications are elaborated to the index URL + if let Some(registry) = d.registry.take() { + d.registry_index = Some(config.get_registry_index(®istry)?.to_string()); } - WorkspaceConfig::Member { root: None } => { - match find_workspace_root(&resolved_path, config)? { - Some(path_to_root) => inheritable_from_path(config, path_to_root), - None => Err(anyhow!("failed to find a workspace root")), - } + Ok(d) + } + schema::InheritableDependency::Value(schema::TomlDependency::Simple(s)) => { + Ok(schema::TomlDetailedDependency { + version: Some(s.clone()), + ..Default::default() + }) + } + _ => unreachable!(), + }; + dep.map(schema::TomlDependency::Detailed) + .map(schema::InheritableDependency::Value) + } +} + +pub fn to_real_manifest( + me: schema::TomlManifest, + embedded: bool, + source_id: SourceId, + package_root: &Path, + config: &Config, +) -> CargoResult<(Manifest, Vec)> { + fn get_ws( + config: &Config, + resolved_path: &Path, + workspace_config: &WorkspaceConfig, + ) -> CargoResult { + match workspace_config { + WorkspaceConfig::Root(root) => Ok(root.inheritable().clone()), + WorkspaceConfig::Member { + root: Some(ref path_to_root), + } => { + let path = resolved_path + .parent() + .unwrap() + .join(path_to_root) + .join("Cargo.toml"); + let root_path = paths::normalize_path(&path); + inheritable_from_path(config, root_path) + } + WorkspaceConfig::Member { root: None } => { + match find_workspace_root(&resolved_path, config)? { + Some(path_to_root) => inheritable_from_path(config, path_to_root), + None => Err(anyhow!("failed to find a workspace root")), } } } + } - if !package_root.is_dir() { - bail!( - "package root '{}' is not a directory", - package_root.display() - ); - }; + if !package_root.is_dir() { + bail!( + "package root '{}' is not a directory", + package_root.display() + ); + }; - let mut nested_paths = vec![]; - let mut warnings = vec![]; - let mut errors = vec![]; + let mut nested_paths = vec![]; + let mut warnings = vec![]; + let mut errors = vec![]; - // Parse features first so they will be available when parsing other parts of the TOML. - let empty = Vec::new(); - let cargo_features = me.cargo_features.as_ref().unwrap_or(&empty); - let features = Features::new(cargo_features, config, &mut warnings, source_id.is_path())?; + // Parse features first so they will be available when parsing other parts of the TOML. + let empty = Vec::new(); + let cargo_features = me.cargo_features.as_ref().unwrap_or(&empty); + let features = Features::new(cargo_features, config, &mut warnings, source_id.is_path())?; - let mut package = match (&me.package, &me.project) { - (Some(_), Some(project)) => { - if source_id.is_path() { - config.shell().warn(format!( - "manifest at `{}` contains both `project` and `package`, \ + let mut package = match (&me.package, &me.project) { + (Some(_), Some(project)) => { + if source_id.is_path() { + config.shell().warn(format!( + "manifest at `{}` contains both `project` and `package`, \ this could become a hard error in the future", - package_root.display() - ))?; - } - project.clone() + package_root.display() + ))?; } - (Some(package), None) => package.clone(), - (None, Some(project)) => { - if source_id.is_path() { - config.shell().warn(format!( - "manifest at `{}` contains `[project]` instead of `[package]`, \ + project.clone() + } + (Some(package), None) => package.clone(), + (None, Some(project)) => { + if source_id.is_path() { + config.shell().warn(format!( + "manifest at `{}` contains `[project]` instead of `[package]`, \ this could become a hard error in the future", - package_root.display() - ))?; - } - project.clone() + package_root.display() + ))?; } - (None, None) => bail!("no `package` section found"), - }; + project.clone() + } + (None, None) => bail!("no `package` section found"), + }; - let workspace_config = match (me.workspace.as_ref(), package.workspace.as_ref()) { - (Some(toml_config), None) => { - let lints = toml_config.lints.clone(); - let lints = verify_lints(lints)?; - let inheritable = InheritableFields { - package: toml_config.package.clone(), - dependencies: toml_config.dependencies.clone(), - lints, - _ws_root: package_root.to_path_buf(), - }; - if let Some(ws_deps) = &inheritable.dependencies { - for (name, dep) in ws_deps { - unused_dep_keys( - name, - "workspace.dependencies", - dep.unused_keys(), - &mut warnings, - ); - } + let workspace_config = match (me.workspace.as_ref(), package.workspace.as_ref()) { + (Some(toml_config), None) => { + let lints = toml_config.lints.clone(); + let lints = verify_lints(lints)?; + let inheritable = InheritableFields { + package: toml_config.package.clone(), + dependencies: toml_config.dependencies.clone(), + lints, + _ws_root: package_root.to_path_buf(), + }; + if let Some(ws_deps) = &inheritable.dependencies { + for (name, dep) in ws_deps { + unused_dep_keys( + name, + "workspace.dependencies", + dep.unused_keys(), + &mut warnings, + ); } - let ws_root_config = WorkspaceRootConfig::new( - package_root, - &toml_config.members, - &toml_config.default_members, - &toml_config.exclude, - &Some(inheritable), - &toml_config.metadata, - ); - config - .ws_roots - .borrow_mut() - .insert(package_root.to_path_buf(), ws_root_config.clone()); - WorkspaceConfig::Root(ws_root_config) } - (None, root) => WorkspaceConfig::Member { - root: root.cloned(), - }, - (Some(..), Some(..)) => bail!( - "cannot configure both `package.workspace` and \ + let ws_root_config = WorkspaceRootConfig::new( + package_root, + &toml_config.members, + &toml_config.default_members, + &toml_config.exclude, + &Some(inheritable), + &toml_config.metadata, + ); + config + .ws_roots + .borrow_mut() + .insert(package_root.to_path_buf(), ws_root_config.clone()); + WorkspaceConfig::Root(ws_root_config) + } + (None, root) => WorkspaceConfig::Member { + root: root.cloned(), + }, + (Some(..), Some(..)) => bail!( + "cannot configure both `package.workspace` and \ `[workspace]`, only one can be specified" - ), - }; + ), + }; - let package_name = package.name.trim(); - if package_name.is_empty() { - bail!("package name cannot be an empty string") - } + let package_name = package.name.trim(); + if package_name.is_empty() { + bail!("package name cannot be an empty string") + } - validate_package_name(package_name, "package name", "")?; + validate_package_name(package_name, "package name", "")?; - let resolved_path = package_root.join("Cargo.toml"); + let resolved_path = package_root.join("Cargo.toml"); - let inherit_cell: LazyCell = LazyCell::new(); - let inherit = - || inherit_cell.try_borrow_with(|| get_ws(config, &resolved_path, &workspace_config)); + let inherit_cell: LazyCell = LazyCell::new(); + let inherit = + || inherit_cell.try_borrow_with(|| get_ws(config, &resolved_path, &workspace_config)); - let version = package - .version - .clone() - .map(|version| field_inherit_with(version, "version", || inherit()?.version())) - .transpose()?; + let version = package + .version + .clone() + .map(|version| field_inherit_with(version, "version", || inherit()?.version())) + .transpose()?; - package.version = version.clone().map(schema::InheritableField::Value); + package.version = version.clone().map(schema::InheritableField::Value); - let pkgid = PackageId::pure( - package.name.as_str().into(), - version - .clone() - .unwrap_or_else(|| semver::Version::new(0, 0, 0)), - source_id, - ); + let pkgid = PackageId::pure( + package.name.as_str().into(), + version + .clone() + .unwrap_or_else(|| semver::Version::new(0, 0, 0)), + source_id, + ); - let edition = if let Some(edition) = package.edition.clone() { - let edition: Edition = field_inherit_with(edition, "edition", || inherit()?.edition())? - .parse() - .with_context(|| "failed to parse the `edition` key")?; - package.edition = Some(schema::InheritableField::Value(edition.to_string())); + let edition = if let Some(edition) = package.edition.clone() { + let edition: Edition = field_inherit_with(edition, "edition", || inherit()?.edition())? + .parse() + .with_context(|| "failed to parse the `edition` key")?; + package.edition = Some(schema::InheritableField::Value(edition.to_string())); + edition + } else { + Edition::Edition2015 + }; + // Add these lines if start a new unstable edition. + // ``` + // if edition == Edition::Edition20xx { + // features.require(Feature::edition20xx())?; + // } + // ``` + if edition == Edition::Edition2024 { + features.require(Feature::edition2024())?; + } else if !edition.is_stable() { + // Guard in case someone forgets to add .require() + return Err(util::errors::internal(format!( + "edition {} should be gated", edition - } else { - Edition::Edition2015 - }; - // Add these lines if start a new unstable edition. - // ``` - // if edition == Edition::Edition20xx { - // features.require(Feature::edition20xx())?; - // } - // ``` - if edition == Edition::Edition2024 { - features.require(Feature::edition2024())?; - } else if !edition.is_stable() { - // Guard in case someone forgets to add .require() - return Err(util::errors::internal(format!( - "edition {} should be gated", - edition - ))); - } + ))); + } - let rust_version = if let Some(rust_version) = &package.rust_version { - let rust_version = field_inherit_with(rust_version.clone(), "rust_version", || { - inherit()?.rust_version() - })?; - let req = rust_version.to_caret_req(); - if let Some(first_version) = edition.first_version() { - let unsupported = - semver::Version::new(first_version.major, first_version.minor - 1, 9999); - if req.matches(&unsupported) { - bail!( - "rust-version {} is older than first version ({}) required by \ + let rust_version = if let Some(rust_version) = &package.rust_version { + let rust_version = field_inherit_with(rust_version.clone(), "rust_version", || { + inherit()?.rust_version() + })?; + let req = rust_version.to_caret_req(); + if let Some(first_version) = edition.first_version() { + let unsupported = + semver::Version::new(first_version.major, first_version.minor - 1, 9999); + if req.matches(&unsupported) { + bail!( + "rust-version {} is older than first version ({}) required by \ the specified edition ({})", - rust_version, - first_version, - edition, - ) - } + rust_version, + first_version, + edition, + ) } - Some(rust_version) - } else { - None - }; - - if package.metabuild.is_some() { - features.require(Feature::metabuild())?; } + Some(rust_version) + } else { + None + }; - let resolve_behavior = match ( - package.resolver.as_ref(), - me.workspace.as_ref().and_then(|ws| ws.resolver.as_ref()), - ) { - (None, None) => None, - (Some(s), None) | (None, Some(s)) => Some(ResolveBehavior::from_manifest(s)?), - (Some(_), Some(_)) => { - bail!("cannot specify `resolver` field in both `[workspace]` and `[package]`") - } - }; - - // If we have no lib at all, use the inferred lib, if available. - // If we have a lib with a path, we're done. - // If we have a lib with no path, use the inferred lib or else the package name. - let targets = targets( - &features, - &me, - package_name, - package_root, - edition, - &package.build, - &package.metabuild, - &mut warnings, - &mut errors, - )?; + if package.metabuild.is_some() { + features.require(Feature::metabuild())?; + } - if targets.is_empty() { - debug!("manifest has no build targets"); + let resolve_behavior = match ( + package.resolver.as_ref(), + me.workspace.as_ref().and_then(|ws| ws.resolver.as_ref()), + ) { + (None, None) => None, + (Some(s), None) | (None, Some(s)) => Some(ResolveBehavior::from_manifest(s)?), + (Some(_), Some(_)) => { + bail!("cannot specify `resolver` field in both `[workspace]` and `[package]`") } + }; - if let Err(conflict_targets) = unique_build_targets(&targets, package_root) { - conflict_targets - .iter() - .for_each(|(target_path, conflicts)| { - warnings.push(format!( - "file `{}` found to be present in multiple \ + // If we have no lib at all, use the inferred lib, if available. + // If we have a lib with a path, we're done. + // If we have a lib with no path, use the inferred lib or else the package name. + let targets = targets( + &features, + &me, + package_name, + package_root, + edition, + &package.build, + &package.metabuild, + &mut warnings, + &mut errors, + )?; + + if targets.is_empty() { + debug!("manifest has no build targets"); + } + + if let Err(conflict_targets) = unique_build_targets(&targets, package_root) { + conflict_targets + .iter() + .for_each(|(target_path, conflicts)| { + warnings.push(format!( + "file `{}` found to be present in multiple \ build targets:\n{}", - target_path.display().to_string(), - conflicts - .iter() - .map(|t| format!( - " * `{}` target `{}`", - t.kind().description(), - t.name(), - )) - .join("\n") - )); - }) - } + target_path.display().to_string(), + conflicts + .iter() + .map(|t| format!(" * `{}` target `{}`", t.kind().description(), t.name(),)) + .join("\n") + )); + }) + } - if let Some(links) = &package.links { - if !targets.iter().any(|t| t.is_custom_build()) { - bail!( - "package `{}` specifies that it links to `{}` but does not \ + if let Some(links) = &package.links { + if !targets.iter().any(|t| t.is_custom_build()) { + bail!( + "package `{}` specifies that it links to `{}` but does not \ have a custom build script", - pkgid, - links - ) - } + pkgid, + links + ) } + } - let mut deps = Vec::new(); + let mut deps = Vec::new(); + + let mut cx = Context { + deps: &mut deps, + source_id, + nested_paths: &mut nested_paths, + config, + warnings: &mut warnings, + features: &features, + platform: None, + root: package_root, + }; - let mut cx = Context { - deps: &mut deps, - source_id, - nested_paths: &mut nested_paths, - config, - warnings: &mut warnings, - features: &features, - platform: None, - root: package_root, + fn process_dependencies( + cx: &mut Context<'_, '_>, + new_deps: Option<&BTreeMap>, + kind: Option, + workspace_config: &WorkspaceConfig, + inherit_cell: &LazyCell, + ) -> CargoResult>> { + let Some(dependencies) = new_deps else { + return Ok(None); }; - fn process_dependencies( - cx: &mut Context<'_, '_>, - new_deps: Option<&BTreeMap>, - kind: Option, - workspace_config: &WorkspaceConfig, - inherit_cell: &LazyCell, - ) -> CargoResult>> { - let Some(dependencies) = new_deps else { - return Ok(None); - }; + let inheritable = || { + inherit_cell.try_borrow_with(|| { + get_ws(cx.config, &cx.root.join("Cargo.toml"), &workspace_config) + }) + }; - let inheritable = || { - inherit_cell.try_borrow_with(|| { - get_ws(cx.config, &cx.root.join("Cargo.toml"), &workspace_config) - }) + let mut deps: BTreeMap = BTreeMap::new(); + for (n, v) in dependencies.iter() { + let resolved = dependency_inherit_with(v.clone(), n, inheritable, cx)?; + let dep = dep_to_dependency(&resolved, n, cx, kind)?; + let name_in_toml = dep.name_in_toml().as_str(); + validate_package_name(name_in_toml, "dependency name", "")?; + let kind_name = match kind { + Some(k) => k.kind_table(), + None => "dependencies", }; - - let mut deps: BTreeMap = BTreeMap::new(); - for (n, v) in dependencies.iter() { - let resolved = dependency_inherit_with(v.clone(), n, inheritable, cx)?; - let dep = dep_to_dependency(&resolved, n, cx, kind)?; - let name_in_toml = dep.name_in_toml().as_str(); - validate_package_name(name_in_toml, "dependency name", "")?; - let kind_name = match kind { - Some(k) => k.kind_table(), - None => "dependencies", - }; - let table_in_toml = if let Some(platform) = &cx.platform { - format!("target.{}.{kind_name}", platform.to_string()) - } else { - kind_name.to_string() - }; - unused_dep_keys(name_in_toml, &table_in_toml, v.unused_keys(), cx.warnings); - cx.deps.push(dep); - deps.insert( - n.to_string(), - schema::InheritableDependency::Value(resolved.clone()), - ); - } - Ok(Some(deps)) + let table_in_toml = if let Some(platform) = &cx.platform { + format!("target.{}.{kind_name}", platform.to_string()) + } else { + kind_name.to_string() + }; + unused_dep_keys(name_in_toml, &table_in_toml, v.unused_keys(), cx.warnings); + cx.deps.push(dep); + deps.insert( + n.to_string(), + schema::InheritableDependency::Value(resolved.clone()), + ); } + Ok(Some(deps)) + } - // Collect the dependencies. - let dependencies = process_dependencies( + // Collect the dependencies. + let dependencies = process_dependencies( + &mut cx, + me.dependencies.as_ref(), + None, + &workspace_config, + &inherit_cell, + )?; + if me.dev_dependencies.is_some() && me.dev_dependencies2.is_some() { + warn_on_deprecated("dev-dependencies", package_name, "package", cx.warnings); + } + let dev_deps = me.dev_dependencies(); + let dev_deps = process_dependencies( + &mut cx, + dev_deps, + Some(DepKind::Development), + &workspace_config, + &inherit_cell, + )?; + if me.build_dependencies.is_some() && me.build_dependencies2.is_some() { + warn_on_deprecated("build-dependencies", package_name, "package", cx.warnings); + } + let build_deps = me.build_dependencies(); + let build_deps = process_dependencies( + &mut cx, + build_deps, + Some(DepKind::Build), + &workspace_config, + &inherit_cell, + )?; + + let lints = me + .lints + .clone() + .map(|mw| lints_inherit_with(mw, || inherit()?.lints())) + .transpose()?; + let lints = verify_lints(lints)?; + let default = schema::TomlLints::default(); + let rustflags = lints_to_rustflags(lints.as_ref().unwrap_or(&default)); + + let mut target: BTreeMap = BTreeMap::new(); + for (name, platform) in me.target.iter().flatten() { + cx.platform = { + let platform: Platform = name.parse()?; + platform.check_cfg_attributes(cx.warnings); + Some(platform) + }; + let deps = process_dependencies( &mut cx, - me.dependencies.as_ref(), + platform.dependencies.as_ref(), None, &workspace_config, &inherit_cell, )?; - if me.dev_dependencies.is_some() && me.dev_dependencies2.is_some() { - warn_on_deprecated("dev-dependencies", package_name, "package", cx.warnings); + if platform.build_dependencies.is_some() && platform.build_dependencies2.is_some() { + warn_on_deprecated("build-dependencies", name, "platform target", cx.warnings); } - let dev_deps = me.dev_dependencies(); - let dev_deps = process_dependencies( + let build_deps = platform.build_dependencies(); + let build_deps = process_dependencies( &mut cx, - dev_deps, - Some(DepKind::Development), + build_deps, + Some(DepKind::Build), &workspace_config, &inherit_cell, )?; - if me.build_dependencies.is_some() && me.build_dependencies2.is_some() { - warn_on_deprecated("build-dependencies", package_name, "package", cx.warnings); + if platform.dev_dependencies.is_some() && platform.dev_dependencies2.is_some() { + warn_on_deprecated("dev-dependencies", name, "platform target", cx.warnings); } - let build_deps = me.build_dependencies(); - let build_deps = process_dependencies( + let dev_deps = platform.dev_dependencies(); + let dev_deps = process_dependencies( &mut cx, - build_deps, - Some(DepKind::Build), + dev_deps, + Some(DepKind::Development), &workspace_config, &inherit_cell, )?; + target.insert( + name.clone(), + schema::TomlPlatform { + dependencies: deps, + build_dependencies: build_deps, + build_dependencies2: None, + dev_dependencies: dev_deps, + dev_dependencies2: None, + }, + ); + } - let lints = me - .lints - .clone() - .map(|mw| lints_inherit_with(mw, || inherit()?.lints())) - .transpose()?; - let lints = verify_lints(lints)?; - let default = schema::TomlLints::default(); - let rustflags = lints_to_rustflags(lints.as_ref().unwrap_or(&default)); - - let mut target: BTreeMap = BTreeMap::new(); - for (name, platform) in me.target.iter().flatten() { - cx.platform = { - let platform: Platform = name.parse()?; - platform.check_cfg_attributes(cx.warnings); - Some(platform) - }; - let deps = process_dependencies( - &mut cx, - platform.dependencies.as_ref(), - None, - &workspace_config, - &inherit_cell, - )?; - if platform.build_dependencies.is_some() && platform.build_dependencies2.is_some() { - warn_on_deprecated("build-dependencies", name, "platform target", cx.warnings); - } - let build_deps = platform.build_dependencies(); - let build_deps = process_dependencies( - &mut cx, - build_deps, - Some(DepKind::Build), - &workspace_config, - &inherit_cell, - )?; - if platform.dev_dependencies.is_some() && platform.dev_dependencies2.is_some() { - warn_on_deprecated("dev-dependencies", name, "platform target", cx.warnings); - } - let dev_deps = platform.dev_dependencies(); - let dev_deps = process_dependencies( - &mut cx, - dev_deps, - Some(DepKind::Development), - &workspace_config, - &inherit_cell, - )?; - target.insert( - name.clone(), - schema::TomlPlatform { - dependencies: deps, - build_dependencies: build_deps, - build_dependencies2: None, - dev_dependencies: dev_deps, - dev_dependencies2: None, - }, - ); - } - - let target = if target.is_empty() { - None - } else { - Some(target) - }; - let replace = me.replace(&mut cx)?; - let patch = me.patch(&mut cx)?; + let target = if target.is_empty() { + None + } else { + Some(target) + }; + let replace = replace(&me, &mut cx)?; + let patch = patch(&me, &mut cx)?; - { - let mut names_sources = BTreeMap::new(); - for dep in &deps { - let name = dep.name_in_toml(); - let prev = names_sources.insert(name, dep.source_id()); - if prev.is_some() && prev != Some(dep.source_id()) { - bail!( - "Dependency '{}' has different source paths depending on the build \ + { + let mut names_sources = BTreeMap::new(); + for dep in &deps { + let name = dep.name_in_toml(); + let prev = names_sources.insert(name, dep.source_id()); + if prev.is_some() && prev != Some(dep.source_id()) { + bail!( + "Dependency '{}' has different source paths depending on the build \ target. Each dependency must have a single canonical source path \ irrespective of build target.", - name - ); - } + name + ); } } + } - let exclude = package - .exclude - .clone() - .map(|mw| field_inherit_with(mw, "exclude", || inherit()?.exclude())) - .transpose()? - .unwrap_or_default(); - let include = package - .include - .clone() - .map(|mw| field_inherit_with(mw, "include", || inherit()?.include())) - .transpose()? - .unwrap_or_default(); - let empty_features = BTreeMap::new(); - - let summary = Summary::new( - pkgid, - deps, - &me.features - .as_ref() - .unwrap_or(&empty_features) - .iter() - .map(|(k, v)| { - ( - InternedString::new(k), - v.iter().map(InternedString::from).collect(), - ) - }) - .collect(), - package.links.as_deref(), - rust_version.clone(), - )?; + let exclude = package + .exclude + .clone() + .map(|mw| field_inherit_with(mw, "exclude", || inherit()?.exclude())) + .transpose()? + .unwrap_or_default(); + let include = package + .include + .clone() + .map(|mw| field_inherit_with(mw, "include", || inherit()?.include())) + .transpose()? + .unwrap_or_default(); + let empty_features = BTreeMap::new(); + + let summary = Summary::new( + pkgid, + deps, + &me.features + .as_ref() + .unwrap_or(&empty_features) + .iter() + .map(|(k, v)| { + ( + InternedString::new(k), + v.iter().map(InternedString::from).collect(), + ) + }) + .collect(), + package.links.as_deref(), + rust_version.clone(), + )?; - let metadata = ManifestMetadata { - description: package - .description - .clone() - .map(|mw| field_inherit_with(mw, "description", || inherit()?.description())) - .transpose()?, - homepage: package - .homepage - .clone() - .map(|mw| field_inherit_with(mw, "homepage", || inherit()?.homepage())) - .transpose()?, - documentation: package - .documentation - .clone() - .map(|mw| field_inherit_with(mw, "documentation", || inherit()?.documentation())) - .transpose()?, - readme: readme_for_package( - package_root, - package - .readme - .clone() - .map(|mw| field_inherit_with(mw, "readme", || inherit()?.readme(package_root))) - .transpose()? - .as_ref(), - ), - authors: package - .authors - .clone() - .map(|mw| field_inherit_with(mw, "authors", || inherit()?.authors())) - .transpose()? - .unwrap_or_default(), - license: package - .license - .clone() - .map(|mw| field_inherit_with(mw, "license", || inherit()?.license())) - .transpose()?, - license_file: package - .license_file - .clone() - .map(|mw| { - field_inherit_with(mw, "license", || inherit()?.license_file(package_root)) - }) - .transpose()?, - repository: package - .repository - .clone() - .map(|mw| field_inherit_with(mw, "repository", || inherit()?.repository())) - .transpose()?, - keywords: package - .keywords - .clone() - .map(|mw| field_inherit_with(mw, "keywords", || inherit()?.keywords())) - .transpose()? - .unwrap_or_default(), - categories: package - .categories - .clone() - .map(|mw| field_inherit_with(mw, "categories", || inherit()?.categories())) - .transpose()? - .unwrap_or_default(), - badges: me - .badges - .clone() - .map(|mw| field_inherit_with(mw, "badges", || inherit()?.badges())) - .transpose()? - .unwrap_or_default(), - links: package.links.clone(), - rust_version: package - .rust_version - .map(|mw| field_inherit_with(mw, "rust-version", || inherit()?.rust_version())) - .transpose()?, - }; - package.description = metadata + let metadata = ManifestMetadata { + description: package .description .clone() - .map(|description| schema::InheritableField::Value(description)); - package.homepage = metadata + .map(|mw| field_inherit_with(mw, "description", || inherit()?.description())) + .transpose()?, + homepage: package .homepage .clone() - .map(|homepage| schema::InheritableField::Value(homepage)); - package.documentation = metadata + .map(|mw| field_inherit_with(mw, "homepage", || inherit()?.homepage())) + .transpose()?, + documentation: package .documentation .clone() - .map(|documentation| schema::InheritableField::Value(documentation)); - package.readme = metadata - .readme - .clone() - .map(|readme| schema::InheritableField::Value(schema::StringOrBool::String(readme))); - package.authors = package + .map(|mw| field_inherit_with(mw, "documentation", || inherit()?.documentation())) + .transpose()?, + readme: readme_for_package( + package_root, + package + .readme + .clone() + .map(|mw| field_inherit_with(mw, "readme", || inherit()?.readme(package_root))) + .transpose()? + .as_ref(), + ), + authors: package .authors - .as_ref() - .map(|_| schema::InheritableField::Value(metadata.authors.clone())); - package.license = metadata + .clone() + .map(|mw| field_inherit_with(mw, "authors", || inherit()?.authors())) + .transpose()? + .unwrap_or_default(), + license: package .license .clone() - .map(|license| schema::InheritableField::Value(license)); - package.license_file = metadata + .map(|mw| field_inherit_with(mw, "license", || inherit()?.license())) + .transpose()?, + license_file: package .license_file .clone() - .map(|license_file| schema::InheritableField::Value(license_file)); - package.repository = metadata + .map(|mw| field_inherit_with(mw, "license", || inherit()?.license_file(package_root))) + .transpose()?, + repository: package .repository .clone() - .map(|repository| schema::InheritableField::Value(repository)); - package.keywords = package + .map(|mw| field_inherit_with(mw, "repository", || inherit()?.repository())) + .transpose()?, + keywords: package .keywords - .as_ref() - .map(|_| schema::InheritableField::Value(metadata.keywords.clone())); - package.categories = package + .clone() + .map(|mw| field_inherit_with(mw, "keywords", || inherit()?.keywords())) + .transpose()? + .unwrap_or_default(), + categories: package .categories - .as_ref() - .map(|_| schema::InheritableField::Value(metadata.categories.clone())); - package.rust_version = rust_version .clone() - .map(|rv| schema::InheritableField::Value(rv)); - package.exclude = package - .exclude - .as_ref() - .map(|_| schema::InheritableField::Value(exclude.clone())); - package.include = package - .include - .as_ref() - .map(|_| schema::InheritableField::Value(include.clone())); + .map(|mw| field_inherit_with(mw, "categories", || inherit()?.categories())) + .transpose()? + .unwrap_or_default(), + badges: me + .badges + .clone() + .map(|mw| field_inherit_with(mw, "badges", || inherit()?.badges())) + .transpose()? + .unwrap_or_default(), + links: package.links.clone(), + rust_version: package + .rust_version + .map(|mw| field_inherit_with(mw, "rust-version", || inherit()?.rust_version())) + .transpose()?, + }; + package.description = metadata + .description + .clone() + .map(|description| schema::InheritableField::Value(description)); + package.homepage = metadata + .homepage + .clone() + .map(|homepage| schema::InheritableField::Value(homepage)); + package.documentation = metadata + .documentation + .clone() + .map(|documentation| schema::InheritableField::Value(documentation)); + package.readme = metadata + .readme + .clone() + .map(|readme| schema::InheritableField::Value(schema::StringOrBool::String(readme))); + package.authors = package + .authors + .as_ref() + .map(|_| schema::InheritableField::Value(metadata.authors.clone())); + package.license = metadata + .license + .clone() + .map(|license| schema::InheritableField::Value(license)); + package.license_file = metadata + .license_file + .clone() + .map(|license_file| schema::InheritableField::Value(license_file)); + package.repository = metadata + .repository + .clone() + .map(|repository| schema::InheritableField::Value(repository)); + package.keywords = package + .keywords + .as_ref() + .map(|_| schema::InheritableField::Value(metadata.keywords.clone())); + package.categories = package + .categories + .as_ref() + .map(|_| schema::InheritableField::Value(metadata.categories.clone())); + package.rust_version = rust_version + .clone() + .map(|rv| schema::InheritableField::Value(rv)); + package.exclude = package + .exclude + .as_ref() + .map(|_| schema::InheritableField::Value(exclude.clone())); + package.include = package + .include + .as_ref() + .map(|_| schema::InheritableField::Value(include.clone())); - let profiles = me.profile.clone(); - if let Some(profiles) = &profiles { - let cli_unstable = config.cli_unstable(); - validate_profiles(profiles, cli_unstable, &features, &mut warnings)?; - } + let profiles = me.profile.clone(); + if let Some(profiles) = &profiles { + let cli_unstable = config.cli_unstable(); + validate_profiles(profiles, cli_unstable, &features, &mut warnings)?; + } - let publish = package.publish.clone().map(|publish| { - field_inherit_with(publish, "publish", || inherit()?.publish()).unwrap() - }); + let publish = package + .publish + .clone() + .map(|publish| field_inherit_with(publish, "publish", || inherit()?.publish()).unwrap()); - package.publish = publish.clone().map(|p| schema::InheritableField::Value(p)); + package.publish = publish.clone().map(|p| schema::InheritableField::Value(p)); - let publish = match publish { - Some(schema::VecStringOrBool::VecString(ref vecstring)) => Some(vecstring.clone()), - Some(schema::VecStringOrBool::Bool(false)) => Some(vec![]), - Some(schema::VecStringOrBool::Bool(true)) => None, - None => version.is_none().then_some(vec![]), - }; + let publish = match publish { + Some(schema::VecStringOrBool::VecString(ref vecstring)) => Some(vecstring.clone()), + Some(schema::VecStringOrBool::Bool(false)) => Some(vec![]), + Some(schema::VecStringOrBool::Bool(true)) => None, + None => version.is_none().then_some(vec![]), + }; - if version.is_none() && publish != Some(vec![]) { - bail!("`package.publish` requires `package.version` be specified"); - } + if version.is_none() && publish != Some(vec![]) { + bail!("`package.publish` requires `package.version` be specified"); + } - if summary.features().contains_key("default-features") { - warnings.push( - "`default-features = [\"..\"]` was found in [features]. \ + if summary.features().contains_key("default-features") { + warnings.push( + "`default-features = [\"..\"]` was found in [features]. \ Did you mean to use `default = [\"..\"]`?" - .to_string(), - ) - } + .to_string(), + ) + } - if let Some(run) = &package.default_run { - if !targets - .iter() - .filter(|t| t.is_bin()) - .any(|t| t.name() == run) - { - let suggestion = - util::closest_msg(run, targets.iter().filter(|t| t.is_bin()), |t| t.name()); - bail!("default-run target `{}` not found{}", run, suggestion); - } + if let Some(run) = &package.default_run { + if !targets + .iter() + .filter(|t| t.is_bin()) + .any(|t| t.name() == run) + { + let suggestion = + util::closest_msg(run, targets.iter().filter(|t| t.is_bin()), |t| t.name()); + bail!("default-run target `{}` not found{}", run, suggestion); } + } - let default_kind = package - .default_target - .as_ref() - .map(|t| CompileTarget::new(&*t)) - .transpose()? - .map(CompileKind::Target); - let forced_kind = package - .forced_target + let default_kind = package + .default_target + .as_ref() + .map(|t| CompileTarget::new(&*t)) + .transpose()? + .map(CompileKind::Target); + let forced_kind = package + .forced_target + .as_ref() + .map(|t| CompileTarget::new(&*t)) + .transpose()? + .map(CompileKind::Target); + let custom_metadata = package.metadata.clone(); + let resolved_toml = schema::TomlManifest { + cargo_features: me.cargo_features.clone(), + package: Some(package.clone()), + project: None, + profile: me.profile.clone(), + lib: me.lib.clone(), + bin: me.bin.clone(), + example: me.example.clone(), + test: me.test.clone(), + bench: me.bench.clone(), + dependencies, + dev_dependencies: dev_deps, + dev_dependencies2: None, + build_dependencies: build_deps, + build_dependencies2: None, + features: me.features.clone(), + target, + replace: me.replace.clone(), + patch: me.patch.clone(), + workspace: me.workspace.clone(), + badges: me + .badges .as_ref() - .map(|t| CompileTarget::new(&*t)) - .transpose()? - .map(CompileKind::Target); - let custom_metadata = package.metadata.clone(); - let resolved_toml = schema::TomlManifest { - cargo_features: me.cargo_features.clone(), - package: Some(package.clone()), - project: None, - profile: me.profile.clone(), - lib: me.lib.clone(), - bin: me.bin.clone(), - example: me.example.clone(), - test: me.test.clone(), - bench: me.bench.clone(), - dependencies, - dev_dependencies: dev_deps, - dev_dependencies2: None, - build_dependencies: build_deps, - build_dependencies2: None, - features: me.features.clone(), - target, - replace: me.replace.clone(), - patch: me.patch.clone(), - workspace: me.workspace.clone(), - badges: me - .badges - .as_ref() - .map(|_| schema::InheritableField::Value(metadata.badges.clone())), - lints: lints.map(|lints| schema::InheritableLints { - workspace: false, - lints, - }), - }; - let mut manifest = Manifest::new( - summary, - default_kind, - forced_kind, - targets, - exclude, - include, - package.links.clone(), - metadata, - custom_metadata, - profiles, - publish, - replace, - patch, - workspace_config, - features, - edition, - rust_version, - package.im_a_teapot, - package.default_run.clone(), - Rc::new(resolved_toml), - package.metabuild.clone().map(|sov| sov.0), - resolve_behavior, - rustflags, - embedded, - ); - if package.license_file.is_some() && package.license.is_some() { - manifest.warnings_mut().add_warning( + .map(|_| schema::InheritableField::Value(metadata.badges.clone())), + lints: lints.map(|lints| schema::InheritableLints { + workspace: false, + lints, + }), + }; + let mut manifest = Manifest::new( + summary, + default_kind, + forced_kind, + targets, + exclude, + include, + package.links.clone(), + metadata, + custom_metadata, + profiles, + publish, + replace, + patch, + workspace_config, + features, + edition, + rust_version, + package.im_a_teapot, + package.default_run.clone(), + Rc::new(resolved_toml), + package.metabuild.clone().map(|sov| sov.0), + resolve_behavior, + rustflags, + embedded, + ); + if package.license_file.is_some() && package.license.is_some() { + manifest.warnings_mut().add_warning( "only one of `license` or `license-file` is necessary\n\ `license` should be used if the package license can be expressed \ with a standard SPDX expression.\n\ @@ -1073,214 +1061,221 @@ impl schema::TomlManifest { for more information." .to_string(), ); - } - for warning in warnings { - manifest.warnings_mut().add_warning(warning); - } - for error in errors { - manifest.warnings_mut().add_critical_warning(error); - } + } + for warning in warnings { + manifest.warnings_mut().add_warning(warning); + } + for error in errors { + manifest.warnings_mut().add_critical_warning(error); + } + + manifest.feature_gate()?; - manifest.feature_gate()?; + Ok((manifest, nested_paths)) +} - Ok((manifest, nested_paths)) +fn to_virtual_manifest( + me: schema::TomlManifest, + source_id: SourceId, + root: &Path, + config: &Config, +) -> CargoResult<(VirtualManifest, Vec)> { + if me.project.is_some() { + bail!("this virtual manifest specifies a [project] section, which is not allowed"); + } + if me.package.is_some() { + bail!("this virtual manifest specifies a [package] section, which is not allowed"); + } + if me.lib.is_some() { + bail!("this virtual manifest specifies a [lib] section, which is not allowed"); + } + if me.bin.is_some() { + bail!("this virtual manifest specifies a [[bin]] section, which is not allowed"); + } + if me.example.is_some() { + bail!("this virtual manifest specifies a [[example]] section, which is not allowed"); + } + if me.test.is_some() { + bail!("this virtual manifest specifies a [[test]] section, which is not allowed"); + } + if me.bench.is_some() { + bail!("this virtual manifest specifies a [[bench]] section, which is not allowed"); + } + if me.dependencies.is_some() { + bail!("this virtual manifest specifies a [dependencies] section, which is not allowed"); + } + if me.dev_dependencies().is_some() { + bail!("this virtual manifest specifies a [dev-dependencies] section, which is not allowed"); + } + if me.build_dependencies().is_some() { + bail!( + "this virtual manifest specifies a [build-dependencies] section, which is not allowed" + ); + } + if me.features.is_some() { + bail!("this virtual manifest specifies a [features] section, which is not allowed"); + } + if me.target.is_some() { + bail!("this virtual manifest specifies a [target] section, which is not allowed"); + } + if me.badges.is_some() { + bail!("this virtual manifest specifies a [badges] section, which is not allowed"); } - fn to_virtual_manifest( - me: schema::TomlManifest, - source_id: SourceId, - root: &Path, - config: &Config, - ) -> CargoResult<(VirtualManifest, Vec)> { - if me.project.is_some() { - bail!("this virtual manifest specifies a [project] section, which is not allowed"); - } - if me.package.is_some() { - bail!("this virtual manifest specifies a [package] section, which is not allowed"); - } - if me.lib.is_some() { - bail!("this virtual manifest specifies a [lib] section, which is not allowed"); - } - if me.bin.is_some() { - bail!("this virtual manifest specifies a [[bin]] section, which is not allowed"); - } - if me.example.is_some() { - bail!("this virtual manifest specifies a [[example]] section, which is not allowed"); - } - if me.test.is_some() { - bail!("this virtual manifest specifies a [[test]] section, which is not allowed"); - } - if me.bench.is_some() { - bail!("this virtual manifest specifies a [[bench]] section, which is not allowed"); - } - if me.dependencies.is_some() { - bail!("this virtual manifest specifies a [dependencies] section, which is not allowed"); - } - if me.dev_dependencies().is_some() { - bail!("this virtual manifest specifies a [dev-dependencies] section, which is not allowed"); - } - if me.build_dependencies().is_some() { - bail!("this virtual manifest specifies a [build-dependencies] section, which is not allowed"); - } - if me.features.is_some() { - bail!("this virtual manifest specifies a [features] section, which is not allowed"); - } - if me.target.is_some() { - bail!("this virtual manifest specifies a [target] section, which is not allowed"); - } - if me.badges.is_some() { - bail!("this virtual manifest specifies a [badges] section, which is not allowed"); - } + let mut nested_paths = Vec::new(); + let mut warnings = Vec::new(); + let mut deps = Vec::new(); + let empty = Vec::new(); + let cargo_features = me.cargo_features.as_ref().unwrap_or(&empty); + let features = Features::new(cargo_features, config, &mut warnings, source_id.is_path())?; - let mut nested_paths = Vec::new(); - let mut warnings = Vec::new(); - let mut deps = Vec::new(); - let empty = Vec::new(); - let cargo_features = me.cargo_features.as_ref().unwrap_or(&empty); - let features = Features::new(cargo_features, config, &mut warnings, source_id.is_path())?; - - let (replace, patch) = { - let mut cx = Context { - deps: &mut deps, - source_id, - nested_paths: &mut nested_paths, - config, - warnings: &mut warnings, - platform: None, - features: &features, - root, - }; - (me.replace(&mut cx)?, me.patch(&mut cx)?) - }; - let profiles = me.profile.clone(); - if let Some(profiles) = &profiles { - validate_profiles(profiles, config.cli_unstable(), &features, &mut warnings)?; - } - let resolve_behavior = me - .workspace - .as_ref() - .and_then(|ws| ws.resolver.as_deref()) - .map(|r| ResolveBehavior::from_manifest(r)) - .transpose()?; - let workspace_config = match me.workspace { - Some(ref toml_config) => { - let lints = toml_config.lints.clone(); - let lints = verify_lints(lints)?; - let inheritable = InheritableFields { - package: toml_config.package.clone(), - dependencies: toml_config.dependencies.clone(), - lints, - _ws_root: root.to_path_buf(), - }; - let ws_root_config = WorkspaceRootConfig::new( - root, - &toml_config.members, - &toml_config.default_members, - &toml_config.exclude, - &Some(inheritable), - &toml_config.metadata, - ); - config - .ws_roots - .borrow_mut() - .insert(root.to_path_buf(), ws_root_config.clone()); - WorkspaceConfig::Root(ws_root_config) - } - None => { - bail!("virtual manifests must be configured with [workspace]"); - } + let (replace, patch) = { + let mut cx = Context { + deps: &mut deps, + source_id, + nested_paths: &mut nested_paths, + config, + warnings: &mut warnings, + platform: None, + features: &features, + root, }; - Ok(( - VirtualManifest::new( - replace, - patch, - workspace_config, - profiles, - features, - resolve_behavior, - ), - nested_paths, - )) + (replace(&me, &mut cx)?, patch(&me, &mut cx)?) + }; + let profiles = me.profile.clone(); + if let Some(profiles) = &profiles { + validate_profiles(profiles, config.cli_unstable(), &features, &mut warnings)?; } - - fn replace(&self, cx: &mut Context<'_, '_>) -> CargoResult> { - if self.patch.is_some() && self.replace.is_some() { - bail!("cannot specify both [replace] and [patch]"); + let resolve_behavior = me + .workspace + .as_ref() + .and_then(|ws| ws.resolver.as_deref()) + .map(|r| ResolveBehavior::from_manifest(r)) + .transpose()?; + let workspace_config = match me.workspace { + Some(ref toml_config) => { + let lints = toml_config.lints.clone(); + let lints = verify_lints(lints)?; + let inheritable = InheritableFields { + package: toml_config.package.clone(), + dependencies: toml_config.dependencies.clone(), + lints, + _ws_root: root.to_path_buf(), + }; + let ws_root_config = WorkspaceRootConfig::new( + root, + &toml_config.members, + &toml_config.default_members, + &toml_config.exclude, + &Some(inheritable), + &toml_config.metadata, + ); + config + .ws_roots + .borrow_mut() + .insert(root.to_path_buf(), ws_root_config.clone()); + WorkspaceConfig::Root(ws_root_config) } - let mut replace = Vec::new(); - for (spec, replacement) in self.replace.iter().flatten() { - let mut spec = PackageIdSpec::parse(spec).with_context(|| { - format!( - "replacements must specify a valid semver \ + None => { + bail!("virtual manifests must be configured with [workspace]"); + } + }; + Ok(( + VirtualManifest::new( + replace, + patch, + workspace_config, + profiles, + features, + resolve_behavior, + ), + nested_paths, + )) +} + +fn replace( + me: &schema::TomlManifest, + cx: &mut Context<'_, '_>, +) -> CargoResult> { + if me.patch.is_some() && me.replace.is_some() { + bail!("cannot specify both [replace] and [patch]"); + } + let mut replace = Vec::new(); + for (spec, replacement) in me.replace.iter().flatten() { + let mut spec = PackageIdSpec::parse(spec).with_context(|| { + format!( + "replacements must specify a valid semver \ version to replace, but `{}` does not", - spec - ) - })?; - if spec.url().is_none() { - spec.set_url(CRATES_IO_INDEX.parse().unwrap()); - } + spec + ) + })?; + if spec.url().is_none() { + spec.set_url(CRATES_IO_INDEX.parse().unwrap()); + } - if replacement.is_version_specified() { - bail!( - "replacements cannot specify a version \ + if replacement.is_version_specified() { + bail!( + "replacements cannot specify a version \ requirement, but found one for `{}`", - spec - ); - } - - let mut dep = dep_to_dependency(replacement, spec.name(), cx, None)?; - let version = spec.version().ok_or_else(|| { - anyhow!( - "replacements must specify a version \ - to replace, but `{}` does not", - spec - ) - })?; - unused_dep_keys( - dep.name_in_toml().as_str(), - "replace", - replacement.unused_keys(), - &mut cx.warnings, + spec ); - dep.set_version_req(OptVersionReq::exact(&version)); - replace.push((spec, dep)); } - Ok(replace) - } - - fn patch(&self, cx: &mut Context<'_, '_>) -> CargoResult>> { - let mut patch = HashMap::new(); - for (toml_url, deps) in self.patch.iter().flatten() { - let url = match &toml_url[..] { - CRATES_IO_REGISTRY => CRATES_IO_INDEX.parse().unwrap(), - _ => cx - .config - .get_registry_index(toml_url) - .or_else(|_| toml_url.into_url()) - .with_context(|| { - format!( - "[patch] entry `{}` should be a URL or registry name", - toml_url - ) - })?, - }; - patch.insert( - url, - deps.iter() - .map(|(name, dep)| { - unused_dep_keys( - name, - &format!("patch.{toml_url}",), - dep.unused_keys(), - &mut cx.warnings, - ); - dep_to_dependency(dep, name, cx, None) - }) - .collect::>>()?, - ); - } - Ok(patch) + + let mut dep = dep_to_dependency(replacement, spec.name(), cx, None)?; + let version = spec.version().ok_or_else(|| { + anyhow!( + "replacements must specify a version \ + to replace, but `{}` does not", + spec + ) + })?; + unused_dep_keys( + dep.name_in_toml().as_str(), + "replace", + replacement.unused_keys(), + &mut cx.warnings, + ); + dep.set_version_req(OptVersionReq::exact(&version)); + replace.push((spec, dep)); + } + Ok(replace) +} + +fn patch( + me: &schema::TomlManifest, + cx: &mut Context<'_, '_>, +) -> CargoResult>> { + let mut patch = HashMap::new(); + for (toml_url, deps) in me.patch.iter().flatten() { + let url = match &toml_url[..] { + CRATES_IO_REGISTRY => CRATES_IO_INDEX.parse().unwrap(), + _ => cx + .config + .get_registry_index(toml_url) + .or_else(|_| toml_url.into_url()) + .with_context(|| { + format!( + "[patch] entry `{}` should be a URL or registry name", + toml_url + ) + })?, + }; + patch.insert( + url, + deps.iter() + .map(|(name, dep)| { + unused_dep_keys( + name, + &format!("patch.{toml_url}",), + dep.unused_keys(), + &mut cx.warnings, + ); + dep_to_dependency(dep, name, cx, None) + }) + .collect::>>()?, + ); } + Ok(patch) } struct Context<'a, 'b> {