From 3469dd6de4bcde3990adc9b1b1537ec04e6cfa21 Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Thu, 8 Feb 2024 19:27:23 -0300 Subject: [PATCH 01/41] test: add test for `required_version` --- src/config/mod.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/config/mod.rs b/src/config/mod.rs index 9484b2e5829..23146455d9d 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1065,4 +1065,34 @@ make_backup = false ]) ); } + + #[test] + fn test_required_version_default() { + let config = Config::default(); + assert!(config.version_meets_requirement()); + } + + #[test] + fn test_current_required_version() { + let toml = format!("required_version=\"{}\"", env!("CARGO_PKG_VERSION")); + let config = Config::from_toml(&toml, Path::new("")).unwrap(); + + assert!(config.version_meets_requirement()); + } + + #[test] + fn test_required_version_above() { + let toml = "required_version=\"1000.0.0\""; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + + assert!(!config.version_meets_requirement()); + } + + #[test] + fn test_required_version_below() { + let toml = "required_version=\"0.0.0\""; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + + assert!(!config.version_meets_requirement()); + } } From 2ce0666d74577d92601c0a8d5258d08021e30da3 Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Thu, 8 Feb 2024 19:31:06 -0300 Subject: [PATCH 02/41] chore: install `semver` --- Cargo.lock | 25 +++++++++++++------------ Cargo.toml | 1 + 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7465a4f9521..4148e504a0a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -455,18 +455,18 @@ checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" [[package]] name = "proc-macro2" -version = "1.0.63" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b368fba921b0dce7e60f5e04ec15e565b3303972b42bcfde1d0713b881959eb" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.26" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -544,6 +544,7 @@ dependencies = [ "lazy_static", "regex", "rustfmt-config_proc_macro", + "semver", "serde", "serde_json", "term", @@ -579,27 +580,27 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.7" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d65bd28f48be7196d222d95b9243287f48d27aca604e08497513019ff0502cc4" +checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" dependencies = [ "serde", ] [[package]] name = "serde" -version = "1.0.160" +version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.160" +version = "1.0.196" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" dependencies = [ "proc-macro2", "quote", @@ -649,9 +650,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "2.0.14" +version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf316d5356ed6847742d036f8a39c3b8435cac10bd528a4bd461928a6ab34d5" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index bcd3b420acb..3a3e664e6a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,6 +58,7 @@ unicode-width = "0.1" unicode-properties = { version = "0.1", default-features = false, features = ["general-category"] } rustfmt-config_proc_macro = { version = "0.3", path = "config_proc_macro" } +semver = "1.0.21" # Rustc dependencies are loaded from the sysroot, Cargo doesn't know about them. From 01a1ebf800f5b056e9f895a3aa31366c7e631faf Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Thu, 8 Feb 2024 19:31:17 -0300 Subject: [PATCH 03/41] refactor: use `semver` to compare versions --- src/config/mod.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index 23146455d9d..4cffd435f0f 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -216,12 +216,14 @@ impl PartialConfig { impl Config { pub(crate) fn version_meets_requirement(&self) -> bool { if self.was_set().required_version() { - let version = env!("CARGO_PKG_VERSION"); - let required_version = self.required_version(); - if version != required_version { - println!( - "Error: rustfmt version ({version}) doesn't match the required version \ -({required_version})" + let version = semver::Version::parse(env!("CARGO_PKG_VERSION")).unwrap(); + let required_version = semver::VersionReq::parse(self.required_version().as_str()) + .expect("Failed to parse required_version"); + + if !required_version.matches(&version) { + eprintln!( + "Error: rustfmt version ({}) doesn't match the required version ({})", + version, required_version ); return false; } From 73e75adc63e337c048cfdac380710b7dd1939ba7 Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Thu, 8 Feb 2024 20:04:04 -0300 Subject: [PATCH 04/41] fix: use exact version for comparison when no comparator is specified --- src/config/mod.rs | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index 4cffd435f0f..3dcb882e27f 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -217,13 +217,24 @@ impl Config { pub(crate) fn version_meets_requirement(&self) -> bool { if self.was_set().required_version() { let version = semver::Version::parse(env!("CARGO_PKG_VERSION")).unwrap(); - let required_version = semver::VersionReq::parse(self.required_version().as_str()) + let comparators = vec!["=", ">", "<", ">=", "<=", "~", "^"]; + let required_version = self.required_version(); + let has_comparator = comparators.iter().any(|c| required_version.contains(c)); + let mut req_version = semver::VersionReq::parse(required_version.as_str()) .expect("Failed to parse required_version"); - if !required_version.matches(&version) { + if !has_comparator { + // If no comparator is specified, we default to an exact match. + // By default, `semver::VersionReq` uses `^` when no comparator is specified. + let exact_version = format!("={}", required_version); + req_version = semver::VersionReq::parse(exact_version.as_str()) + .expect("Failed to parse required_version"); + } + + if !req_version.matches(&version) { eprintln!( "Error: rustfmt version ({}) doesn't match the required version ({})", - version, required_version + version, req_version ); return false; } @@ -1092,9 +1103,13 @@ make_backup = false #[test] fn test_required_version_below() { - let toml = "required_version=\"0.0.0\""; - let config = Config::from_toml(toml, Path::new("")).unwrap(); + let versions = vec!["0.0.0", "0.0.1", "0.1.0", "1.0.0", "1.0.1", "1.1.0"]; - assert!(!config.version_meets_requirement()); + for version in versions { + let toml = format!("required_version=\"{}\"", version); + let config = Config::from_toml(&toml, Path::new("")).unwrap(); + + assert!(!config.version_meets_requirement()); + } } } From 75b65d698a87b4c049d8051cce2afa696e66bc9f Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Thu, 8 Feb 2024 21:51:01 -0300 Subject: [PATCH 05/41] refactor: avoid overhead by considering default behavior from `semver` --- src/config/mod.rs | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index 3dcb882e27f..1ceb847016a 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -217,24 +217,13 @@ impl Config { pub(crate) fn version_meets_requirement(&self) -> bool { if self.was_set().required_version() { let version = semver::Version::parse(env!("CARGO_PKG_VERSION")).unwrap(); - let comparators = vec!["=", ">", "<", ">=", "<=", "~", "^"]; - let required_version = self.required_version(); - let has_comparator = comparators.iter().any(|c| required_version.contains(c)); - let mut req_version = semver::VersionReq::parse(required_version.as_str()) + let required_version = semver::VersionReq::parse(self.required_version().as_str()) .expect("Failed to parse required_version"); - if !has_comparator { - // If no comparator is specified, we default to an exact match. - // By default, `semver::VersionReq` uses `^` when no comparator is specified. - let exact_version = format!("={}", required_version); - req_version = semver::VersionReq::parse(exact_version.as_str()) - .expect("Failed to parse required_version"); - } - - if !req_version.matches(&version) { + if !required_version.matches(&version) { eprintln!( "Error: rustfmt version ({}) doesn't match the required version ({})", - version, req_version + version, required_version ); return false; } @@ -1103,10 +1092,10 @@ make_backup = false #[test] fn test_required_version_below() { - let versions = vec!["0.0.0", "0.0.1", "0.1.0", "1.0.0", "1.0.1", "1.1.0"]; + let versions = vec!["0.0.0", "0.0.1", "0.1.0"]; for version in versions { - let toml = format!("required_version=\"{}\"", version); + let toml = format!("required_version=\"{}\"", version.to_string()); let config = Config::from_toml(&toml, Path::new("")).unwrap(); assert!(!config.version_meets_requirement()); From 33eb55c31393bcf67f7617f0cd949f61b98bebd5 Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Thu, 8 Feb 2024 22:33:16 -0300 Subject: [PATCH 06/41] test: add complex comparisons for required_version --- src/config/mod.rs | 222 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 200 insertions(+), 22 deletions(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index 1ceb847016a..1034c32b735 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1068,36 +1068,214 @@ make_backup = false ); } - #[test] - fn test_required_version_default() { - let config = Config::default(); - assert!(config.version_meets_requirement()); - } + #[cfg(test)] + mod required_version { + use super::*; - #[test] - fn test_current_required_version() { - let toml = format!("required_version=\"{}\"", env!("CARGO_PKG_VERSION")); - let config = Config::from_toml(&toml, Path::new("")).unwrap(); + fn get_current_version() -> semver::Version { + semver::Version::parse(env!("CARGO_PKG_VERSION")).unwrap() + } - assert!(config.version_meets_requirement()); - } + #[nightly_only_test] + #[test] + fn test_required_version_default() { + let config = Config::default(); + assert!(config.version_meets_requirement()); + } - #[test] - fn test_required_version_above() { - let toml = "required_version=\"1000.0.0\""; - let config = Config::from_toml(toml, Path::new("")).unwrap(); + #[nightly_only_test] + #[test] + fn test_current_required_version() { + let toml = format!("required_version=\"{}\"", env!("CARGO_PKG_VERSION")); + let config = Config::from_toml(&toml, Path::new("")).unwrap(); - assert!(!config.version_meets_requirement()); - } + assert!(config.version_meets_requirement()); + } - #[test] - fn test_required_version_below() { - let versions = vec!["0.0.0", "0.0.1", "0.1.0"]; + #[nightly_only_test] + #[test] + fn test_required_version_above() { + let toml = "required_version=\"1000.0.0\""; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + + assert!(!config.version_meets_requirement()); + } + + #[nightly_only_test] + #[test] + fn test_required_version_below() { + let versions = vec!["0.0.0", "0.0.1", "0.1.0"]; + + for version in versions { + let toml = format!("required_version=\"{}\"", version.to_string()); + let config = Config::from_toml(&toml, Path::new("")).unwrap(); + + assert!(!config.version_meets_requirement()); + } + } + + #[nightly_only_test] + #[test] + fn test_required_version_tilde() { + let toml = format!("required_version=\"~{}\"", env!("CARGO_PKG_VERSION")); + let config = Config::from_toml(&toml, Path::new("")).unwrap(); + + assert!(config.version_meets_requirement()); + } + + #[nightly_only_test] + #[test] + fn test_required_version_caret() { + let current_version = get_current_version(); + + for minor in current_version.minor..0 { + let toml = format!( + "required_version=\"^{}.{}.0\"", + current_version.major.to_string(), + minor.to_string() + ); + let config = Config::from_toml(&toml, Path::new("")).unwrap(); + + assert!(!config.version_meets_requirement()); + } + } + + #[nightly_only_test] + #[test] + fn test_required_version_greater_than() { + let toml = "required_version=\">1.0.0\""; + let config = Config::from_toml(toml, Path::new("")).unwrap(); - for version in versions { - let toml = format!("required_version=\"{}\"", version.to_string()); + assert!(config.version_meets_requirement()); + } + + #[nightly_only_test] + #[test] + fn test_required_version_less_than() { + let toml = "required_version=\"<1.0.0\""; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + + assert!(!config.version_meets_requirement()); + } + + #[nightly_only_test] + #[test] + fn test_required_version_range() { + let current_version = get_current_version(); + + let toml = format!( + "required_version=\">={}.0.0, <{}.0.0\"", + current_version.major, + current_version.major + 1 + ); let config = Config::from_toml(&toml, Path::new("")).unwrap(); + assert!(config.version_meets_requirement()); + } + + #[nightly_only_test] + #[test] + fn test_required_version_exact_boundary() { + let toml = format!("required_version=\"{}\"", get_current_version().to_string()); + let config = Config::from_toml(&toml, Path::new("")).unwrap(); + + assert!(config.version_meets_requirement()); + } + + #[nightly_only_test] + #[test] + fn test_required_version_pre_release() { + let toml = format!( + "required_version=\"{}-alpha\"", + get_current_version().to_string() + ); + let config = Config::from_toml(&toml, Path::new("")).unwrap(); + + assert!(config.version_meets_requirement()); + } + + #[nightly_only_test] + #[test] + fn test_required_version_with_build_metadata() { + let toml = format!( + "required_version=\"{}+build.1\"", + get_current_version().to_string() + ); + + let config = Config::from_toml(&toml, Path::new("")).unwrap(); + + assert!(config.version_meets_requirement()); + } + + #[nightly_only_test] + #[test] + fn test_required_version_invalid_specification() { + let toml = "required_version=\"not.a.version\""; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + + assert!(!config.version_meets_requirement()) + } + + #[nightly_only_test] + #[test] + fn test_required_version_complex_range() { + let current_version = get_current_version(); + + let toml = format!( + "required_version=\">={}.0.0, <{}.0.0, ~{}.{}.0\"", + current_version.major, + current_version.major + 1, + current_version.major, + current_version.minor + ); + let config = Config::from_toml(&toml, Path::new("")).unwrap(); + + assert!(config.version_meets_requirement()); + } + + #[nightly_only_test] + #[test] + fn test_required_version_wildcard_major() { + let toml = "required_version=\"1.x\""; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + + assert!(config.version_meets_requirement()); + } + + #[nightly_only_test] + #[test] + fn test_required_version_wildcard_any() { + let toml = "required_version=\"*\""; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + + assert!(config.version_meets_requirement()); + } + + #[nightly_only_test] + #[test] + fn test_required_version_major_version_zero() { + let toml = "required_version=\"0.1.0\""; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + + assert!(!config.version_meets_requirement()); + } + + #[nightly_only_test] + #[test] + fn test_required_version_future_major_version() { + let toml = "required_version=\"3.0.0\""; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + + assert!(!config.version_meets_requirement()); + } + + #[nightly_only_test] + #[test] + fn test_required_version_fail_different_operator() { + // != is not supported + let toml = "required_version=\"!=1.0.0\""; + let config = Config::from_toml(toml, Path::new("")).unwrap(); + assert!(!config.version_meets_requirement()); } } From b9934e64371c61a63949c26b7b2cf797cdd4a798 Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Thu, 8 Feb 2024 22:33:33 -0300 Subject: [PATCH 07/41] refactor: handle `required_version` parsing manually --- src/config/mod.rs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index 1034c32b735..78eb71f3ed4 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -217,15 +217,22 @@ impl Config { pub(crate) fn version_meets_requirement(&self) -> bool { if self.was_set().required_version() { let version = semver::Version::parse(env!("CARGO_PKG_VERSION")).unwrap(); - let required_version = semver::VersionReq::parse(self.required_version().as_str()) - .expect("Failed to parse required_version"); - - if !required_version.matches(&version) { - eprintln!( - "Error: rustfmt version ({}) doesn't match the required version ({})", - version, required_version - ); - return false; + let required_version = semver::VersionReq::parse(self.required_version().as_str()); + + match required_version { + Ok(required_version) => { + if !required_version.matches(&version) { + eprintln!( + "Error: rustfmt version ({}) doesn't match the required version ({})", + version, required_version + ); + return false; + } + } + Err(e) => { + eprintln!("Error: failed to parse required version: {}", e); + return false; + } } } From 21ca2e234dd398023c7f3a193104d4c449e4e7f2 Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Thu, 8 Feb 2024 22:45:46 -0300 Subject: [PATCH 08/41] chore: add `#[allow(dead_code)]` --- src/config/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config/mod.rs b/src/config/mod.rs index 78eb71f3ed4..3030b756f1d 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1079,6 +1079,7 @@ make_backup = false mod required_version { use super::*; + #[allow(dead_code)] // Only used in tests fn get_current_version() -> semver::Version { semver::Version::parse(env!("CARGO_PKG_VERSION")).unwrap() } From 435d1b92be73b7ece8e08bb1df7f909c18fb1dc2 Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Thu, 8 Feb 2024 22:50:45 -0300 Subject: [PATCH 09/41] docs: specify atual behavior on required_version docs --- Configurations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Configurations.md b/Configurations.md index 2d01fb3bb3b..039897f7699 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2353,7 +2353,7 @@ Require a specific version of rustfmt. If you want to make sure that the specific version of rustfmt is used in your CI, use this option. - **Default value**: `CARGO_PKG_VERSION` -- **Possible values**: any published version (e.g. `"0.3.8"`) +- **Possible values**: `semver` compliant values, such as defined on [semver.org](https://semver.org/). Multiple values can be used, they should be separeted by a comma, e.g.: `1.0.0, 1.1.0`. By default, values without comparison operators are treated as `^` (caret) version requirements. If you want to specify an exact version, use the operator `=`, e.g.: `=1.0.0`. - **Stable**: No (tracking issue: [#3386](https://github.com/rust-lang/rustfmt/issues/3386)) ## `short_array_element_width_threshold` From cbb6f62141be263eaa324cb6cf4e8a3b919dec44 Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Fri, 9 Feb 2024 11:18:26 -0300 Subject: [PATCH 10/41] refactor: avoid unwrap and nesting --- src/config/mod.rs | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index 3030b756f1d..994915776f6 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -216,23 +216,25 @@ impl PartialConfig { impl Config { pub(crate) fn version_meets_requirement(&self) -> bool { if self.was_set().required_version() { - let version = semver::Version::parse(env!("CARGO_PKG_VERSION")).unwrap(); - let required_version = semver::VersionReq::parse(self.required_version().as_str()); - - match required_version { - Ok(required_version) => { - if !required_version.matches(&version) { - eprintln!( - "Error: rustfmt version ({}) doesn't match the required version ({})", - version, required_version - ); - return false; - } - } + let Ok(version) = semver::Version::parse(env!("CARGO_PKG_VERSION")) else { + return false; + }; + + let required_version = match semver::VersionReq::parse(self.required_version().as_str()) + { + Ok(required_version) => required_version, Err(e) => { eprintln!("Error: failed to parse required version: {}", e); return false; } + }; + + if !required_version.matches(&version) { + eprintln!( + "Error: rustfmt version ({}) doesn't match the required version ({})", + version, required_version + ); + return false; } } From 93a34005405a37e8e478694906c184818cac790c Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Fri, 9 Feb 2024 11:23:30 -0300 Subject: [PATCH 11/41] refactor: extract semver version checking --- src/config/mod.rs | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index 994915776f6..695e979a2ac 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -213,23 +213,30 @@ impl PartialConfig { } } +fn check_semver_version(version: &str, required: &str) -> bool { + let required = match semver::VersionReq::parse(required) { + Ok(r) => r, + Err(e) => { + eprintln!("Error: failed to parse required version: {}", e); + return false; + } + }; + let version = match semver::Version::parse(version) { + Ok(v) => v, + Err(e) => { + eprintln!("Error: failed to parse current version: {}", e); + return false; + } + }; + required.matches(&version) +} + impl Config { pub(crate) fn version_meets_requirement(&self) -> bool { if self.was_set().required_version() { - let Ok(version) = semver::Version::parse(env!("CARGO_PKG_VERSION")) else { - return false; - }; - - let required_version = match semver::VersionReq::parse(self.required_version().as_str()) - { - Ok(required_version) => required_version, - Err(e) => { - eprintln!("Error: failed to parse required version: {}", e); - return false; - } - }; - - if !required_version.matches(&version) { + let version = env!("CARGO_PKG_VERSION"); + let required_version = self.required_version(); + if !check_semver_version(version, &required_version) { eprintln!( "Error: rustfmt version ({}) doesn't match the required version ({})", version, required_version From 1fc86bc181a64137ddeaec9b0daf8052eb6bebd1 Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Fri, 9 Feb 2024 11:37:26 -0300 Subject: [PATCH 12/41] test: extensively test `chek_semver_version` --- src/config/mod.rs | 127 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 124 insertions(+), 3 deletions(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index 695e979a2ac..ba080d10c87 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -213,7 +213,7 @@ impl PartialConfig { } } -fn check_semver_version(version: &str, required: &str) -> bool { +fn check_semver_version(required: &str, actual: &str) -> bool { let required = match semver::VersionReq::parse(required) { Ok(r) => r, Err(e) => { @@ -221,7 +221,7 @@ fn check_semver_version(version: &str, required: &str) -> bool { return false; } }; - let version = match semver::Version::parse(version) { + let version = match semver::Version::parse(actual) { Ok(v) => v, Err(e) => { eprintln!("Error: failed to parse current version: {}", e); @@ -236,7 +236,7 @@ impl Config { if self.was_set().required_version() { let version = env!("CARGO_PKG_VERSION"); let required_version = self.required_version(); - if !check_semver_version(version, &required_version) { + if !check_semver_version(&required_version, version) { eprintln!( "Error: rustfmt version ({}) doesn't match the required version ({})", version, required_version @@ -1296,4 +1296,125 @@ make_backup = false assert!(!config.version_meets_requirement()); } } + + #[cfg(test)] + mod check_semver_version { + use super::*; + + #[test] + fn test_exact_version_match() { + assert!(check_semver_version("1.0.0", "1.0.0")); + } + + #[test] + fn test_version_mismatch() { + assert!(!check_semver_version("2.0.0", "1.0.0")); + } + + #[test] + fn test_patch_version_greater() { + assert!(check_semver_version("1.0.0", "1.0.1")); + } + + #[test] + fn test_minor_version_greater() { + assert!(check_semver_version("1.0.0", "1.1.0")); + } + + #[test] + fn test_major_version_less() { + assert!(!check_semver_version("1.0.0", "0.9.0")); + } + + #[test] + fn test_prerelease_less_than_release() { + assert!(!check_semver_version("1.0.0", "1.0.0-alpha")); + } + + #[test] + fn test_prerelease_version_specific_match() { + assert!(check_semver_version("1.0.0-alpha", "1.0.0-alpha")); + } + + #[test] + fn test_build_metadata_ignored() { + assert!(check_semver_version("1.0.0", "1.0.0+build.1")); + } + + #[test] + fn test_greater_than_requirement() { + assert!(check_semver_version(">1.0.0", "1.1.0")); + } + + #[test] + fn test_less_than_requirement_fails_when_greater() { + assert!(!check_semver_version("<1.0.0", "1.1.0")); + } + + #[test] + fn test_caret_requirement_matches_minor_update() { + assert!(check_semver_version("^1.1.0", "1.2.0")); + } + + #[test] + fn test_tilde_requirement_matches_patch_update() { + assert!(check_semver_version("~1.0.0", "1.0.1")); + } + + #[test] + fn test_range_requirement_inclusive() { + assert!(check_semver_version(">=1.0.0, <2.0.0", "1.5.0")); + } + + #[test] + fn test_pre_release_specific_match() { + assert!(check_semver_version("1.0.0-alpha.1", "1.0.0-alpha.1")); + } + + #[test] + fn test_pre_release_non_match_when_requiring_release() { + assert!(!check_semver_version("1.0.0", "1.0.0-alpha.1")); + } + + #[test] + fn test_wildcard_match_minor() { + assert!(check_semver_version("1.*", "1.1.0")); + } + + #[test] + fn test_wildcard_match_major() { + assert!(check_semver_version("2.*", "2.0.0")); + } + + #[test] + fn test_invalid_inputs() { + assert!(!check_semver_version("not.a.requirement", "1.0.0")); + assert!(!check_semver_version("1.0.0", "not.a.version")); + } + + #[test] + fn test_version_with_pre_release_and_build() { + assert!(check_semver_version("1.0.0-alpha", "1.0.0-alpha+001")); + } + + // Demonstrates precedence of numeric identifiers over alphanumeric in pre-releases + #[test] + fn test_pre_release_numeric_vs_alphanumeric() { + assert!(!check_semver_version("1.0.0-alpha.beta", "1.0.0-alpha.1")); + assert!(check_semver_version("1.0.0-alpha.1", "1.0.0-alpha.beta")); + } + + // Demonstrates lexicographic ordering of alphanumeric identifiers in pre-releases + #[test] + fn test_pre_release_lexicographic_ordering() { + assert!(check_semver_version( + "1.0.0-alpha.alpha", + "1.0.0-alpha.beta", + )); + assert!(!check_semver_version( + "1.0.0-alpha.beta", + "1.0.0-alpha.alpha", + )); + } + } } From 005ecb903b1c07ae953b1e26a4293a1ff2ecfdfe Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Fri, 9 Feb 2024 11:39:56 -0300 Subject: [PATCH 13/41] docs: add examples on configurations --- Configurations.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Configurations.md b/Configurations.md index 039897f7699..68bfd9106a5 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2353,7 +2353,11 @@ Require a specific version of rustfmt. If you want to make sure that the specific version of rustfmt is used in your CI, use this option. - **Default value**: `CARGO_PKG_VERSION` -- **Possible values**: `semver` compliant values, such as defined on [semver.org](https://semver.org/). Multiple values can be used, they should be separeted by a comma, e.g.: `1.0.0, 1.1.0`. By default, values without comparison operators are treated as `^` (caret) version requirements. If you want to specify an exact version, use the operator `=`, e.g.: `=1.0.0`. +- **Possible values**: `semver` compliant values, such as defined on [semver.org](https://semver.org/). + - Multiple values can be used, they should be separeted by a comma, e.g.: `1.0.0, 1.1.0`. + - By default, values without comparison operators are treated as `^` (caret) version requirements. + - If you want to specify an exact version, use the operator `=`, e.g.: `=1.0.0`. + - If you want to specify a range, use the operator `, `, e.g.: `1.0.0, <2.0.0`. This is the same as `^1.0.0, <2.0.0` or `^1.0.0`. - **Stable**: No (tracking issue: [#3386](https://github.com/rust-lang/rustfmt/issues/3386)) ## `short_array_element_width_threshold` From 012f71b6929c932d502cc2f7991f4d76578e4d83 Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Fri, 9 Feb 2024 11:42:14 -0300 Subject: [PATCH 14/41] docs: explain multiple values --- Configurations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Configurations.md b/Configurations.md index 68bfd9106a5..d90c1af650e 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2354,7 +2354,7 @@ specific version of rustfmt is used in your CI, use this option. - **Default value**: `CARGO_PKG_VERSION` - **Possible values**: `semver` compliant values, such as defined on [semver.org](https://semver.org/). - - Multiple values can be used, they should be separeted by a comma, e.g.: `1.0.0, 1.1.0`. + - Multiple values can be used, they should be separated by a comma, e.g.: `1.0.0, 1.1.0`. The comparison is done using the `&&` operator, so all versions from a given range must be satisfied. - By default, values without comparison operators are treated as `^` (caret) version requirements. - If you want to specify an exact version, use the operator `=`, e.g.: `=1.0.0`. - If you want to specify a range, use the operator `, `, e.g.: `1.0.0, <2.0.0`. This is the same as `^1.0.0, <2.0.0` or `^1.0.0`. From da749b656b4213aaed17236034094cb58cfef9d7 Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Fri, 9 Feb 2024 11:44:01 -0300 Subject: [PATCH 15/41] docs: mention `semver::Version` spec --- Configurations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Configurations.md b/Configurations.md index d90c1af650e..8f0d3aa7247 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2353,7 +2353,7 @@ Require a specific version of rustfmt. If you want to make sure that the specific version of rustfmt is used in your CI, use this option. - **Default value**: `CARGO_PKG_VERSION` -- **Possible values**: `semver` compliant values, such as defined on [semver.org](https://semver.org/). +- **Possible values**: `semver` compliant values, such as defined on [semver.org](https://semver.org/) and following the following [`semver::Version`](https://docs.rs/semver/latest/semver/struct.Version.html#total-ordering) specification. - Multiple values can be used, they should be separated by a comma, e.g.: `1.0.0, 1.1.0`. The comparison is done using the `&&` operator, so all versions from a given range must be satisfied. - By default, values without comparison operators are treated as `^` (caret) version requirements. - If you want to specify an exact version, use the operator `=`, e.g.: `=1.0.0`. From 18e692d22c9b399d458b603a0b3cae2b152cb30e Mon Sep 17 00:00:00 2001 From: ologbonowiwi <100464352+ologbonowiwi@users.noreply.github.com> Date: Fri, 9 Feb 2024 13:56:15 -0300 Subject: [PATCH 16/41] docs: delete `semver::Version` from spec Co-authored-by: Yacin Tmimi --- Configurations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Configurations.md b/Configurations.md index 8f0d3aa7247..d90c1af650e 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2353,7 +2353,7 @@ Require a specific version of rustfmt. If you want to make sure that the specific version of rustfmt is used in your CI, use this option. - **Default value**: `CARGO_PKG_VERSION` -- **Possible values**: `semver` compliant values, such as defined on [semver.org](https://semver.org/) and following the following [`semver::Version`](https://docs.rs/semver/latest/semver/struct.Version.html#total-ordering) specification. +- **Possible values**: `semver` compliant values, such as defined on [semver.org](https://semver.org/). - Multiple values can be used, they should be separated by a comma, e.g.: `1.0.0, 1.1.0`. The comparison is done using the `&&` operator, so all versions from a given range must be satisfied. - By default, values without comparison operators are treated as `^` (caret) version requirements. - If you want to specify an exact version, use the operator `=`, e.g.: `=1.0.0`. From dd1d8ce9ec19a41025a6d5bcd5e0c5b0fcf66b38 Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Fri, 9 Feb 2024 14:03:36 -0300 Subject: [PATCH 17/41] docs: add examples of different settings --- Configurations.md | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/Configurations.md b/Configurations.md index 8f0d3aa7247..0cb8e0cd280 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2353,13 +2353,27 @@ Require a specific version of rustfmt. If you want to make sure that the specific version of rustfmt is used in your CI, use this option. - **Default value**: `CARGO_PKG_VERSION` -- **Possible values**: `semver` compliant values, such as defined on [semver.org](https://semver.org/) and following the following [`semver::Version`](https://docs.rs/semver/latest/semver/struct.Version.html#total-ordering) specification. - - Multiple values can be used, they should be separated by a comma, e.g.: `1.0.0, 1.1.0`. The comparison is done using the `&&` operator, so all versions from a given range must be satisfied. - - By default, values without comparison operators are treated as `^` (caret) version requirements. - - If you want to specify an exact version, use the operator `=`, e.g.: `=1.0.0`. - - If you want to specify a range, use the operator `, `, e.g.: `1.0.0, <2.0.0`. This is the same as `^1.0.0, <2.0.0` or `^1.0.0`. +- **Possible values**: `semver` compliant values, such as defined on [semver.org](https://semver.org/). - **Stable**: No (tracking issue: [#3386](https://github.com/rust-lang/rustfmt/issues/3386)) +#### `CARGO_PKG_VERSION` (default): + +```toml +required_version="1.0.0" +``` + +#### Multiple values: + +```toml +required_version="1.0.0, 1.1.0" +``` + +#### Multiple values with comparison operators: + +```toml +required_version="^1.0.0, <2.0.0" +``` + ## `short_array_element_width_threshold` The width threshold for an array element to be considered "short". From 7a92ca55650b54c98222977b497b94bdff6795b4 Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Fri, 9 Feb 2024 14:25:01 -0300 Subject: [PATCH 18/41] feat: enforce exact comparison --- src/config/mod.rs | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index ba080d10c87..bc59c6d9430 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -214,21 +214,30 @@ impl PartialConfig { } fn check_semver_version(required: &str, actual: &str) -> bool { - let required = match semver::VersionReq::parse(required) { + let mut required_version = match semver::VersionReq::parse(required) { Ok(r) => r, Err(e) => { eprintln!("Error: failed to parse required version: {}", e); return false; } }; - let version = match semver::Version::parse(actual) { + let actual_version = match semver::Version::parse(actual) { Ok(v) => v, Err(e) => { eprintln!("Error: failed to parse current version: {}", e); return false; } }; - required.matches(&version) + + required.split(',').enumerate().for_each(|(i, required)| { + if let Some(comparator) = required_version.comparators.get_mut(i) { + if !required.contains("^") && comparator.op == semver::Op::Caret { + comparator.op = semver::Op::Exact; + }; + }; + }); + + required_version.matches(&actual_version) } impl Config { @@ -1203,7 +1212,7 @@ make_backup = false #[test] fn test_required_version_pre_release() { let toml = format!( - "required_version=\"{}-alpha\"", + "required_version=\"^{}-alpha\"", get_current_version().to_string() ); let config = Config::from_toml(&toml, Path::new("")).unwrap(); @@ -1304,6 +1313,11 @@ make_backup = false #[test] fn test_exact_version_match() { assert!(check_semver_version("1.0.0", "1.0.0")); + assert!(!check_semver_version("1.0.0", "1.1.0")); + assert!(!check_semver_version("1.0.0", "1.0.1")); + assert!(!check_semver_version("1.0.0", "2.1.0")); + assert!(!check_semver_version("1.0.0", "0.1.0")); + assert!(!check_semver_version("1.0.0", "0.0.1")); } #[test] @@ -1313,12 +1327,12 @@ make_backup = false #[test] fn test_patch_version_greater() { - assert!(check_semver_version("1.0.0", "1.0.1")); + assert!(check_semver_version("^1.0.0", "1.0.1")); } #[test] fn test_minor_version_greater() { - assert!(check_semver_version("1.0.0", "1.1.0")); + assert!(check_semver_version("^1.0.0", "1.1.0")); } #[test] @@ -1400,19 +1414,19 @@ make_backup = false // Demonstrates precedence of numeric identifiers over alphanumeric in pre-releases #[test] fn test_pre_release_numeric_vs_alphanumeric() { - assert!(!check_semver_version("1.0.0-alpha.beta", "1.0.0-alpha.1")); - assert!(check_semver_version("1.0.0-alpha.1", "1.0.0-alpha.beta")); + assert!(!check_semver_version("^1.0.0-alpha.beta", "1.0.0-alpha.1")); + assert!(check_semver_version("^1.0.0-alpha.1", "1.0.0-alpha.beta")); } // Demonstrates lexicographic ordering of alphanumeric identifiers in pre-releases #[test] fn test_pre_release_lexicographic_ordering() { assert!(check_semver_version( - "1.0.0-alpha.alpha", + "^1.0.0-alpha.alpha", "1.0.0-alpha.beta", )); assert!(!check_semver_version( - "1.0.0-alpha.beta", + "^1.0.0-alpha.beta", "1.0.0-alpha.alpha", )); } From a7a04c7725600be91f3b8e5f4a47d602b59e15d6 Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Fri, 9 Feb 2024 14:30:34 -0300 Subject: [PATCH 19/41] docs: add more examples of operators --- Configurations.md | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/Configurations.md b/Configurations.md index 0cb8e0cd280..20b8ceef5f3 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2358,20 +2358,42 @@ specific version of rustfmt is used in your CI, use this option. #### `CARGO_PKG_VERSION` (default): +Match on exact version + ```toml required_version="1.0.0" ``` -#### Multiple values: +#### Higher or equal to: + +```toml +required_version=">=1.0.0" +``` + +#### Lower or equal to: ```toml -required_version="1.0.0, 1.1.0" +required_version="<=1.0.0" ``` -#### Multiple values with comparison operators: +#### New minor or patch versions + +```toml +required_version="^1.0.0" +``` + +#### New patch versions + +```toml +required_version="~1.0.0" +``` + +#### Multiple values: +Any of the semver operators can be combined, being split with commas. The comparison is done using `&&` operator. + ```toml -required_version="^1.0.0, <2.0.0" +required_version=">=1.0.0, <2.0.0" ``` ## `short_array_element_width_threshold` From aceb3fd1789e858015028acf7511ffd2da95693f Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Fri, 9 Feb 2024 14:37:59 -0300 Subject: [PATCH 20/41] docs: improve formatting --- Configurations.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Configurations.md b/Configurations.md index 20b8ceef5f3..ec4e1f32a39 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2376,19 +2376,19 @@ required_version=">=1.0.0" required_version="<=1.0.0" ``` -#### New minor or patch versions +#### New minor or patch versions: ```toml required_version="^1.0.0" ``` -#### New patch versions +#### New patch versions: ```toml required_version="~1.0.0" ``` -#### Multiple values: +#### Multiple versions to match: Any of the semver operators can be combined, being split with commas. The comparison is done using `&&` operator. From 7fe12d1dec6505ef9eda80c382b2247abae09ddd Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Fri, 9 Feb 2024 14:50:13 -0300 Subject: [PATCH 21/41] refactor: early return `None` to avoid cognitive load --- src/config/mod.rs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index bc59c6d9430..938fe08b3a1 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -230,11 +230,13 @@ fn check_semver_version(required: &str, actual: &str) -> bool { }; required.split(',').enumerate().for_each(|(i, required)| { - if let Some(comparator) = required_version.comparators.get_mut(i) { - if !required.contains("^") && comparator.op == semver::Op::Caret { - comparator.op = semver::Op::Exact; - }; + let Some(comparator) = required_version.comparators.get_mut(i) else { + return; }; + + if !required.contains('^') && comparator.op == semver::Op::Caret { + comparator.op = semver::Op::Exact; + } }); required_version.matches(&actual_version) @@ -1393,6 +1395,7 @@ make_backup = false #[test] fn test_wildcard_match_minor() { assert!(check_semver_version("1.*", "1.1.0")); + assert!(check_semver_version("1.*, <2.0.0", "1.1.0")); } #[test] @@ -1400,6 +1403,11 @@ make_backup = false assert!(check_semver_version("2.*", "2.0.0")); } + #[test] + fn test_wildcard_match_patch() { + assert!(check_semver_version("1.0.*", "1.0.1")); + } + #[test] fn test_invalid_inputs() { assert!(!check_semver_version("not.a.requirement", "1.0.0")); @@ -1430,5 +1438,12 @@ make_backup = false "1.0.0-alpha.alpha", )); } + + // These are not allowed. '*' can't be used with other version specifiers. + #[test] + fn test_wildcard_any_with_range() { + assert!(!check_semver_version("*, <2.0.0", "1.0.0")); + assert!(!check_semver_version("*, 1.0.0", "1.5.0")); + } } } From 9142de43f6b257d2d41a402a85e11af55518cc2a Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Fri, 9 Feb 2024 14:50:25 -0300 Subject: [PATCH 22/41] docs: add `*` to examples --- Configurations.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Configurations.md b/Configurations.md index ec4e1f32a39..09e59ce53af 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2388,9 +2388,19 @@ required_version="^1.0.0" required_version="~1.0.0" ``` +#### Wildcard: + +```toml +required_version="*" # matches any version. +required_version="1.*" # matches any version with the same major version +required_version="1.0.*" # matches any version with the same major and minor version +``` + #### Multiple versions to match: Any of the semver operators can be combined, being split with commas. The comparison is done using `&&` operator. + +Can be used with any operator except by `*`, as any comparator will override the `*` operator. If you use `*` with other comparators, the comparison will fail. ```toml required_version=">=1.0.0, <2.0.0" From 899d843bfcda38c6305cccb1f6ba0827dd6b7830 Mon Sep 17 00:00:00 2001 From: ologbonowiwi <100464352+ologbonowiwi@users.noreply.github.com> Date: Fri, 9 Feb 2024 17:21:36 -0300 Subject: [PATCH 23/41] docs: clarify `*` restrictions on range --- Configurations.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Configurations.md b/Configurations.md index 09e59ce53af..dd1b93e94af 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2398,9 +2398,9 @@ required_version="1.0.*" # matches any version with the same major and minor ver #### Multiple versions to match: -Any of the semver operators can be combined, being split with commas. The comparison is done using `&&` operator. +Except by `*`, can be used by any of the semver operators can be combined, being split with commas. The comparison is done using `&&` operator. -Can be used with any operator except by `*`, as any comparator will override the `*` operator. If you use `*` with other comparators, the comparison will fail. +`*` has range restrictions as any comparator will override the wildcard operator. When `*` is used within a range, the comparison always fails. ```toml required_version=">=1.0.0, <2.0.0" From 5b6592d8db05f9efbecf1844b37c71ab2fc7e16b Mon Sep 17 00:00:00 2001 From: ologbonowiwi <100464352+ologbonowiwi@users.noreply.github.com> Date: Fri, 16 Feb 2024 22:35:46 -0300 Subject: [PATCH 24/41] Update Configurations.md Co-authored-by: Yacin Tmimi --- Configurations.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Configurations.md b/Configurations.md index dd1b93e94af..01856754ee1 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2356,9 +2356,7 @@ specific version of rustfmt is used in your CI, use this option. - **Possible values**: `semver` compliant values, such as defined on [semver.org](https://semver.org/). - **Stable**: No (tracking issue: [#3386](https://github.com/rust-lang/rustfmt/issues/3386)) -#### `CARGO_PKG_VERSION` (default): - -Match on exact version +#### Match on exact version ```toml required_version="1.0.0" From 60b7e2fddca35f976ca2228b2e652174fb461862 Mon Sep 17 00:00:00 2001 From: ologbonowiwi <100464352+ologbonowiwi@users.noreply.github.com> Date: Sat, 17 Feb 2024 17:00:58 -0300 Subject: [PATCH 25/41] style(docs): add `:` to keep standard Co-authored-by: Yacin Tmimi --- Configurations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Configurations.md b/Configurations.md index 01856754ee1..cf831f444be 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2356,7 +2356,7 @@ specific version of rustfmt is used in your CI, use this option. - **Possible values**: `semver` compliant values, such as defined on [semver.org](https://semver.org/). - **Stable**: No (tracking issue: [#3386](https://github.com/rust-lang/rustfmt/issues/3386)) -#### Match on exact version +#### Match on exact version: ```toml required_version="1.0.0" From ab0ba60728521b02662ba4aae35a04acf2f99ad0 Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Tue, 8 Oct 2024 02:37:16 -0300 Subject: [PATCH 26/41] refactor: improve variable naming --- src/config/mod.rs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index 938fe08b3a1..f78946bb93f 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -213,8 +213,8 @@ impl PartialConfig { } } -fn check_semver_version(required: &str, actual: &str) -> bool { - let mut required_version = match semver::VersionReq::parse(required) { +fn check_semver_version(range_requirement: &str, actual: &str) -> bool { + let mut required_version = match semver::VersionReq::parse(range_requirement) { Ok(r) => r, Err(e) => { eprintln!("Error: failed to parse required version: {}", e); @@ -229,15 +229,18 @@ fn check_semver_version(required: &str, actual: &str) -> bool { } }; - required.split(',').enumerate().for_each(|(i, required)| { - let Some(comparator) = required_version.comparators.get_mut(i) else { - return; - }; + range_requirement + .split(',') + .enumerate() + .for_each(|(i, requirement)| { + let Some(comparator) = required_version.comparators.get_mut(i) else { + return; + }; - if !required.contains('^') && comparator.op == semver::Op::Caret { - comparator.op = semver::Op::Exact; - } - }); + if !requirement.starts_with('^') && comparator.op == semver::Op::Caret { + comparator.op = semver::Op::Exact; + } + }); required_version.matches(&actual_version) } From f096df64709cfe2718afba3492d7a15cbf1ba421 Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Tue, 8 Oct 2024 02:39:42 -0300 Subject: [PATCH 27/41] docs: add comment explaining why we need to check `^` and override the comparator operation --- src/config/mod.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/config/mod.rs b/src/config/mod.rs index f78946bb93f..b5cb5f58b98 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -237,6 +237,10 @@ fn check_semver_version(range_requirement: &str, actual: &str) -> bool { return; }; + // semver crate handles "1.0.0" as "^1.0.0", and we want to treat it as "=1.0.0" + // because of this, we need to iterate over the comparators, and change each one + // that has "default caret operator" to an exact operator + // this condition overrides the "default caret operator" of semver create. if !requirement.starts_with('^') && comparator.op == semver::Op::Caret { comparator.op = semver::Op::Exact; } From c29d067d5a81ed774335f0111673b10a2c62c511 Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Tue, 8 Oct 2024 02:56:50 -0300 Subject: [PATCH 28/41] refactor: use `version_req` instead of `required_version` and `label` instead of `requirement` --- src/config/mod.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index 3141955266d..ddbf1f473bb 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -235,7 +235,7 @@ impl PartialConfig { } fn check_semver_version(range_requirement: &str, actual: &str) -> bool { - let mut required_version = match semver::VersionReq::parse(range_requirement) { + let mut version_req = match semver::VersionReq::parse(range_requirement) { Ok(r) => r, Err(e) => { eprintln!("Error: failed to parse required version: {}", e); @@ -253,8 +253,9 @@ fn check_semver_version(range_requirement: &str, actual: &str) -> bool { range_requirement .split(',') .enumerate() - .for_each(|(i, requirement)| { - let Some(comparator) = required_version.comparators.get_mut(i) else { + .for_each(|(i, label)| { + // the label refers to the current comparator + let Some(comparator) = version_req.comparators.get_mut(i) else { return; }; @@ -262,12 +263,12 @@ fn check_semver_version(range_requirement: &str, actual: &str) -> bool { // because of this, we need to iterate over the comparators, and change each one // that has "default caret operator" to an exact operator // this condition overrides the "default caret operator" of semver create. - if !requirement.starts_with('^') && comparator.op == semver::Op::Caret { + if !label.starts_with('^') && comparator.op == semver::Op::Caret { comparator.op = semver::Op::Exact; } }); - required_version.matches(&actual_version) + version_req.matches(&actual_version) } impl Config { From aa2df658e30a96b10bbe1020d470211870382f55 Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Tue, 8 Oct 2024 03:09:04 -0300 Subject: [PATCH 29/41] test: add `"./rustfmt.toml"` to `Path` --- src/config/mod.rs | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index ddbf1f473bb..19d8498fd75 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1341,7 +1341,7 @@ make_backup = false #[test] fn test_current_required_version() { let toml = format!("required_version=\"{}\"", env!("CARGO_PKG_VERSION")); - let config = Config::from_toml(&toml, Path::new("")).unwrap(); + let config = Config::from_toml(&toml, Path::new("./rustfmt.toml")).unwrap(); assert!(config.version_meets_requirement()); } @@ -1350,7 +1350,7 @@ make_backup = false #[test] fn test_required_version_above() { let toml = "required_version=\"1000.0.0\""; - let config = Config::from_toml(toml, Path::new("")).unwrap(); + let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap(); assert!(!config.version_meets_requirement()); } @@ -1362,7 +1362,7 @@ make_backup = false for version in versions { let toml = format!("required_version=\"{}\"", version.to_string()); - let config = Config::from_toml(&toml, Path::new("")).unwrap(); + let config = Config::from_toml(&toml, Path::new("./rustfmt.toml")).unwrap(); assert!(!config.version_meets_requirement()); } @@ -1372,7 +1372,7 @@ make_backup = false #[test] fn test_required_version_tilde() { let toml = format!("required_version=\"~{}\"", env!("CARGO_PKG_VERSION")); - let config = Config::from_toml(&toml, Path::new("")).unwrap(); + let config = Config::from_toml(&toml, Path::new("./rustfmt.toml")).unwrap(); assert!(config.version_meets_requirement()); } @@ -1388,7 +1388,7 @@ make_backup = false current_version.major.to_string(), minor.to_string() ); - let config = Config::from_toml(&toml, Path::new("")).unwrap(); + let config = Config::from_toml(&toml, Path::new("./rustfmt.toml")).unwrap(); assert!(!config.version_meets_requirement()); } @@ -1398,7 +1398,7 @@ make_backup = false #[test] fn test_required_version_greater_than() { let toml = "required_version=\">1.0.0\""; - let config = Config::from_toml(toml, Path::new("")).unwrap(); + let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap(); assert!(config.version_meets_requirement()); } @@ -1407,7 +1407,7 @@ make_backup = false #[test] fn test_required_version_less_than() { let toml = "required_version=\"<1.0.0\""; - let config = Config::from_toml(toml, Path::new("")).unwrap(); + let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap(); assert!(!config.version_meets_requirement()); } @@ -1422,7 +1422,7 @@ make_backup = false current_version.major, current_version.major + 1 ); - let config = Config::from_toml(&toml, Path::new("")).unwrap(); + let config = Config::from_toml(&toml, Path::new("./rustfmt.toml")).unwrap(); assert!(config.version_meets_requirement()); } @@ -1431,7 +1431,7 @@ make_backup = false #[test] fn test_required_version_exact_boundary() { let toml = format!("required_version=\"{}\"", get_current_version().to_string()); - let config = Config::from_toml(&toml, Path::new("")).unwrap(); + let config = Config::from_toml(&toml, Path::new("./rustfmt.toml")).unwrap(); assert!(config.version_meets_requirement()); } @@ -1443,7 +1443,7 @@ make_backup = false "required_version=\"^{}-alpha\"", get_current_version().to_string() ); - let config = Config::from_toml(&toml, Path::new("")).unwrap(); + let config = Config::from_toml(&toml, Path::new("./rustfmt.toml")).unwrap(); assert!(config.version_meets_requirement()); } @@ -1456,7 +1456,7 @@ make_backup = false get_current_version().to_string() ); - let config = Config::from_toml(&toml, Path::new("")).unwrap(); + let config = Config::from_toml(&toml, Path::new("./rustfmt.toml")).unwrap(); assert!(config.version_meets_requirement()); } @@ -1465,7 +1465,7 @@ make_backup = false #[test] fn test_required_version_invalid_specification() { let toml = "required_version=\"not.a.version\""; - let config = Config::from_toml(toml, Path::new("")).unwrap(); + let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap(); assert!(!config.version_meets_requirement()) } @@ -1482,7 +1482,7 @@ make_backup = false current_version.major, current_version.minor ); - let config = Config::from_toml(&toml, Path::new("")).unwrap(); + let config = Config::from_toml(&toml, Path::new("./rustfmt.toml")).unwrap(); assert!(config.version_meets_requirement()); } @@ -1491,7 +1491,7 @@ make_backup = false #[test] fn test_required_version_wildcard_major() { let toml = "required_version=\"1.x\""; - let config = Config::from_toml(toml, Path::new("")).unwrap(); + let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap(); assert!(config.version_meets_requirement()); } @@ -1500,7 +1500,7 @@ make_backup = false #[test] fn test_required_version_wildcard_any() { let toml = "required_version=\"*\""; - let config = Config::from_toml(toml, Path::new("")).unwrap(); + let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap(); assert!(config.version_meets_requirement()); } @@ -1509,7 +1509,7 @@ make_backup = false #[test] fn test_required_version_major_version_zero() { let toml = "required_version=\"0.1.0\""; - let config = Config::from_toml(toml, Path::new("")).unwrap(); + let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap(); assert!(!config.version_meets_requirement()); } @@ -1518,7 +1518,7 @@ make_backup = false #[test] fn test_required_version_future_major_version() { let toml = "required_version=\"3.0.0\""; - let config = Config::from_toml(toml, Path::new("")).unwrap(); + let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap(); assert!(!config.version_meets_requirement()); } @@ -1528,7 +1528,7 @@ make_backup = false fn test_required_version_fail_different_operator() { // != is not supported let toml = "required_version=\"!=1.0.0\""; - let config = Config::from_toml(toml, Path::new("")).unwrap(); + let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap(); assert!(!config.version_meets_requirement()); } From 2b58939cb5281d5b46b63596715908f0be3c95c1 Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Tue, 8 Oct 2024 15:22:38 -0300 Subject: [PATCH 30/41] test: add wildcard unmatch --- src/config/mod.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/config/mod.rs b/src/config/mod.rs index 19d8498fd75..0c043e3f170 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1624,6 +1624,14 @@ make_backup = false assert!(check_semver_version("1.*, <2.0.0", "1.1.0")); } + #[test] + fn test_wildcard_unmatch() { + assert!(!check_semver_version("1.*, <2.0.0", "2.1.0")); + assert!(!check_semver_version("1.*, <2.0.0", "2.0.0")); + assert!(!check_semver_version("1.*, <2.*", "2.1.0")); + assert!(!check_semver_version("1.*, <2.*", "2.0.0")); + } + #[test] fn test_wildcard_match_major() { assert!(check_semver_version("2.*", "2.0.0")); From 9380e25b87576b9ac0fc533ea734bafdca4bf1e6 Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Tue, 8 Oct 2024 15:28:57 -0300 Subject: [PATCH 31/41] docs: improve multiple versions to match session --- Configurations.md | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/Configurations.md b/Configurations.md index b302c43cbe0..8b4dd0ca157 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2405,10 +2405,16 @@ required_version="1.0.*" # matches any version with the same major and minor ver #### Multiple versions to match: -Except by `*`, can be used by any of the semver operators can be combined, being split with commas. The comparison is done using `&&` operator. +Except by `*`, any of the semver operators can be combined, being split with commas. The comparison is done using `&&` operator. + +`*` can't be used with other comparators. + +Since `*` has range restrictions, any comparator will override the wildcard operator. When `*` is used alone, the comparison always fails, as that's an invalid config. `*, <1.0.0` for example is invalid. + +Versions can't contradict themselves, otherwise they'll always fail. Some examples are `"1.*, <2.0.0"` and `1.0.*, <2.0.0`, since both versions can't be true at the same time. + +By now, `&&` (using comma `,`) is the only way to declare `required_version`. `||` Operator is not valid. Valid examples are `1.0.0, 1.1.0` and `1.0.0, 1.1.0, 1.1.*` but not `1.0.0 || 1.1.0` or `1.0.0, 2.0.0`. -`*` has range restrictions as any comparator will override the wildcard operator. When `*` is used within a range, the comparison always fails. - ```toml required_version=">=1.0.0, <2.0.0" ``` @@ -2740,17 +2746,6 @@ By default this option is set as a percentage of [`max_width`](#max_width) provi See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuristics) -## `style_edition` - -Controls the edition of the [Rust Style Guide] to use for formatting ([RFC 3338]) - -- **Default value**: `"2015"` -- **Possible values**: `"2015"`, `"2018"`, `"2021"`, `"2024"` (unstable variant) -- **Stable**: No - -[Rust Style Guide]: https://doc.rust-lang.org/nightly/style-guide/ -[RFC 3338]: https://rust-lang.github.io/rfcs/3338-style-evolution.html - ## `tab_spaces` Number of spaces per tab @@ -3110,7 +3105,9 @@ fn main() { ## `version` -This option is deprecated and has been replaced by [`style_edition`](#style_edition) +Which version of the formatting rules to use. `Version::One` is backwards-compatible +with Rustfmt 1.0. Other versions are only backwards compatible within a major +version number. - **Default value**: `One` - **Possible values**: `One`, `Two` From 9c4682a34501d283e7eac18b9c26f44554ff3720 Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Tue, 8 Oct 2024 15:33:15 -0300 Subject: [PATCH 32/41] test: add test for invalid usage of `||` --- src/config/mod.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/config/mod.rs b/src/config/mod.rs index 0c043e3f170..41bcaa46606 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1618,6 +1618,15 @@ make_backup = false assert!(!check_semver_version("1.0.0", "1.0.0-alpha.1")); } + // That's not our choice. `semver` does not support `||` operator. + // Only asserting here to ensure this behavior (which match our docs). + #[test] + fn test_invalid_or() { + assert!(!check_semver_version("1.0.0 || 2.0.0", "1.0.0")); + assert!(!check_semver_version("1.0.0 || 2.0.0", "2.0.0")); + assert!(!check_semver_version("1.0.0 || 2.0.0", "3.0.0")); + } + #[test] fn test_wildcard_match_minor() { assert!(check_semver_version("1.*", "1.1.0")); From d649463c26f5f8b1d5207c0f3b301c0e48880ed1 Mon Sep 17 00:00:00 2001 From: ologbonowiwi Date: Thu, 10 Oct 2024 14:47:59 -0300 Subject: [PATCH 33/41] refactor: add versions on error logs Co-authored-by: Yacin Tmimi --- src/config/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index 41bcaa46606..71b13543aaf 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -238,14 +238,14 @@ fn check_semver_version(range_requirement: &str, actual: &str) -> bool { let mut version_req = match semver::VersionReq::parse(range_requirement) { Ok(r) => r, Err(e) => { - eprintln!("Error: failed to parse required version: {}", e); + eprintln!("Error: failed to parse required version {range_requirement:?}: {e}"); return false; } }; let actual_version = match semver::Version::parse(actual) { Ok(v) => v, Err(e) => { - eprintln!("Error: failed to parse current version: {}", e); + eprintln!("Error: failed to parse current version {actual:?}: {e}"); return false; } }; From 82dbefd6660d501469751b776cf2f0936814d236 Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Thu, 10 Oct 2024 14:50:14 -0300 Subject: [PATCH 34/41] test: include more tests for mismatching versions --- src/config/mod.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index 41bcaa46606..3436a744925 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1634,11 +1634,16 @@ make_backup = false } #[test] - fn test_wildcard_unmatch() { + fn test_wildcard_mismatch() { assert!(!check_semver_version("1.*, <2.0.0", "2.1.0")); assert!(!check_semver_version("1.*, <2.0.0", "2.0.0")); assert!(!check_semver_version("1.*, <2.*", "2.1.0")); assert!(!check_semver_version("1.*, <2.*", "2.0.0")); + + assert!(!check_semver_version("1.*, >2.0.0", "1.1.0")); + assert!(!check_semver_version("1.*, >2.0.0", "1.0.0")); + assert!(!check_semver_version("1.*, >2.*", "1.1.0")); + assert!(!check_semver_version("1.*, >2.*", "1.0.0")); } #[test] From fbaddcc672fb1ebcd5f71839e4591cbbbd2a5248 Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Thu, 10 Oct 2024 14:50:27 -0300 Subject: [PATCH 35/41] docs: remove confusing section --- Configurations.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Configurations.md b/Configurations.md index 6f429d4a1c2..42fe1717dcd 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2411,9 +2411,7 @@ Except by `*`, any of the semver operators can be combined, being split with com Since `*` has range restrictions, any comparator will override the wildcard operator. When `*` is used alone, the comparison always fails, as that's an invalid config. `*, <1.0.0` for example is invalid. -Versions can't contradict themselves, otherwise they'll always fail. Some examples are `"1.*, <2.0.0"` and `1.0.*, <2.0.0`, since both versions can't be true at the same time. - -By now, `&&` (using comma `,`) is the only way to declare `required_version`. `||` Operator is not valid. Valid examples are `1.0.0, 1.1.0` and `1.0.0, 1.1.0, 1.1.*` but not `1.0.0 || 1.1.0` or `1.0.0, 2.0.0`. +Versions can't contradict themselves, otherwise they'll always fail. Some examples are `"1.*, >2.0.0"`, `1.0.*, >2.0.0`, since both versions can't be true at the same time. ```toml required_version=">=1.0.0, <2.0.0" From 23cf4879d333716f737b14076d162cd073f21f53 Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Thu, 10 Oct 2024 14:51:43 -0300 Subject: [PATCH 36/41] docs: re-include `style_edition` deleted by merge --- Configurations.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Configurations.md b/Configurations.md index 42fe1717dcd..f4466ea1bbd 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2732,6 +2732,17 @@ By default this option is set as a percentage of [`max_width`](#max_width) provi See also [`max_width`](#max_width), [`use_small_heuristics`](#use_small_heuristics), and [`struct_lit_single_line`](#struct_lit_single_line) +## `style_edition` + +Controls the edition of the [Rust Style Guide] to use for formatting ([RFC 3338]) + +- **Default value**: `"2015"` +- **Possible values**: `"2015"`, `"2018"`, `"2021"`, `"2024"` (unstable variant) +- **Stable**: No + +[Rust Style Guide]: https://doc.rust-lang.org/nightly/style-guide/ +[RFC 3338]: https://rust-lang.github.io/rfcs/3338-style-evolution.html + ## `struct_variant_width` Maximum width in the body of a struct variant before falling back to vertical formatting. A value of `0` (zero) results in struct literals always being broken into multiple lines. Note this occurs when `use_small_heuristics` is set to `Off`. From f5684a71a5170311c9548f6fa360cb113181400e Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Thu, 10 Oct 2024 14:55:13 -0300 Subject: [PATCH 37/41] docs: improve contradicting requirements explanation --- Configurations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Configurations.md b/Configurations.md index f4466ea1bbd..99a542cd08b 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2411,7 +2411,7 @@ Except by `*`, any of the semver operators can be combined, being split with com Since `*` has range restrictions, any comparator will override the wildcard operator. When `*` is used alone, the comparison always fails, as that's an invalid config. `*, <1.0.0` for example is invalid. -Versions can't contradict themselves, otherwise they'll always fail. Some examples are `"1.*, >2.0.0"`, `1.0.*, >2.0.0`, since both versions can't be true at the same time. +Version requirements can't contradict themselves, otherwise they'll always fail. Some examples are `"1.*, >2.0.0"`, `1.0.*, >2.0.0` and `<1.5.0, >1.10.*`, because both requirements can't be true at the same time. ```toml required_version=">=1.0.0, <2.0.0" From fede8d0de367185aa403780b595ca76d10282a52 Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Thu, 10 Oct 2024 14:55:21 -0300 Subject: [PATCH 38/41] test: include more mismatch tests --- src/config/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/config/mod.rs b/src/config/mod.rs index 9409ccdec8e..a51efdb67db 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1644,6 +1644,8 @@ make_backup = false assert!(!check_semver_version("1.*, >2.0.0", "1.0.0")); assert!(!check_semver_version("1.*, >2.*", "1.1.0")); assert!(!check_semver_version("1.*, >2.*", "1.0.0")); + + assert!(!check_semver_version("<1.5.0, >1.10.*", "1.6.0")); } #[test] From 4e15dbfd47e61add21c7b9ade8b6afd1dbbf00bf Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Thu, 10 Oct 2024 15:07:07 -0300 Subject: [PATCH 39/41] docs: revert unnecessary change --- Configurations.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Configurations.md b/Configurations.md index 99a542cd08b..0f636a05c4b 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2732,17 +2732,6 @@ By default this option is set as a percentage of [`max_width`](#max_width) provi See also [`max_width`](#max_width), [`use_small_heuristics`](#use_small_heuristics), and [`struct_lit_single_line`](#struct_lit_single_line) -## `style_edition` - -Controls the edition of the [Rust Style Guide] to use for formatting ([RFC 3338]) - -- **Default value**: `"2015"` -- **Possible values**: `"2015"`, `"2018"`, `"2021"`, `"2024"` (unstable variant) -- **Stable**: No - -[Rust Style Guide]: https://doc.rust-lang.org/nightly/style-guide/ -[RFC 3338]: https://rust-lang.github.io/rfcs/3338-style-evolution.html - ## `struct_variant_width` Maximum width in the body of a struct variant before falling back to vertical formatting. A value of `0` (zero) results in struct literals always being broken into multiple lines. Note this occurs when `use_small_heuristics` is set to `Off`. @@ -2755,6 +2744,17 @@ By default this option is set as a percentage of [`max_width`](#max_width) provi See also [`max_width`](#max_width) and [`use_small_heuristics`](#use_small_heuristics) +## `style_edition` + +Controls the edition of the [Rust Style Guide] to use for formatting ([RFC 3338]) + +- **Default value**: `"2015"` +- **Possible values**: `"2015"`, `"2018"`, `"2021"`, `"2024"` (unstable variant) +- **Stable**: No + +[Rust Style Guide]: https://doc.rust-lang.org/nightly/style-guide/ +[RFC 3338]: https://rust-lang.github.io/rfcs/3338-style-evolution.html + ## `tab_spaces` Number of spaces per tab From e2010406ff31ee9e41eaabca065158d8d1e82eeb Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Tue, 18 Feb 2025 18:36:29 -0300 Subject: [PATCH 40/41] test: include test matching to wildcard matching any version --- src/config/mod.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/config/mod.rs b/src/config/mod.rs index a51efdb67db..a2cbd5a5cd8 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -1676,6 +1676,13 @@ make_backup = false assert!(check_semver_version("^1.0.0-alpha.1", "1.0.0-alpha.beta")); } + // Any version is allowed when * is used + #[test] + fn test_wildcard_any() { + assert!(check_semver_version("*", "1.0.0")); + assert!(check_semver_version("*", "1.0.0+build")); + } + // Demonstrates lexicographic ordering of alphanumeric identifiers in pre-releases #[test] fn test_pre_release_lexicographic_ordering() { From afdef6904fd5e6eaa43f416888d81912871bfd67 Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Tue, 18 Feb 2025 18:38:43 -0300 Subject: [PATCH 41/41] docs: improve multiple versions to match explanation --- Configurations.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Configurations.md b/Configurations.md index 0f636a05c4b..0f8b7ce0185 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2405,13 +2405,14 @@ required_version="1.0.*" # matches any version with the same major and minor ver #### Multiple versions to match: -Except by `*`, any of the semver operators can be combined, being split with commas. The comparison is done using `&&` operator. +A comma separated list of version requirements. +The match succeeds when the current rustfmt version matches all version requirements. -`*` can't be used with other comparators. +The one notable exception is that a wildcard matching any version cannot be used in the list. +For example, `*, <1.0.0` will always fail. -Since `*` has range restrictions, any comparator will override the wildcard operator. When `*` is used alone, the comparison always fails, as that's an invalid config. `*, <1.0.0` for example is invalid. - -Version requirements can't contradict themselves, otherwise they'll always fail. Some examples are `"1.*, >2.0.0"`, `1.0.*, >2.0.0` and `<1.5.0, >1.10.*`, because both requirements can't be true at the same time. +Additionally, the version match will always fail if any of the version requirements contradict themselves. +Some examples of contradictory requirements are `1.*, >2.0.0`, `1.0.*, >2.0.0` and `<1.5.0, >1.10.*`. ```toml required_version=">=1.0.0, <2.0.0"