Skip to content

Commit 185d607

Browse files
scheglovCommit Queue
authored andcommitted
Fine. A/B testing tool.
Introduce a CLI under `pkg/analyzer/tool/fine/ab_mutate` to compare the conservative analyzer pipeline with the fine-grained dependencies pipeline across seeded mutation chains. The tool applies deterministic source mutations, collects diagnostics and timing for each engine, and writes reproducible artifacts. Runs stop on divergence, exception, or when a diagnostics cap is exceeded. Key pieces: - Engines: A = current state with conservative analysis without fine dependencies (not full rebuild without caching, but current coarse-grained incremental); B = incremental with `withFineDependencies`. A/B are warmed once, then performance counters are reset so the first mutation is incremental. - Baseline: compute diagnostics for both engines and require equality before running chains. The per-run diagnostics cap is `max(baseline*1.20, +50)` unless explicitly set. - Mutations (deterministic discovery & selection): - `insert_unit_header_comment` (trivia only) - `rename_local_variable` (declaration + local references in block) - `remove_last_formal_parameter` (robust comma/group handling) - `swap_top_level_functions` (preserves raw text/comments) - `toggle_return_type_nullability` (named return types) - Selection & editing: - Separate `SiteSelector` context for parsed/resolved discovery so A/B stay cold; edits are applied via `OverlayResourceProvider`. - RNG is derived from a SHA-256–based 32-bit seed over structured parts for stable, reproducible picks. Outputs (for reproducibility and analysis): - `manifest.json`, `files.json` (paths + SHA-256), and per-run `run_summary`. - `baseline/diagnostics_{A,B}.json`, performance dumps, and a compare file. - Per step: `mutation.json`, `before.dart`, `after.dart`, `patch.diff`, `diagnostics_{A,B}.json`, `metrics_{A,B}.json`, performance, equality check, and `diverge_details.json` when A≠B. - Chain summary includes end reason, step list, per-kind counts, and p50/p90 speedup (A_time / B_time). API & model additions: - `Mutation`, `MutationEdit`, `MutationResult`, and `HarnessDiagnostic` (normalized with stable `key()`); diagnostics use `ErrorProcessor` and skip `TODO`s. - Utility helpers for file discovery (skips generated/build/tool dirs), JSON writing, deterministic seeding, and speedup stats. Minor supporting change: - Extend `FunctionAstVisitor` with `functionDeclaration` and `methodDeclaration` callbacks and visit overrides. These are used by mutation discovery (e.g., collecting bodies/identifiers for renames). This adds an end-to-end, reproducible A/B harness for measuring correctness and performance impacts of fine-grained dependencies on realistic, evolving code, with minimal intrusion into the analyzer proper. Change-Id: I068138d6aa201ef9a79f884f0f4638d02c796b29 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/446480 Reviewed-by: Johnni Winther <[email protected]> Commit-Queue: Konstantin Shcheglov <[email protected]>
1 parent 6ce015e commit 185d607

16 files changed

+1810
-0
lines changed

pkg/analyzer/lib/src/test_utilities/function_ast_visitor.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@ class FunctionAstVisitor extends RecursiveAstVisitor<void> {
1313
final void Function(DeclaredVariablePattern)? declaredVariablePattern;
1414
final void Function(ForElement)? forElement;
1515
final void Function(ForStatement)? forStatement;
16+
final void Function(FunctionDeclaration)? functionDeclaration;
1617
final void Function(FunctionDeclarationStatement)?
1718
functionDeclarationStatement;
1819
final void Function(FunctionExpression, bool)? functionExpression;
1920
final void Function(GuardedPattern)? guardedPattern;
2021
final void Function(IfElement)? ifElement;
2122
final void Function(IfStatement)? ifStatement;
2223
final void Function(Label)? label;
24+
final void Function(MethodDeclaration)? methodDeclaration;
2325
final void Function(MethodInvocation)? methodInvocation;
2426
final void Function(PatternAssignment)? patternAssignment;
2527
final void Function(PatternVariableDeclaration)? patternVariableDeclaration;
@@ -39,12 +41,14 @@ class FunctionAstVisitor extends RecursiveAstVisitor<void> {
3941
this.declaredVariablePattern,
4042
this.forElement,
4143
this.forStatement,
44+
this.functionDeclaration,
4245
this.functionDeclarationStatement,
4346
this.functionExpression,
4447
this.guardedPattern,
4548
this.ifElement,
4649
this.ifStatement,
4750
this.label,
51+
this.methodDeclaration,
4852
this.methodInvocation,
4953
this.patternAssignment,
5054
this.patternVariableDeclaration,
@@ -99,6 +103,12 @@ class FunctionAstVisitor extends RecursiveAstVisitor<void> {
99103
super.visitForStatement(node);
100104
}
101105

106+
@override
107+
void visitFunctionDeclaration(FunctionDeclaration node) {
108+
functionDeclaration?.call(node);
109+
super.visitFunctionDeclaration(node);
110+
}
111+
102112
@override
103113
void visitFunctionDeclarationStatement(FunctionDeclarationStatement node) {
104114
if (functionDeclarationStatement != null) {
@@ -144,6 +154,12 @@ class FunctionAstVisitor extends RecursiveAstVisitor<void> {
144154
super.visitLabel(node);
145155
}
146156

157+
@override
158+
void visitMethodDeclaration(MethodDeclaration node) {
159+
methodDeclaration?.call(node);
160+
super.visitMethodDeclaration(node);
161+
}
162+
147163
@override
148164
void visitMethodInvocation(MethodInvocation node) {
149165
if (methodInvocation != null) {

0 commit comments

Comments
 (0)