From 75a5ee285aba5b8974dd7b4475c58fc66cfb9a06 Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Sat, 22 Aug 2026 16:57:34 -0300 Subject: [PATCH 1/2] Use caret (^) as the default required_version comparator required_version currently treats a bare version (no comparator prefix) as an exact match, overriding the semver crate's own default of a caret requirement. This diverges from how Cargo treats dependency version requirements with no operator. Remove the override so a bare version behaves like `^version`, matching Cargo's convention. The old exact-match behavior is still available by opting in explicitly with `=`. Closes #6729 --- CHANGELOG.md | 3 +++ Configurations.md | 10 +++++++++- src/config/mod.rs | 44 ++++++++++++++++++++++---------------------- 3 files changed, 34 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b6d9a3b8dc..1e82c3521b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## [Unreleased] +### Changed +- The unstable `required_version` configuration option now defaults to a caret (`^`) semver requirement instead of an exact match when no comparator is given, matching how Cargo treats dependency version requirements. Use the explicit `=` operator (e.g. `required_version = "=1.2.3"`) to opt back into exact matching [#6729](https://github.com/rust-lang/rustfmt/issues/6729) + ## [1.10.0] 2026-07-21 diff --git a/Configurations.md b/Configurations.md index 189389fe143..01e6cb2b3dc 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2465,14 +2465,22 @@ 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/). + A value with no comparator, such as `"1.0.0"`, defaults to a caret (`^`) + requirement, matching how Cargo treats dependency version requirements. - **Stable**: No (tracking issue: [#3386](https://github.com/rust-lang/rustfmt/issues/3386)) -#### Match on exact version: +#### Default matching (new minor or patch versions): ```toml required_version="1.0.0" ``` +#### Match on exact version: + +```toml +required_version="=1.0.0" +``` + #### Higher or equal to: ```toml diff --git a/src/config/mod.rs b/src/config/mod.rs index a3f9842cd4f..148a9bdb980 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -242,7 +242,11 @@ impl PartialConfig { } fn check_semver_version(range_requirement: &str, actual: &str) -> bool { - let mut version_req = match semver::VersionReq::parse(range_requirement) { + // A bare version (no comparator prefix) is left as `semver`'s own default, + // which is a caret requirement, matching how Cargo treats dependency version + // requirements. Callers who want the old exact-match behavior can opt in + // explicitly with the `=` operator, e.g. `required_version = "=1.2.3"`. + let version_req = match semver::VersionReq::parse(range_requirement) { Ok(r) => r, Err(e) => { eprintln!("Error: failed to parse required version {range_requirement:?}: {e}"); @@ -257,24 +261,6 @@ fn check_semver_version(range_requirement: &str, actual: &str) -> bool { } }; - range_requirement - .split(',') - .enumerate() - .for_each(|(i, label)| { - // the label refers to the current comparator - let Some(comparator) = version_req.comparators.get_mut(i) else { - 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 !label.starts_with('^') && comparator.op == semver::Op::Caret { - comparator.op = semver::Op::Exact; - } - }); - version_req.matches(&actual_version) } @@ -1552,15 +1538,29 @@ make_backup = false use super::*; #[test] - fn test_exact_version_match() { + fn test_default_caret_match() { + // A bare version (no operator) defaults to a caret requirement, + // matching how Cargo treats dependency version requirements. 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", "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] + fn test_explicit_exact_match() { + // The old exact-match default is still available by opting in + // with the `=` operator. + 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] fn test_version_mismatch() { assert!(!check_semver_version("2.0.0", "1.0.0")); From 68d1b9069e67fbd759daf5c9012c571fb4498219 Mon Sep 17 00:00:00 2001 From: Wesley Matos Date: Sat, 22 Aug 2026 20:32:03 -0300 Subject: [PATCH 2/2] docs: simplify caret-default comments and docs per review Drop the now-unnecessary old-behavior explanations from the check_semver_version comment and the test_explicit_exact_match test, and combine the Configurations.md caret-default note with an equivalent bare-version example. --- Configurations.md | 8 +++++++- src/config/mod.rs | 5 +---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Configurations.md b/Configurations.md index 01e6cb2b3dc..f0773dd1500 100644 --- a/Configurations.md +++ b/Configurations.md @@ -2469,7 +2469,13 @@ specific version of rustfmt is used in your CI, use this option. requirement, matching how Cargo treats dependency version requirements. - **Stable**: No (tracking issue: [#3386](https://github.com/rust-lang/rustfmt/issues/3386)) -#### Default matching (new minor or patch versions): +#### New minor or patch versions (default behavior): + +```toml +required_version="^1.0.0" +``` + +This is the default behavior, so that is equivalent to: ```toml required_version="1.0.0" diff --git a/src/config/mod.rs b/src/config/mod.rs index 148a9bdb980..96aac3c7549 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -244,8 +244,7 @@ impl PartialConfig { fn check_semver_version(range_requirement: &str, actual: &str) -> bool { // A bare version (no comparator prefix) is left as `semver`'s own default, // which is a caret requirement, matching how Cargo treats dependency version - // requirements. Callers who want the old exact-match behavior can opt in - // explicitly with the `=` operator, e.g. `required_version = "=1.2.3"`. + // requirements. let version_req = match semver::VersionReq::parse(range_requirement) { Ok(r) => r, Err(e) => { @@ -1551,8 +1550,6 @@ make_backup = false #[test] fn test_explicit_exact_match() { - // The old exact-match default is still available by opting in - // with the `=` operator. 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"));