agent-joule/crates/edit_prediction/benches/ts_error_count.rs
David Charlot 4e1eb7ab71 Clear the rest of the workspace lint debt
Continues the earlier pass. Each sweep can only check crates whose
dependencies compiled, so every round reached deeper and uncovered
another layer: 16, 35, 7, 12, 15, 69, 31, 1, then a long tail. The spikes
are large crates unblocking — `project` in one round, the `editor` tree in
another — not new breakage.

Nearly all of it applied from rustc's own machine-applicable spans.
Judgement was needed in a few places: prost's `from_i32` becomes
`try_from(..).ok()` at 24 sites, written that way because every caller
wanted an `Option` and `.ok()` keeps the semantics identical; a
paren-matching rewrite rather than a regex, since the argument is often
itself a call. `criterion::black_box` moved to `std::hint`, `rand`'s
`choose_multiple` is `sample`, and `time::format_description::parse` is
`parse_borrowed::<2>`.

Two are deliberately marked rather than changed. `SettingsInputField`'s
four builder methods really are unused — the other `with_id` hits in the
tree are `window.with_id` — so they carry `#[allow(dead_code)]` and a note
rather than being deleted out of a fork. `open_ai`'s large `Err` variant
keeps its scoped allow for the reason given there.

One lint was ours: the panel had a manual checked division, now
`checked_div`.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-30 17:13:47 -04:00

455 lines
14 KiB
Rust

