Sfoglia il codice sorgente

docs(playbook): record C/C++ validation (inheritance fix + override synthesis)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Colby McHenry 1 mese fa
parent
commit
e355cb9c07
1 ha cambiato i file con 17 aggiunte e 1 eliminazioni
  1. 17 1
      docs/design/dynamic-dispatch-coverage-playbook.md

+ 17 - 1
docs/design/dynamic-dispatch-coverage-playbook.md

@@ -191,7 +191,7 @@ Status legend: ✅ done+validated · 🔬 hole identified · ⬜ not started.
 | Ruby | Rails / Sinatra | request → routes.rb → Controller#action → model | R | ✅ **RESTful `resources`/`resource` routing → controller#action** (realworld S 16 / spree M / forem L), pluralization + only/except + claimsReference; explicit routes fixed to precise `controller#action` too. 🔬 ActiveRecord dynamic finders (`Article.find_by_slug`) — metaprogramming frontier |
 | PHP | Laravel | request → route → controller → Eloquent | R | ✅ **precise `Route::get([Ctrl::class,'m'])` / `'Ctrl@m'` → Ctrl@method** (realworld S / firefly M / bookstack L) — was resolving the bare method name to the WRONG controller (every `index`→ArticleController); Route::resource→controller. 🔬 Eloquent dynamic finders/relationships (metaprogramming frontier) |
 | PHP | Drupal | request → *.routing.yml → _controller/_form | R | ✅ **`claimsReference` for FQCN handlers** (`\Drupal\…\Class::method` passed the pre-filter only because the `::method` name was known; bare `_form` FQCNs `\…\FormClass` and single-colon `Class:method` controller-services were dropped before resolve()) + **single-colon controller match** + **detect via composer `type:drupal-*` / `name:drupal/*` + `*.info.yml` fallback** (a contrib module with empty `require` was undetected → 0 routes). admin_toolbar S **0→14 (14/14)** / webform M 208 (**144**) / core L 836 (536→**731, 87%**). Remainder is the **entity-annotation handler frontier** (`_entity_form: type.op` resolves via the entity's PHP `#[ContentEntityType]` handlers, not a direct class). 🔬 **OOP `#[Hook]` attributes** — Drupal 11 moved ~all procedural hooks to attribute methods (core: 418 `#[Hook]` files vs 3 procedural), so the resolver's docblock/`module_hook` detection is obsolete for modern core (0 hook edges) |
-| C/C++ | (callback structs / vtables) | function-pointer dispatch | ? | ⬜ |
+| C/C++ | C++ vtables / inheritance | virtual call → override; general direct dispatch | S + X | ✅ **general dispatch strong** (redis C **29k** cross-file calls / leveldb C++ **1.4k**) + **C++ inheritance extraction fix** (`base_class_clause` was unhandled, so C++ extends edges were missing — leveldb **219→298**) + **cpp-override synthesizer** (base virtual method → subclass override, gated to C++, capped — leveldb 12 precise: `Iterator::Next→MergingIterator`). 🔬 C callback structs (`s->fn()` → 422-way fan-out, too noisy to synthesize) + C++ pure-virtual base methods (`virtual void f()=0;` declarations aren't extracted as nodes, so those overrides can't bridge) |
 | Dart | Flutter | setState → build; build → child widgets | S + X | ✅ **setState→build synthesizer** (Dart analog of react-render: a State method whose body calls `setState(` → `build`) gated to `.dart` + **foundational Dart method-range fix** — Dart models a method body as a *sibling* of the signature, so method nodes were signature-only (`end==start`); now `endLine` spans the body (required for ALL body analysis: callees, context slices, the synthesizer's body scan). counter `initState→build`, books `build→BookDetail/BookForm`; widget composition already static (compass_app `build→ErrorIndicator/HomeButton`). Controls unchanged (excalidraw 9,290 / django 302 — the range fix only extends sibling-body grammars). 🔬 MVVM Command/ChangeNotifier dispatch (compass_app — no setState) + `Navigator.push(MaterialPageRoute(builder:))` nav routes |
 | Lua / Luau | Neovim / Roblox | module dispatch (require→mod, mod.fn); event/callback | — | ✅ **already covered for the dominant flow (measure-first, no code change)** — Neovim is module-heavy (`require('x')` + `x.fn()`), and the general import + name resolution already handles it: telescope.nvim **220 imports + 335 cross-file `mod.fn` calls**, traces end-to-end (`map_entries ← init.lua → get_current_picker (state.lua)`). Luau instance-path `require(game:GetService(...))` handled by the extractor. 🔬 event-callback registration (`vim.keymap.set(…, fn)`, autocmd `callback=`, Roblox `signal:Connect(fn)`) is predominantly INLINE anonymous closures (corpus ~12 inline vs ~2 named) — the anonymous-handler frontier; named handlers too rare to justify a synthesizer |
 | Scala | Play / Akka | request → conf/routes → controller action | R + X | ✅ **Play `conf/routes` → controller** — the extensionless `conf/routes` wasn't indexed; added narrow file-walk opt-in (`isPlayRoutesFile`) + a Play resolver parsing `METHOD /path Controller.action(args)` → the action method (computer-database **0→8, 7/8**; starter 0→4, 3/4 — the unresolved are Play's framework `Assets` controller, external). Scala general controller→DAO dispatch already resolves. No-regression: the file-walk change only ADDS Play routes files (excalidraw 9,290 / suite 800 unchanged). 🔬 SIRD programmatic router (`-> /v1 Router` include + `case GET(p"/x")` in code) + Akka actor `receive`/`Behaviors.receiveMessage` message→handler |
@@ -490,6 +490,22 @@ Status legend: ✅ done+validated · 🔬 hole identified · ⬜ not started.
   **Residuals (frontier):** Play SIRD programmatic routers (`-> /v1 v1.PostRouter` include + `case GET(p"/x")`
   in a Router class — rest-api-example) and Akka actor message→handler (`receive { case Msg => … }` /
   `Behaviors.receiveMessage` — untyped, a synthesizer shape).
+- **C / C++ (validated 2026-05-23, redis C / leveldb C++) — general dispatch works; a C++ inheritance fix + override bridge.**
+  Measure-first: C/C++ DIRECT dispatch is excellent out of the box (redis **29,464 cross-file call edges**,
+  leveldb **1,462**) — the bulk of the value. The dynamic-dispatch frontier is two shapes: (1) C callback
+  structs (`struct {.proc=fn}` + `cmd->proc()`) — but in redis the `proc` field fans out to **422** command
+  functions, far too noisy to synthesize precisely, so deliberately skipped (per "partial coverage worse than
+  none"). (2) C++ vtables (`iter->Next()` → the subclass override). The override link was blocked upstream:
+  `extractInheritance` handled `base_clause` (PHP) but not C++'s `base_class_clause`, so C++ `extends` edges
+  were missing/partial (leveldb 219→**298** after the fix). Added a `cpp-override` synthesizer channel (the C++
+  analog of react-render): for each `extends` edge, link each base method → the subclass method of the same
+  name, so trace/callees from the interface method reach the implementation. leveldb **12 precise edges**
+  (`Iterator::Next/Seek/Prev → MergingIterator`), 0 on C (redis) and TS (excalidraw — gated to C++); the C++
+  override integration test passes. **Residual (frontier):** pure-virtual base methods (`virtual void Next() =
+  0;`) are declarations the extractor doesn't emit as nodes, so overrides of a purely-abstract interface can't
+  be bridged (only bases with a real method node — an inline default or non-pure virtual); plus the C
+  callback-struct fan-out. Relied on deterministic validation (no A/B): the cross-file-call counts + precise
+  override spot-check are conclusive.
 - **Difficulty gradient is real:** named-ref dispatch (resolver) is cheap; anonymous
   callback dispatch (synthesizer) is medium; **anonymous-arrow handlers are the hard
   remaining gap** (no identity → need synthesizer link-through-body, not yet built).