diff --git a/Cargo.lock b/Cargo.lock index 045c01588ef..781a3e1ab1b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -437,13 +437,13 @@ dependencies = [ [[package]] name = "cargo-util-schemas" -version = "0.1.1" +version = "0.2.0" dependencies = [ - "anyhow", "semver", "serde", "serde-untagged", "serde-value", + "thiserror", "toml", "unicode-xid", "url", diff --git a/Cargo.toml b/Cargo.toml index 88a5e0fcc35..fdbdd4ed2c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ cargo-platform = { path = "crates/cargo-platform", version = "0.1.4" } cargo-test-macro = { path = "crates/cargo-test-macro" } cargo-test-support = { path = "crates/cargo-test-support" } cargo-util = { version = "0.2.6", path = "crates/cargo-util" } -cargo-util-schemas = { version = "0.1.0", path = "crates/cargo-util-schemas" } +cargo-util-schemas = { version = "0.2.0", path = "crates/cargo-util-schemas" } cargo_metadata = "0.18.1" clap = "4.4.10" color-print = "0.3.5" diff --git a/crates/cargo-util-schemas/Cargo.toml b/crates/cargo-util-schemas/Cargo.toml index 32a13c26394..19170618e11 100644 --- a/crates/cargo-util-schemas/Cargo.toml +++ b/crates/cargo-util-schemas/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cargo-util-schemas" -version = "0.1.1" +version = "0.2.0" rust-version.workspace = true edition.workspace = true license.workspace = true @@ -9,11 +9,11 @@ repository.workspace = true description = "Deserialization schemas for Cargo" [dependencies] -anyhow.workspace = true semver.workspace = true serde = { workspace = true, features = ["derive"] } serde-untagged.workspace = true serde-value.workspace = true +thiserror.workspace = true toml.workspace = true unicode-xid.workspace = true url.workspace = true diff --git a/crates/cargo-util-schemas/src/core/mod.rs b/crates/cargo-util-schemas/src/core/mod.rs index aad6d1488f3..e8a878aa77c 100644 --- a/crates/cargo-util-schemas/src/core/mod.rs +++ b/crates/cargo-util-schemas/src/core/mod.rs @@ -3,6 +3,8 @@ mod partial_version; mod source_kind; pub use package_id_spec::PackageIdSpec; +pub use package_id_spec::PackageIdSpecError; pub use partial_version::PartialVersion; +pub use partial_version::PartialVersionError; pub use source_kind::GitReference; pub use source_kind::SourceKind; diff --git a/crates/cargo-util-schemas/src/core/package_id_spec.rs b/crates/cargo-util-schemas/src/core/package_id_spec.rs index e0a706750af..7584771b3ee 100644 --- a/crates/cargo-util-schemas/src/core/package_id_spec.rs +++ b/crates/cargo-util-schemas/src/core/package_id_spec.rs @@ -1,15 +1,17 @@ use std::fmt; -use anyhow::bail; -use anyhow::Result; use semver::Version; use serde::{de, ser}; use url::Url; use crate::core::GitReference; use crate::core::PartialVersion; +use crate::core::PartialVersionError; use crate::core::SourceKind; use crate::manifest::PackageName; +use crate::restricted_names::NameValidationError; + +type Result = std::result::Result; /// Some or all of the data required to identify a package: /// @@ -83,12 +85,11 @@ impl PackageIdSpec { if abs.exists() { let maybe_url = Url::from_file_path(abs) .map_or_else(|_| "a file:// URL".to_string(), |url| url.to_string()); - bail!( - "package ID specification `{}` looks like a file path, \ - maybe try {}", - spec, - maybe_url - ); + return Err(ErrorKind::MaybeFilePath { + spec: spec.into(), + maybe_url, + } + .into()); } } let mut parts = spec.splitn(2, [':', '@']); @@ -119,14 +120,14 @@ impl PackageIdSpec { } "registry" => { if url.query().is_some() { - bail!("cannot have a query string in a pkgid: {url}") + return Err(ErrorKind::UnexpectedQueryString(url).into()); } kind = Some(SourceKind::Registry); url = strip_url_protocol(&url); } "sparse" => { if url.query().is_some() { - bail!("cannot have a query string in a pkgid: {url}") + return Err(ErrorKind::UnexpectedQueryString(url).into()); } kind = Some(SourceKind::SparseRegistry); // Leave `sparse` as part of URL, see `SourceId::new` @@ -134,19 +135,19 @@ impl PackageIdSpec { } "path" => { if url.query().is_some() { - bail!("cannot have a query string in a pkgid: {url}") + return Err(ErrorKind::UnexpectedQueryString(url).into()); } if scheme != "file" { - anyhow::bail!("`path+{scheme}` is unsupported; `path+file` and `file` schemes are supported"); + return Err(ErrorKind::UnsupportedPathPlusScheme(scheme.into()).into()); } kind = Some(SourceKind::Path); url = strip_url_protocol(&url); } - kind => anyhow::bail!("unsupported source protocol: {kind}"), + kind => return Err(ErrorKind::UnsupportedProtocol(kind.into()).into()), } } else { if url.query().is_some() { - bail!("cannot have a query string in a pkgid: {url}") + return Err(ErrorKind::UnexpectedQueryString(url).into()); } } @@ -154,16 +155,9 @@ impl PackageIdSpec { url.set_fragment(None); let (name, version) = { - let mut path = url - .path_segments() - .ok_or_else(|| anyhow::format_err!("pkgid urls must have a path: {}", url))?; - let path_name = path.next_back().ok_or_else(|| { - anyhow::format_err!( - "pkgid urls must have at least one path \ - component: {}", - url - ) - })?; + let Some(path_name) = url.path_segments().and_then(|mut p| p.next_back()) else { + return Err(ErrorKind::MissingUrlPath(url).into()); + }; match frag { Some(fragment) => match fragment.split_once([':', '@']) { Some((name, part)) => { @@ -259,7 +253,7 @@ impl fmt::Display for PackageIdSpec { } impl ser::Serialize for PackageIdSpec { - fn serialize(&self, s: S) -> Result + fn serialize(&self, s: S) -> std::result::Result where S: ser::Serializer, { @@ -268,7 +262,7 @@ impl ser::Serialize for PackageIdSpec { } impl<'de> de::Deserialize<'de> for PackageIdSpec { - fn deserialize(d: D) -> Result + fn deserialize(d: D) -> std::result::Result where D: de::Deserializer<'de>, { @@ -277,6 +271,49 @@ impl<'de> de::Deserialize<'de> for PackageIdSpec { } } +/// Error parsing a [`PackageIdSpec`]. +#[derive(Debug, thiserror::Error)] +#[error(transparent)] +pub struct PackageIdSpecError(#[from] ErrorKind); + +impl From for PackageIdSpecError { + fn from(value: PartialVersionError) -> Self { + ErrorKind::PartialVersion(value).into() + } +} + +impl From for PackageIdSpecError { + fn from(value: NameValidationError) -> Self { + ErrorKind::NameValidation(value).into() + } +} + +/// Non-public error kind for [`PackageIdSpecError`]. +#[non_exhaustive] +#[derive(Debug, thiserror::Error)] +enum ErrorKind { + #[error("unsupported source protocol: {0}")] + UnsupportedProtocol(String), + + #[error("`path+{0}` is unsupported; `path+file` and `file` schemes are supported")] + UnsupportedPathPlusScheme(String), + + #[error("cannot have a query string in a pkgid: {0}")] + UnexpectedQueryString(Url), + + #[error("pkgid urls must have at least one path component: {0}")] + MissingUrlPath(Url), + + #[error("package ID specification `{spec}` looks like a file path, maybe try {maybe_url}")] + MaybeFilePath { spec: String, maybe_url: String }, + + #[error(transparent)] + NameValidation(#[from] crate::restricted_names::NameValidationError), + + #[error(transparent)] + PartialVersion(#[from] crate::core::PartialVersionError), +} + #[cfg(test)] mod tests { use super::PackageIdSpec; diff --git a/crates/cargo-util-schemas/src/core/partial_version.rs b/crates/cargo-util-schemas/src/core/partial_version.rs index 9670bd87764..b9c1db82ed1 100644 --- a/crates/cargo-util-schemas/src/core/partial_version.rs +++ b/crates/cargo-util-schemas/src/core/partial_version.rs @@ -80,11 +80,11 @@ impl From for PartialVersion { } impl std::str::FromStr for PartialVersion { - type Err = anyhow::Error; + type Err = PartialVersionError; fn from_str(value: &str) -> Result { if is_req(value) { - anyhow::bail!("unexpected version requirement, expected a version like \"1.32\"") + return Err(ErrorKind::VersionReq.into()); } match semver::Version::parse(value) { Ok(ver) => Ok(ver.into()), @@ -92,15 +92,9 @@ impl std::str::FromStr for PartialVersion { // HACK: Leverage `VersionReq` for partial version parsing let mut version_req = match semver::VersionReq::parse(value) { Ok(req) => req, - Err(_) if value.contains('-') => { - anyhow::bail!( - "unexpected prerelease field, expected a version like \"1.32\"" - ) - } - Err(_) if value.contains('+') => { - anyhow::bail!("unexpected build field, expected a version like \"1.32\"") - } - Err(_) => anyhow::bail!("expected a version like \"1.32\""), + Err(_) if value.contains('-') => return Err(ErrorKind::Prerelease.into()), + Err(_) if value.contains('+') => return Err(ErrorKind::BuildMetadata.into()), + Err(_) => return Err(ErrorKind::Unexpected.into()), }; assert_eq!(version_req.comparators.len(), 1, "guaranteed by is_req"); let comp = version_req.comparators.pop().unwrap(); @@ -163,6 +157,28 @@ impl<'de> serde::Deserialize<'de> for PartialVersion { } } +/// Error parsing a [`PartialVersion`]. +#[derive(Debug, thiserror::Error)] +#[error(transparent)] +pub struct PartialVersionError(#[from] ErrorKind); + +/// Non-public error kind for [`PartialVersionError`]. +#[non_exhaustive] +#[derive(Debug, thiserror::Error)] +enum ErrorKind { + #[error("unexpected version requirement, expected a version like \"1.32\"")] + VersionReq, + + #[error("unexpected prerelease field, expected a version like \"1.32\"")] + Prerelease, + + #[error("unexpected build field, expected a version like \"1.32\"")] + BuildMetadata, + + #[error("expected a version like \"1.32\"")] + Unexpected, +} + fn is_req(value: &str) -> bool { let Some(first) = value.chars().next() else { return false; diff --git a/crates/cargo-util-schemas/src/manifest.rs b/crates/cargo-util-schemas/src/manifest.rs index eb6d4ea4852..0bf921418c0 100644 --- a/crates/cargo-util-schemas/src/manifest.rs +++ b/crates/cargo-util-schemas/src/manifest.rs @@ -10,7 +10,6 @@ use std::fmt::{self, Display, Write}; use std::path::PathBuf; use std::str; -use anyhow::Result; use serde::de::{self, IntoDeserializer as _, Unexpected}; use serde::ser; use serde::{Deserialize, Serialize}; @@ -18,8 +17,11 @@ use serde_untagged::UntaggedEnumVisitor; use crate::core::PackageIdSpec; use crate::core::PartialVersion; +use crate::core::PartialVersionError; use crate::restricted_names; +pub use crate::restricted_names::NameValidationError; + /// This type is used to deserialize `Cargo.toml` files. #[derive(Debug, Deserialize, Serialize)] #[serde(rename_all = "kebab-case")] @@ -1144,7 +1146,7 @@ macro_rules! str_newtype { } impl<'a> std::str::FromStr for $name { - type Err = anyhow::Error; + type Err = restricted_names::NameValidationError; fn from_str(value: &str) -> Result { Self::new(value.to_owned()) @@ -1173,8 +1175,8 @@ str_newtype!(PackageName); impl> PackageName { /// Validated package name - pub fn new(name: T) -> Result { - restricted_names::validate_package_name(name.as_ref(), "package name", "")?; + pub fn new(name: T) -> Result { + restricted_names::validate_package_name(name.as_ref(), "package name")?; Ok(Self(name)) } } @@ -1195,8 +1197,8 @@ str_newtype!(RegistryName); impl> RegistryName { /// Validated registry name - pub fn new(name: T) -> Result { - restricted_names::validate_package_name(name.as_ref(), "registry name", "")?; + pub fn new(name: T) -> Result { + restricted_names::validate_package_name(name.as_ref(), "registry name")?; Ok(Self(name)) } } @@ -1205,7 +1207,7 @@ str_newtype!(ProfileName); impl> ProfileName { /// Validated profile name - pub fn new(name: T) -> Result { + pub fn new(name: T) -> Result { restricted_names::validate_profile_name(name.as_ref())?; Ok(Self(name)) } @@ -1215,7 +1217,7 @@ str_newtype!(FeatureName); impl> FeatureName { /// Validated feature name - pub fn new(name: T) -> Result { + pub fn new(name: T) -> Result { restricted_names::validate_feature_name(name.as_ref())?; Ok(Self(name)) } @@ -1334,15 +1336,16 @@ impl std::ops::Deref for RustVersion { } impl std::str::FromStr for RustVersion { - type Err = anyhow::Error; + type Err = RustVersionError; fn from_str(value: &str) -> Result { - let partial = value.parse::()?; + let partial = value.parse::(); + let partial = partial.map_err(RustVersionErrorKind::PartialVersion)?; if partial.pre.is_some() { - anyhow::bail!("unexpected prerelease field, expected a version like \"1.32\"") + return Err(RustVersionErrorKind::Prerelease.into()); } if partial.build.is_some() { - anyhow::bail!("unexpected prerelease field, expected a version like \"1.32\"") + return Err(RustVersionErrorKind::BuildMetadata.into()); } Ok(Self(partial)) } @@ -1366,6 +1369,25 @@ impl Display for RustVersion { } } +/// Error parsing a [`RustVersion`]. +#[derive(Debug, thiserror::Error)] +#[error(transparent)] +pub struct RustVersionError(#[from] RustVersionErrorKind); + +/// Non-public error kind for [`RustVersionError`]. +#[non_exhaustive] +#[derive(Debug, thiserror::Error)] +enum RustVersionErrorKind { + #[error("unexpected prerelease field, expected a version like \"1.32\"")] + Prerelease, + + #[error("unexpected build field, expected a version like \"1.32\"")] + BuildMetadata, + + #[error(transparent)] + PartialVersion(#[from] PartialVersionError), +} + #[derive(Copy, Clone, Debug)] pub struct InvalidCargoFeatures {} diff --git a/crates/cargo-util-schemas/src/restricted_names.rs b/crates/cargo-util-schemas/src/restricted_names.rs index 2d22ce4f2f7..79d01ac9b88 100644 --- a/crates/cargo-util-schemas/src/restricted_names.rs +++ b/crates/cargo-util-schemas/src/restricted_names.rs @@ -1,7 +1,37 @@ //! Helpers for validating and checking names like package and crate names. -use anyhow::bail; -use anyhow::Result; +type Result = std::result::Result; + +/// Error validating names in Cargo. +#[derive(Debug, thiserror::Error)] +#[error(transparent)] +pub struct NameValidationError(#[from] ErrorKind); + +/// Non-public error kind for [`NameValidationError`]. +#[non_exhaustive] +#[derive(Debug, thiserror::Error)] +enum ErrorKind { + #[error("{0} cannot be empty")] + Empty(&'static str), + + #[error("invalid character `{ch}` in {what}: `{name}`, {reason}")] + InvalidCharacter { + ch: char, + what: &'static str, + name: String, + reason: &'static str, + }, + + #[error( + "profile name `{name}` is reserved\n{help}\n\ + See https://doc.rust-lang.org/cargo/reference/profiles.html \ + for more on configuring profiles." + )] + ProfileNameReservedKeyword { name: String, help: &'static str }, + + #[error("feature named `{0}` is not allowed to start with `dep:`")] + FeatureNameStartsWithDepColon(String), +} /// Check the base requirements for a package name. /// @@ -9,53 +39,51 @@ use anyhow::Result; /// level of sanity. Note that package names have other restrictions /// elsewhere. `cargo new` has a few restrictions, such as checking for /// reserved names. crates.io has even more restrictions. -pub fn validate_package_name(name: &str, what: &str, help: &str) -> Result<()> { +pub(crate) fn validate_package_name(name: &str, what: &'static str) -> Result<()> { if name.is_empty() { - bail!("{what} cannot be empty"); + return Err(ErrorKind::Empty(what).into()); } let mut chars = name.chars(); if let Some(ch) = chars.next() { if ch.is_digit(10) { // A specific error for a potentially common case. - bail!( - "the name `{}` cannot be used as a {}, \ - the name cannot start with a digit{}", - name, + return Err(ErrorKind::InvalidCharacter { + ch, what, - help - ); + name: name.into(), + reason: "the name cannot start with a digit", + } + .into()); } if !(unicode_xid::UnicodeXID::is_xid_start(ch) || ch == '_') { - bail!( - "invalid character `{}` in {}: `{}`, \ - the first character must be a Unicode XID start character \ - (most letters or `_`){}", + return Err(ErrorKind::InvalidCharacter { ch, what, - name, - help - ); + name: name.into(), + reason: "the first character must be a Unicode XID start character \ + (most letters or `_`)", + } + .into()); } } for ch in chars { if !(unicode_xid::UnicodeXID::is_xid_continue(ch) || ch == '-') { - bail!( - "invalid character `{}` in {}: `{}`, \ - characters must be Unicode XID characters \ - (numbers, `-`, `_`, or most letters){}", + return Err(ErrorKind::InvalidCharacter { ch, what, - name, - help - ); + name: name.into(), + reason: "characters must be Unicode XID characters \ + (numbers, `-`, `_`, or most letters)", + } + .into()); } } Ok(()) } /// Ensure a package name is [valid][validate_package_name] -pub fn sanitize_package_name(name: &str, placeholder: char) -> String { +pub(crate) fn sanitize_package_name(name: &str, placeholder: char) -> String { let mut slug = String::new(); let mut chars = name.chars(); while let Some(ch) = chars.next() { @@ -78,42 +106,36 @@ pub fn sanitize_package_name(name: &str, placeholder: char) -> String { } /// Validate dir-names and profile names according to RFC 2678. -pub fn validate_profile_name(name: &str) -> Result<()> { +pub(crate) fn validate_profile_name(name: &str) -> Result<()> { if let Some(ch) = name .chars() .find(|ch| !ch.is_alphanumeric() && *ch != '_' && *ch != '-') { - bail!( - "invalid character `{}` in profile name `{}`\n\ - Allowed characters are letters, numbers, underscore, and hyphen.", + return Err(ErrorKind::InvalidCharacter { ch, - name - ); + what: "profile name", + name: name.into(), + reason: "allowed characters are letters, numbers, underscore, and hyphen", + } + .into()); } - const SEE_DOCS: &str = "See https://doc.rust-lang.org/cargo/reference/profiles.html \ - for more on configuring profiles."; - let lower_name = name.to_lowercase(); if lower_name == "debug" { - bail!( - "profile name `{}` is reserved\n\ - To configure the default development profile, use the name `dev` \ - as in [profile.dev]\n\ - {}", - name, - SEE_DOCS - ); + return Err(ErrorKind::ProfileNameReservedKeyword { + name: name.into(), + help: "To configure the default development profile, \ + use the name `dev` as in [profile.dev]", + } + .into()); } if lower_name == "build-override" { - bail!( - "profile name `{}` is reserved\n\ - To configure build dependency settings, use [profile.dev.build-override] \ - and [profile.release.build-override]\n\ - {}", - name, - SEE_DOCS - ); + return Err(ErrorKind::ProfileNameReservedKeyword { + name: name.into(), + help: "To configure build dependency settings, use [profile.dev.build-override] \ + and [profile.release.build-override]", + } + .into()); } // These are some arbitrary reservations. We have no plans to use @@ -144,46 +166,57 @@ pub fn validate_profile_name(name: &str) -> Result<()> { | "uninstall" ) || lower_name.starts_with("cargo") { - bail!( - "profile name `{}` is reserved\n\ - Please choose a different name.\n\ - {}", - name, - SEE_DOCS - ); + return Err(ErrorKind::ProfileNameReservedKeyword { + name: name.into(), + help: "Please choose a different name.", + } + .into()); } Ok(()) } -pub fn validate_feature_name(name: &str) -> Result<()> { +pub(crate) fn validate_feature_name(name: &str) -> Result<()> { + let what = "feature name"; if name.is_empty() { - bail!("feature name cannot be empty"); + return Err(ErrorKind::Empty(what).into()); } if name.starts_with("dep:") { - bail!("feature named `{name}` is not allowed to start with `dep:`",); + return Err(ErrorKind::FeatureNameStartsWithDepColon(name.into()).into()); } if name.contains('/') { - bail!("feature named `{name}` is not allowed to contain slashes",); + return Err(ErrorKind::InvalidCharacter { + ch: '/', + what, + name: name.into(), + reason: "feature name is not allowed to contain slashes", + } + .into()); } let mut chars = name.chars(); if let Some(ch) = chars.next() { if !(unicode_xid::UnicodeXID::is_xid_start(ch) || ch == '_' || ch.is_digit(10)) { - bail!( - "invalid character `{ch}` in feature `{name}`, \ - the first character must be a Unicode XID start character or digit \ - (most letters or `_` or `0` to `9`)", - ); + return Err(ErrorKind::InvalidCharacter { + ch, + what, + name: name.into(), + reason: "the first character must be a Unicode XID start character or digit \ + (most letters or `_` or `0` to `9`)", + } + .into()); } } for ch in chars { if !(unicode_xid::UnicodeXID::is_xid_continue(ch) || ch == '-' || ch == '+' || ch == '.') { - bail!( - "invalid character `{ch}` in feature `{name}`, \ - characters must be Unicode XID characters, '-', `+`, or `.` \ - (numbers, `+`, `-`, `_`, `.`, or most letters)", - ); + return Err(ErrorKind::InvalidCharacter { + ch, + what, + name: name.into(), + reason: "characters must be Unicode XID characters, '-', `+`, or `.` \ + (numbers, `+`, `-`, `_`, `.`, or most letters)", + } + .into()); } } Ok(()) diff --git a/src/cargo/ops/cargo_compile/packages.rs b/src/cargo/ops/cargo_compile/packages.rs index 192bfcb3a3f..0472cc83716 100644 --- a/src/cargo/ops/cargo_compile/packages.rs +++ b/src/cargo/ops/cargo_compile/packages.rs @@ -72,7 +72,7 @@ impl Packages { let mut specs = packages .iter() .map(|p| PackageIdSpec::parse(p)) - .collect::>>()?; + .collect::, _>>()?; if !patterns.is_empty() { let matched_pkgs = ws .members() diff --git a/src/cargo/ops/tree/mod.rs b/src/cargo/ops/tree/mod.rs index 6928ec5f94b..79a69a66a4f 100644 --- a/src/cargo/ops/tree/mod.rs +++ b/src/cargo/ops/tree/mod.rs @@ -186,7 +186,7 @@ pub fn build_and_print(ws: &Workspace<'_>, opts: &TreeOptions) -> CargoResult<() opts.invert .iter() .map(|p| PackageIdSpec::parse(p)) - .collect::>>()? + .collect::, _>>()? }; let root_ids = ws_resolve.targeted_resolve.specs_to_ids(&root_specs)?; let root_indexes = graph.indexes_from_ids(&root_ids); @@ -207,7 +207,7 @@ pub fn build_and_print(ws: &Workspace<'_>, opts: &TreeOptions) -> CargoResult<() let pkgs_to_prune = opts .pkgs_to_prune .iter() - .map(|p| PackageIdSpec::parse(p)) + .map(|p| PackageIdSpec::parse(p).map_err(Into::into)) .map(|r| { // Provide an error message if pkgid is not within the resolved // dependencies graph. diff --git a/tests/testsuite/features.rs b/tests/testsuite/features.rs index febdf52fed8..221de106fa8 100644 --- a/tests/testsuite/features.rs +++ b/tests/testsuite/features.rs @@ -2063,7 +2063,7 @@ Caused by: | 8 | \"+foo\" = [] | ^^^^^^ - invalid character `+` in feature `+foo`, \ + invalid character `+` in feature name: `+foo`, \ the first character must be a Unicode XID start character or digit \ (most letters or `_` or `0` to `9`) ", @@ -2094,7 +2094,7 @@ Caused by: | 8 | \"a&b\" = [] | ^^^^^ - invalid character `&` in feature `a&b`, \ + invalid character `&` in feature name: `a&b`, \ characters must be Unicode XID characters, '-', `+`, or `.` \ (numbers, `+`, `-`, `_`, `.`, or most letters) ", @@ -2131,7 +2131,7 @@ Caused by: | 7 | \"foo/bar\" = [] | ^^^^^^^^^ - feature named `foo/bar` is not allowed to contain slashes + invalid character `/` in feature name: `foo/bar`, feature name is not allowed to contain slashes ", ) .run(); diff --git a/tests/testsuite/new.rs b/tests/testsuite/new.rs index a34169e9dbb..2f56aa677d7 100644 --- a/tests/testsuite/new.rs +++ b/tests/testsuite/new.rs @@ -348,8 +348,7 @@ fn explicit_invalid_name_not_suggested() { .with_status(101) .with_stderr( "\ -[ERROR] the name `10-invalid` cannot be used as a package name, \ -the name cannot start with a digit\n\ +[ERROR] invalid character `1` in package name: `10-invalid`, the name cannot start with a digit If you need a binary with the name \"10-invalid\", use a valid package name, \ and set the binary name to be different from the package. \ This can be done by setting the binary filename to `src/bin/10-invalid.rs` \ diff --git a/tests/testsuite/profile_custom.rs b/tests/testsuite/profile_custom.rs index cf9828d3794..c3374874d8c 100644 --- a/tests/testsuite/profile_custom.rs +++ b/tests/testsuite/profile_custom.rs @@ -90,8 +90,7 @@ Caused by: | 7 | [profile.'.release-lto'] | ^^^^^^^^^^^^^^ - invalid character `.` in profile name `.release-lto` - Allowed characters are letters, numbers, underscore, and hyphen. + invalid character `.` in profile name: `.release-lto`, allowed characters are letters, numbers, underscore, and hyphen ", ) .run();