Files
codegraph/__tests__
Colby MchenryandClaude Opus 5.5 a2870563d7 feat(drupal): services, plugin definitions and #[Hook] methods are part of the graph (#3118)
Drupal wires most of a module by id, attribute and folder, so services,
plugins and OOP hooks were missing from the graph, and a call through the
service container stopped at `\Drupal::service()`.

Built on PR #1077, the stronger base, with what's sound in PR #301:
- *.services.yml: a component node per service, linked to its class and
  to the services it is given, in every place Symfony takes one
  (arguments, calls, factory, parent, decorates, aliases), class-named
  services included. Event subscribers are services and come with it.
- A call on what the container hands out, `\Drupal::service('x')->m()` or
  `$container->get('x')->m()`, links to the method on that service's
  class, its own or inherited. Neither PR had this; it's what connects
  the services to a flow.
- Plugins: an attribute or docblock annotation with an id, on a class
  under src/Plugin, src/Entity or src/Element, read by folder as Drupal's
  discovery does, rather than by the allowlists in both PRs. Those miss
  every contrib type: in farmOS and Commerce, LogType, AssetType,
  CommerceCondition and the like are a large share of the definitions.
- `#[Hook('x')]` on a method, stacked, or on a class naming a method or
  meaning __invoke, from PR #301, reading the method's id from the
  attribute's line. Tree-sitter counts attributes as part of the method,
  so PR #301's id, taken from the `function` line, attached to no node.

Left behind from PR #301: guessing a plugin's base class by name, which
fell back to any class containing the type's name; matching a hook to
any method of that name; and a stray CODEGRAPH_MCP_TRACE change to the
proxy.

Two things the A/B caught:
- Named by bare id, as in PR #1077, a plugin or service answered for the
  class of that name: `new Currency()` landed on the plugin `Currency`,
  and `use Drupal\x\Y;` on the service `Drupal\x\Y`. Nodes are now
  `service:<id>` and `<Type>:<id>`, which no symbol can be, and search
  still finds them by id.
- A FQCN resolved by its last segment, so `use Drupal\commerce_price\Price`
  also linked to the unrelated Element\Price. The namespace now decides
  first, and services use it alone.

Validation:
- drupal-services.test.ts, 25 tests, on the kernel and the wasm path; 21
  fail with main's resolver. drupal.test.ts is unchanged and passes.
- A/B on main cd04b21a: drupal/drupal 555acb12 (a 1,599-file subtree of
  10 modules and core/lib), farmOS 95f2c4d8, Commerce 643bb396, GraphQL
  a1dc10f8, Islandora a518aea2. Edge classes: `+references framework`
  service to class or service, and plugin to class; `+calls framework`
  through the container (192, 19, 61, 0, 5); `+references exact-match`
  #[Hook] method to the hook (58, 47, 0, 0, 0); `-imports framework` (326,
  80, 111, 0, 0). No other edge moves.
- Site by site: every container call read against its line, 277 right;
  every #[Hook] edge, 105 right, no hook reference dangling; every
  service-to-class edge matches the id's namespace, 395 right; every
  plugin reaches its class in its own file, 718 right.
- All 517 removed import edges pointed at the wrong class, and the class
  the `use` names is linked after.
- Plugin files with no node are a class-constant id, tests, and helpers.
- Flow: commerce_cart_cron -> Cron::run, which main can't connect.
- Controls flask 49943b5a and gin 0f09c3a9 are byte-identical.
- Index CPU, median of 3 interleaved, with a suite running: commerce
  -2.5%, farmOS -0.9%, flask -5.9%, gin +2.7%: noise.

Left out: an id given as a class constant; `getSubscribedEvents()` to the
methods it names; and PHP interface dispatch, which is why a call on an
injected `FooInterface` still ends at the interface: the implementer pass
doesn't run for PHP, and that's a language-level change of its own.

Reimplements PR #1077 by @mikedamoiseau and the OOP hooks of PR #301 by
@marcelovani.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-30 18:53:26 -05:00
..