use std::sync::Arc;
use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
use std::hint::black_box;
use edit_prediction::metrics::count_tree_sitter_errors;
use fs::FakeFs;
use gpui::{AppContext as _, TestAppContext};
use language::{Buffer, BufferSnapshot, LanguageRegistry};
use languages::init as init_languages;
use node_runtime::NodeRuntime;
use settings::SettingsStore;
struct ParsedCase {
label: String,
bytes: usize,
error_count: usize,
snapshot: BufferSnapshot,
}
fn replace_nth_occurrences(
source: &mut String,
needle: &str,
replacement: &str,
every: usize,
max_replacements: usize,
) {
let mut rebuilt = String::with_capacity(source.len());
let mut cursor = 0;
let mut seen = 0;
let mut replaced = 0;
while let Some(relative_index) = source[cursor..].find(needle) {
let start = cursor + relative_index;
let end = start + needle.len();
rebuilt.push_str(&source[cursor..start]);
if seen % every == 0 && replaced < max_replacements {
rebuilt.push_str(replacement);
replaced += 1;
} else {
rebuilt.push_str(needle);
}
seen += 1;
cursor = end;
}
rebuilt.push_str(&source[cursor..]);
*source = rebuilt;
}
fn rust_source(function_count: usize) -> String {
let mut source = String::from(
"pub struct Counter {\n value: usize,\n}\n\nimpl Counter {\n pub fn new() -> Self {\n Self { value: 0 }\n }\n}\n\n",
);
for index in 0..function_count {
source.push_str(&format!(
"pub fn compute_value_{index}(input: usize) -> usize {{\n let mut total = input;\n for offset in 0..32 {{\n total += offset + {index};\n }}\n if total % 2 == 0 {{\n total / 2\n }} else {{\n total * 3 + 1\n }}\n}}\n\n"
));
}
source
}
fn rust_source_with_errors(function_count: usize) -> String {
let mut source = rust_source(function_count);
replace_nth_occurrences(
&mut source,
" if total % 2 == 0 {\n",
" if total % 2 == 0 \n",
17,
48,
);
source
}
fn python_source(function_count: usize) -> String {
let mut source = String::from(
"class Counter:\n def __init__(self) -> None:\n self.value = 0\n\n\n",
);
for index in 0..function_count {
source.push_str(&format!(
"def compute_value_{index}(input_value: int) -> int:\n total = input_value\n for offset in range(32):\n total += offset + {index}\n if total % 2 == 0:\n return total // 2\n return total * 3 + 1\n\n"
));
}
source
}
fn python_source_with_errors(function_count: usize) -> String {
let mut source = python_source(function_count);
replace_nth_occurrences(
&mut source,
" if total % 2 == 0:\n",
" if total % 2 == 0\n",
19,
48,
);
source
}
fn go_source(function_count: usize) -> String {
let mut source = String::from(
"package bench\n\ntype Counter struct {\n\tvalue int\n}\n\nfunc NewCounter() Counter {\n\treturn Counter{value: 0}\n}\n\n",
);
for index in 0..function_count {
source.push_str(&format!(
"func ComputeValue{index}(inputValue int) int {{\n\ttotal := inputValue\n\tfor offset := 0; offset < 32; offset++ {{\n\t\ttotal += offset + {index}\n\t}}\n\tif total%2 == 0 {{\n\t\treturn total / 2\n\t}}\n\treturn total*3 + 1\n}}\n\n"
));
}
source
}
fn go_source_with_errors(function_count: usize) -> String {
let mut source = go_source(function_count);
replace_nth_occurrences(
&mut source,
"\tfor offset := 0; offset < 32; offset++ {\n",
"\tfor offset := 0; offset < 32; offset++ \n",
17,
48,
);
source
}
fn typescript_source(function_count: usize) -> String {
let mut source = String::from(
"export type Counter = { value: number };\n\nexport function newCounter(): Counter {\n return { value: 0 };\n}\n\n",
);
for index in 0..function_count {
source.push_str(&format!(
"export function computeValue{index}(inputValue: number): number {{\n let total = inputValue;\n for (let offset = 0; offset < 32; offset += 1) {{\n total += offset + {index};\n }}\n return total % 2 === 0 ? total / 2 : total * 3 + 1;\n}}\n\n"
));
}
source
}
fn typescript_source_with_errors(function_count: usize) -> String {
let mut source = typescript_source(function_count);
replace_nth_occurrences(
&mut source,
" return total % 2 === 0 ? total / 2 : total * 3 + 1;\n",
" return total % 2 === 0 ? total / 2 : ;\n",
17,
64,
);
source
}
fn tsx_source(component_count: usize) -> String {
let mut source = String::from(
"type ItemProps = { index: number; label: string };\n\nfunction Item({ index, label }: ItemProps) {\n return <li data-index={index}>{label}</li>;\n}\n\nexport function App() {\n return <section><ul>{[0, 1, 2].map((value) => <Item key={value} index={value} label={`item-${value}`} />)}</ul></section>;\n}\n\n",
);
for index in 0..component_count {
source.push_str(&format!(
"export function Widget{index}(): JSX.Element {{\n const items = Array.from({{ length: 16 }}, (_, value) => value + {index});\n return (\n <div className=\"widget-{index}\">\n <h2>Widget {index}</h2>\n <ul>\n {{items.map((value) => (\n <Item key={{value}} index={{value}} label={{`widget-{index}-${{value}}`}} />\n ))}}\n </ul>\n </div>\n );\n}}\n\n"
));
}
source
}
fn tsx_source_with_errors(component_count: usize) -> String {
let mut source = tsx_source(component_count);
replace_nth_occurrences(
&mut source,
" const items = Array.from({ length: 16 }, (_, value) => value + ",
" const items = Array.from({ length: 16 }, (_, value) => ); // ",
11,
32,
);
source
}
fn json_source(object_count: usize) -> String {
let mut source = String::from("{\n \"items\": [\n");
for index in 0..object_count {
let suffix = if index + 1 == object_count { "" } else { "," };
source.push_str(&format!(
" {{\n \"id\": {index},\n \"name\": \"item-{index}\",\n \"enabled\": true,\n \"tags\": [\"alpha\", \"beta\", \"gamma\"],\n \"metrics\": {{ \"count\": {}, \"ratio\": {} }}\n }}{suffix}\n",
index * 3 + 1,
index as f64 / 10.0,
));
}
source.push_str(" ]\n}\n");
source
}
fn json_source_with_errors(object_count: usize) -> String {
let mut source = json_source(object_count);
replace_nth_occurrences(
&mut source,
" \"enabled\": true,\n",
" \"enabled\": ,\n",
23,
64,
);
source
}
fn yaml_source(document_count: usize) -> String {
let mut source = String::new();
for index in 0..document_count {
source.push_str(&format!(
"- id: {index}\n name: item-{index}\n enabled: true\n tags:\n - alpha\n - beta\n - gamma\n metrics:\n count: {}\n ratio: {}\n",
index * 3 + 1,
index as f64 / 10.0,
));
}
source
}
fn yaml_source_with_errors(document_count: usize) -> String {
let mut source = yaml_source(document_count);
replace_nth_occurrences(&mut source, " count: ", " count ", 23, 64);
source
}
fn css_source(rule_count: usize) -> String {
let mut source = String::new();
for index in 0..rule_count {
source.push_str(&format!(
".widget-{index} {{\n display: grid;\n grid-template-columns: repeat(4, minmax(0, 1fr));\n gap: 12px;\n padding: 8px;\n color: rgb({}, {}, {});\n}}\n\n.widget-{index} > .item-{index} {{\n border: 1px solid rgba(0, 0, 0, 0.15);\n background: linear-gradient(90deg, #fff, #eef);\n}}\n\n",
(index * 17) % 255,
(index * 31) % 255,
(index * 47) % 255,
));
}
source
}
fn css_source_with_errors(rule_count: usize) -> String {
let mut source = css_source(rule_count);
replace_nth_occurrences(&mut source, " gap: 12px;\n", " gap 12px;\n", 29, 64);
source
}
fn build_case(
context: &mut TestAppContext,
languages: &Arc<LanguageRegistry>,
language_name: &'static str,
variant_name: &'static str,
source: String,
expect_errors: bool,
) -> ParsedCase {
let language_task = context.background_spawn({
let languages = languages.clone();
async move { languages.language_for_name(language_name).await }
});
while !language_task.is_ready() {
context.run_until_parked();
}
let language = futures::executor::block_on(language_task)
.unwrap_or_else(|error| panic!("failed to load {language_name}: {error}"));
let buffer = context.new(|cx| Buffer::local(source, cx).with_language(language, cx));
context.run_until_parked();
while buffer.read_with(context, |buffer, _| buffer.is_parsing()) {
context.run_until_parked();
}
let snapshot = buffer.read_with(context, |buffer, _| buffer.snapshot());
let full_range = 0..snapshot.text.len();
let error_count = count_tree_sitter_errors(snapshot.syntax_layers());
if expect_errors {
assert!(
error_count > 0,
"expected tree-sitter errors for {language_name}/{variant_name}",
);
} else {
assert_eq!(
error_count, 0,
"expected no tree-sitter errors for {language_name}/{variant_name}",
);
}
let label = format!(
"{}/{}_{}kb_{}e",
language_name.to_lowercase(),
variant_name,
full_range.end / 1024,
error_count,
);
ParsedCase {
label,
bytes: full_range.end,
error_count,
snapshot,
}
}
fn parsed_cases() -> Vec<ParsedCase> {
let mut context = TestAppContext::single();
context.update(|cx| {
let settings_store = SettingsStore::test(cx);
cx.set_global(settings_store);
});
let languages = Arc::new(LanguageRegistry::new(context.executor()));
let fs = FakeFs::new(context.executor());
let node_runtime = NodeRuntime::unavailable();
context.update(|cx| init_languages(languages.clone(), fs, node_runtime, cx));
vec![
build_case(
&mut context,
&languages,
"Rust",
"valid",
rust_source(900),
false,
),
build_case(
&mut context,
&languages,
"Rust",
"error_heavy",
rust_source_with_errors(900),
true,
),
build_case(
&mut context,
&languages,
"Python",
"valid",
python_source(1100),
false,
),
build_case(
&mut context,
&languages,
"Python",
"error_heavy",
python_source_with_errors(1100),
true,
),
build_case(
&mut context,
&languages,
"Go",
"valid",
go_source(1000),
false,
),
build_case(
&mut context,
&languages,
"Go",
"error_heavy",
go_source_with_errors(1000),
true,
),
build_case(
&mut context,
&languages,
"TypeScript",
"valid",
typescript_source(1000),
false,
),
build_case(
&mut context,
&languages,
"TypeScript",
"error_heavy",
typescript_source_with_errors(1000),
true,
),
build_case(
&mut context,
&languages,
"TSX",
"valid",
tsx_source(350),
false,
),
build_case(
&mut context,
&languages,
"TSX",
"error_heavy",
tsx_source_with_errors(350),
true,
),
build_case(
&mut context,
&languages,
"JSON",
"valid",
json_source(2200),
false,
),
build_case(
&mut context,
&languages,
"JSON",
"error_heavy",
json_source_with_errors(2200),
true,
),
build_case(
&mut context,
&languages,
"YAML",
"valid",
yaml_source(2200),
false,
),
build_case(
&mut context,
&languages,
"YAML",
"error_heavy",
yaml_source_with_errors(2200),
true,
),
build_case(
&mut context,
&languages,
"CSS",
"valid",
css_source(2400),
false,
),
build_case(
&mut context,
&languages,
"CSS",
"error_heavy",
css_source_with_errors(2400),
true,
),
]
}
fn ts_error_count_benchmark(c: &mut Criterion) {
let cases = parsed_cases();
let mut group = c.benchmark_group("ts_error_count/full_file");
for case in &cases {
group.bench_with_input(
BenchmarkId::from_parameter(&case.label),
case,
|bench, case| {
bench.iter(|| {
black_box(case.bytes);
black_box(case.error_count);
black_box(count_tree_sitter_errors(case.snapshot.syntax_layers()))
});
},
);
}
group.finish();
}
criterion_group!(benches, ts_error_count_benchmark);
criterion_main!(benches);