diff --git a/crates/ocs_plugin_api/src/version_info.rs b/crates/ocs_plugin_api/src/version_info.rs index 4222205cb..f358d17da 100644 --- a/crates/ocs_plugin_api/src/version_info.rs +++ b/crates/ocs_plugin_api/src/version_info.rs @@ -73,15 +73,20 @@ pub fn host_rustc_version() -> &'static str { /// Returns whether two `rustc --version` outputs are considered compatible. /// /// The full version line (e.g. "rustc 1.98.0 (hash date)") is compared -/// verbatim after normalising whitespace. This is intentionally strict: Rust -/// has no stable ABI, so even the same nominal compiler built in different +/// token-by-token after normalising whitespace. This is intentionally strict: +/// Rust has no stable ABI, so even the same nominal compiler built in different /// environments can produce incompatible layouts. pub fn rustc_versions_compatible(a: &str, b: &str) -> bool { if a.is_empty() || b.is_empty() { return false; } - a.split_whitespace().collect::() - .eq_ignore_ascii_case(&b.split_whitespace().collect::()) + let a_tokens: Vec<&str> = a.split_whitespace().collect(); + let b_tokens: Vec<&str> = b.split_whitespace().collect(); + a_tokens.len() == b_tokens.len() + && a_tokens + .iter() + .zip(b_tokens) + .all(|(a, b)| a.eq_ignore_ascii_case(b)) } /// Extracts the full git commit hash from a Cargo source. @@ -187,6 +192,13 @@ mod tests { assert!(!rustc_versions_compatible("", "")); } + #[test] + fn rustc_version_comparison_rejects_collapsed_tokens() { + // A concatenated version must not accidentally match a spaced one. + assert!(!rustc_versions_compatible("rustc 1.98.0", "rustc1.98.0")); + assert!(!rustc_versions_compatible("rustc1.98.0", "rustc 1.98.0")); + } + #[test] fn dependency_gate_starts_at_api_v4() { assert!(!uses_acadrust_gate(3));