Skip to content

Commit f9ee73d

Browse files
authored
perf: Speed up TOML parsing by upgrading toml (#15736)
### What does this PR try to resolve? For numbers, see https://epage.github.io/blog/2025/07/toml-09/ Further areas for improvement: - Enable `fast_hash` (see #15649) - Only track spans for local manifests, allowing us to skip the `make_owned` call for most packages ### How to test and review this PR?
2 parents 8f80388 + 194fe22 commit f9ee73d

File tree

21 files changed

+175
-126
lines changed

21 files changed

+175
-126
lines changed

Cargo.lock

Lines changed: 75 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ tar = { version = "0.4.44", default-features = false }
105105
tempfile = "3.20.0"
106106
thiserror = "2.0.12"
107107
time = { version = "0.3.41", features = ["parsing", "formatting", "serde"] }
108-
toml = { version = "0.8.23", default-features = false }
109-
toml_edit = { version = "0.22.27", features = ["serde"] }
108+
toml = { version = "0.9.0", default-features = false }
109+
toml_edit = { version = "0.23.0", features = ["serde"] }
110110
tracing = { version = "0.1.41", default-features = false, features = ["std"] } # be compatible with rustc_log: https://github.com/rust-lang/rust/blob/e51e98dde6a/compiler/rustc_log/Cargo.toml#L9
111111
tracing-chrome = "0.7.2"
112112
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
@@ -211,7 +211,7 @@ tar.workspace = true
211211
tempfile.workspace = true
212212
thiserror.workspace = true
213213
time.workspace = true
214-
toml = { workspace = true, features = ["display", "parse"] }
214+
toml = { workspace = true, features = ["std", "serde", "parse", "display", "preserve_order"] }
215215
toml_edit.workspace = true
216216
tracing = { workspace = true, features = ["attributes"] }
217217
tracing-subscriber.workspace = true

benches/capture/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ publish = false
1010
cargo_metadata.workspace = true
1111
flate2.workspace = true
1212
tar.workspace = true
13-
toml = { workspace = true, features = ["display", "parse"] }
13+
toml = { workspace = true, features = ["display", "parse", "serde"] }
1414

1515
[lints]
1616
workspace = true

crates/cargo-test-support/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ serde_json.workspace = true
2727
snapbox.workspace = true
2828
tar.workspace = true
2929
time.workspace = true
30-
toml = { workspace = true, features = ["display"] }
30+
toml = { workspace = true, features = ["display", "serde"] }
3131
url.workspace = true
3232
walkdir.workspace = true
3333

crates/cargo-util-schemas/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ serde_json = { workspace = true, optional = true }
1616
serde-untagged.workspace = true
1717
serde-value.workspace = true
1818
thiserror.workspace = true
19-
toml.workspace = true
19+
toml = { workspace = true, features = ["serde"] }
2020
unicode-xid.workspace = true
2121
url.workspace = true
2222

src/cargo/core/manifest.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ impl EitherManifest {
6363
pub struct Manifest {
6464
// alternate forms of manifests:
6565
contents: Rc<String>,
66-
document: Rc<toml_edit::ImDocument<String>>,
66+
document: Rc<toml::Spanned<toml::de::DeTable<'static>>>,
6767
original_toml: Rc<TomlManifest>,
6868
normalized_toml: Rc<TomlManifest>,
6969
summary: Summary,
@@ -109,7 +109,7 @@ pub struct Warnings(Vec<DelayedWarning>);
109109
pub struct VirtualManifest {
110110
// alternate forms of manifests:
111111
contents: Rc<String>,
112-
document: Rc<toml_edit::ImDocument<String>>,
112+
document: Rc<toml::Spanned<toml::de::DeTable<'static>>>,
113113
original_toml: Rc<TomlManifest>,
114114
normalized_toml: Rc<TomlManifest>,
115115

@@ -496,7 +496,7 @@ compact_debug! {
496496
impl Manifest {
497497
pub fn new(
498498
contents: Rc<String>,
499-
document: Rc<toml_edit::ImDocument<String>>,
499+
document: Rc<toml::Spanned<toml::de::DeTable<'static>>>,
500500
original_toml: Rc<TomlManifest>,
501501
normalized_toml: Rc<TomlManifest>,
502502
summary: Summary,
@@ -565,7 +565,7 @@ impl Manifest {
565565
Ok(format!("{}\n{}", MANIFEST_PREAMBLE, toml))
566566
}
567567
/// Collection of spans for the original TOML
568-
pub fn document(&self) -> &toml_edit::ImDocument<String> {
568+
pub fn document(&self) -> &toml::Spanned<toml::de::DeTable<'static>> {
569569
&self.document
570570
}
571571
/// The [`TomlManifest`] as parsed from [`Manifest::document`]
@@ -738,7 +738,7 @@ impl Manifest {
738738
impl VirtualManifest {
739739
pub fn new(
740740
contents: Rc<String>,
741-
document: Rc<toml_edit::ImDocument<String>>,
741+
document: Rc<toml::Spanned<toml::de::DeTable<'static>>>,
742742
original_toml: Rc<TomlManifest>,
743743
normalized_toml: Rc<TomlManifest>,
744744
replace: Vec<(PackageIdSpec, Dependency)>,
@@ -766,7 +766,7 @@ impl VirtualManifest {
766766
self.contents.as_str()
767767
}
768768
/// Collection of spans for the original TOML
769-
pub fn document(&self) -> &toml_edit::ImDocument<String> {
769+
pub fn document(&self) -> &toml::Spanned<toml::de::DeTable<'static>> {
770770
&self.document
771771
}
772772
/// The [`TomlManifest`] as parsed from [`VirtualManifest::document`]

src/cargo/util/context/mod.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,10 +1043,9 @@ impl GlobalContext {
10431043
let def = Definition::Environment(key.as_env_key().to_string());
10441044
if self.cli_unstable().advanced_env && env_val.starts_with('[') && env_val.ends_with(']') {
10451045
// Parse an environment string as a TOML array.
1046-
let toml_v = toml::Value::deserialize(toml::de::ValueDeserializer::new(&env_val))
1047-
.map_err(|e| {
1048-
ConfigError::new(format!("could not parse TOML list: {}", e), def.clone())
1049-
})?;
1046+
let toml_v = env_val.parse::<toml::Value>().map_err(|e| {
1047+
ConfigError::new(format!("could not parse TOML list: {}", e), def.clone())
1048+
})?;
10501049
let values = toml_v.as_array().expect("env var was not array");
10511050
for value in values {
10521051
// TODO: support other types.

src/cargo/util/lints.rs

Lines changed: 19 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ use pathdiff::diff_paths;
66
use std::fmt::Display;
77
use std::ops::Range;
88
use std::path::Path;
9-
use toml_edit::ImDocument;
109

1110
const LINT_GROUPS: &[LintGroup] = &[TEST_DUMMY_UNSTABLE];
1211
pub const LINTS: &[Lint] = &[IM_A_TEAPOT, UNKNOWN_LINTS];
@@ -16,7 +15,7 @@ pub fn analyze_cargo_lints_table(
1615
path: &Path,
1716
pkg_lints: &TomlToolLints,
1817
ws_contents: &str,
19-
ws_document: &ImDocument<String>,
18+
ws_document: &toml::Spanned<toml::de::DeTable<'static>>,
2019
ws_path: &Path,
2120
gctx: &GlobalContext,
2221
) -> CargoResult<()> {
@@ -116,7 +115,7 @@ fn verify_feature_enabled(
116115
manifest: &Manifest,
117116
manifest_path: &str,
118117
ws_contents: &str,
119-
ws_document: &ImDocument<String>,
118+
ws_document: &toml::Spanned<toml::de::DeTable<'static>>,
120119
ws_path: &str,
121120
error_count: &mut usize,
122121
gctx: &GlobalContext,
@@ -191,43 +190,33 @@ fn verify_feature_enabled(
191190
}
192191

193192
pub fn get_span(
194-
document: &ImDocument<String>,
193+
document: &toml::Spanned<toml::de::DeTable<'static>>,
195194
path: &[&str],
196195
get_value: bool,
197196
) -> Option<Range<usize>> {
198-
let mut table = document.as_item().as_table_like()?;
197+
let mut table = document.get_ref();
199198
let mut iter = path.into_iter().peekable();
200199
while let Some(key) = iter.next() {
201-
let (key, item) = table.get_key_value(key)?;
200+
let key_s: &str = key.as_ref();
201+
let (key, item) = table.get_key_value(key_s)?;
202202
if iter.peek().is_none() {
203203
return if get_value {
204-
item.span()
204+
Some(item.span())
205205
} else {
206-
let leaf_decor = key.dotted_decor();
207-
let leaf_prefix_span = leaf_decor.prefix().and_then(|p| p.span());
208-
let leaf_suffix_span = leaf_decor.suffix().and_then(|s| s.span());
209-
if let (Some(leaf_prefix_span), Some(leaf_suffix_span)) =
210-
(leaf_prefix_span, leaf_suffix_span)
211-
{
212-
Some(leaf_prefix_span.start..leaf_suffix_span.end)
213-
} else {
214-
key.span()
215-
}
206+
Some(key.span())
216207
};
217208
}
218-
if item.is_table_like() {
219-
table = item.as_table_like().unwrap();
209+
if let Some(next_table) = item.get_ref().as_table() {
210+
table = next_table;
220211
}
221-
if item.is_array() && iter.peek().is_some() {
222-
let array = item.as_array().unwrap();
223-
let next = iter.next().unwrap();
224-
return array.iter().find_map(|item| {
225-
if next == &item.to_string() {
226-
item.span()
227-
} else {
228-
None
229-
}
230-
});
212+
if iter.peek().is_some() {
213+
if let Some(array) = item.get_ref().as_array() {
214+
let next = iter.next().unwrap();
215+
return array.iter().find_map(|item| match item.get_ref() {
216+
toml::de::DeValue::String(s) if s == next => Some(item.span()),
217+
_ => None,
218+
});
219+
}
231220
}
232221
}
233222
None
@@ -511,7 +500,7 @@ fn output_unknown_lints(
511500
manifest_path: &str,
512501
pkg_lints: &TomlToolLints,
513502
ws_contents: &str,
514-
ws_document: &ImDocument<String>,
503+
ws_document: &toml::Spanned<toml::de::DeTable<'static>>,
515504
ws_path: &str,
516505
error_count: &mut usize,
517506
gctx: &GlobalContext,

0 commit comments

Comments
 (0)