You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -504,7 +504,7 @@ Each extractor is detected by characteristic project files and then parses what
504
504
| Svelte | tree-sitter | `package.json` with `svelte` dependency, or `svelte.config.js/ts/mjs` / `@sveltejs/kit` for SvelteKit |
505
505
| Swift | tree-sitter | `Package.swift`, `.xcodeproj`, or `.xcworkspace` present |
506
506
| Ruby | tree-sitter | `Gemfile` present |
507
-
| C++ | tree-sitter | a C++ source (`.cpp`/`.cc`/`.cxx`/`.hpp`/...) present, or a build file (`CMakeLists.txt`/`Makefile`/`meson.build`/`*.vcxproj`) plus any header |
507
+
| C/C++ | tree-sitter | a C source (`.c`) or C++ source (`.cpp`/`.cc`/`.cxx`/`.hpp`/...) present, or a build file (`CMakeLists.txt`/`Makefile`/`meson.build`/`*.vcxproj`) plus any header |
508
508
| PHP | tree-sitter | `composer.json`, a WordPress bootstrap file (`wp-load.php`/`wp-settings.php`/`wp-config.php`), or any `.php` source within 3 directory levels |
509
509
| OpenAPI | YAML/JSON scanner| any file containing `openapi:` or `swagger:` |
510
510
@@ -530,7 +530,7 @@ Each extractor is detected by characteristic project files and then parses what
530
530
531
531
**Ruby** is parsed with tree-sitter, replacing the former line-based regex scanner — the grammar handles heredocs, endless methods (`def x = expr`), multi-line expressions, and the nested scopes that tripped up the line scanner. It is Rails-aware: ActiveRecord models (`has_many`/`has_one`/`belongs_to`/`has_and_belongs_to_many`, scopes, table inference, explicit `self.table_name`) emit `storage` facts; the route DSL in `config/routes.rb` (plus `config/routes/*.rb` and packwerk `draw`) is walked from the real block structure, so nested `namespace`/`scope`/`resources`/`member`/`collection` blocks produce one `route` per RESTful action (honoring `only:`/`except:`); and Packwerk package boundaries (`package.yml` dependency enforcement, `app/public/` privacy) are parsed. It tracks modules, classes, methods with `public`/`private`/`protected` visibility, `class << self` eigenclass and `module_function` methods — now correctly typed as class methods rather than instance methods — mixins (`include`/`extend`/`prepend` → `implements` edges), `ActiveSupport::Concern` (flagged `concern: true`), constants, and `attr_*` accessors. Like the other AST extractors, it walks method bodies for call sites, emitting `calls` edges (qualified `Const.method`/`Ns::Class.method` and receiver `var.method`, deduplicated) and `implements` edges for superclasses — so Ruby participates in `traverse`, `find_path`, and `impact_analysis`.
532
532
533
-
**C++** is parsed with tree-sitter and handles the header/source split that defines the language. Classes, structs, unions, enums (incl. `enum class`), namespaces, free functions and methods, data members, and `typedef`/`using` aliases become symbol facts named `<dir>.<ns1::ns2::Class::member>` — enola's `<dir>.` module convention on the outside, native C++ `::` scope inside. Because an out-of-line definition `Class::method` (parsed from a `qualified_identifier`) yields the same canonical name as its in-class declaration, a dedup pass **merges a header's method prototype with its `.cpp` definition** into a single symbol (the definition wins for file/line and carries the call-graph edges). Base classes become `implements` edges; method bodies are walked for `calls`/`instantiates` edges; quoted `#include "x.h"` becomes a `dependency` resolved to the declaring module, while system `<...>` includes are skipped. Templates are unwrapped to their inner declaration and flagged `templated`, and the walker descends through `#if`/`#ifdef` preprocessor guards (so code wrapped in `#if defined(HAVE_*)` and headers behind include guards are still extracted). Like the other AST extractors, it walks each function/method body for complexity metrics — `cyclomatic`, `loop_depth`, `loop_count`, `calls_in_loop`, and `recursive_self` (counting `for`/`while`/`do-while`/range-`for` and STL-algorithm lambdas like `for_each`/`transform` as loops) — which the enterprise `analyze_performance` tool consumes. *Limitation:* header/source merging relies on the `.h` and `.cpp` living in the same directory (the common layout); split `include/` + `src/` trees are not merged.
533
+
**C/C++** is parsed with tree-sitter and handles the header/source split that defines the language. The extractor owns both languages: `.c` files (and bare `.h` headers in a C context) are parsed with the **tree-sitter-c** grammar, while `.cpp`/`.cc`/`.cxx`/`.hpp`/... (and `.h` headers in a C++ context) use **tree-sitter-cpp**; every fact carries a `language` prop (`"c"` or `"cpp"`). A real C grammar is required rather than reusing the C++ one, because C code routinely uses C++ keywords as ordinary identifiers (`new`, `try`, `class`, `delete`, `private`, ...) plus C-only constructs (`_Generic`, `restrict`, GCC range designators) that the C++ grammar rejects. Bare `.h` headers are attributed **per directory subtree**: a header is treated as C++ only when its own subtree contains unambiguous C++ sources — so a handful of stray `.cpp` files (e.g. `tools/` in an otherwise pure-C kernel tree) no longer flip every `.h` in the repo to C++; absent that signal, `.h` defaults to C. In C, a `static` function has internal linkage and is emitted with `exported=false` (file-private); C++ keeps `exported=true`. Classes, structs, unions, enums (incl. `enum class`), namespaces, free functions and methods, data members, and `typedef`/`using` aliases become symbol facts named `<dir>.<ns1::ns2::Class::member>` — enola's `<dir>.` module convention on the outside, native C++ `::` scope inside. Because an out-of-line definition `Class::method` (parsed from a `qualified_identifier`) yields the same canonical name as its in-class declaration, a dedup pass **merges a header's method prototype with its `.cpp` definition** into a single symbol (the definition wins for file/line and carries the call-graph edges). Base classes become `implements` edges; method bodies are walked for `calls`/`instantiates` edges; quoted `#include "x.h"` becomes a `dependency` resolved to the declaring module, while system `<...>` includes are skipped. Templates are unwrapped to their inner declaration and flagged `templated`, and the walker descends through `#if`/`#ifdef` preprocessor guards (so code wrapped in `#if defined(HAVE_*)` and headers behind include guards are still extracted). Like the other AST extractors, it walks each function/method body for complexity metrics — `cyclomatic`, `loop_depth`, `loop_count`, `calls_in_loop`, and `recursive_self` (counting `for`/`while`/`do-while`/range-`for` and STL-algorithm lambdas like `for_each`/`transform` as loops) — which the enterprise `analyze_performance` tool consumes. *Limitation:* header/source merging relies on the `.h` and `.cpp` living in the same directory (the common layout); split `include/` + `src/` trees are not merged.
534
534
535
535
**PHP** is parsed with tree-sitter (the `LanguagePHP` grammar, which tolerates the HTML/`<?php` interleaving of WordPress templates). Classes, interfaces, traits, enums (and their cases), top-level functions, methods, class constants, and properties become symbol facts. Type names are fully qualified with the file's `namespace` (`App\Models\Order`), members use PHP's native `Class::method` / `Class::$prop` notation, and global symbols keep their bare name (the common WordPress case). `extends`/`implements` and in-class `use SomeTrait;` become `implements` edges; method/function bodies are walked for `calls` (global-function, static `Class::method`, and bare instance-method targets), `instantiates` (`new X`), and the standard complexity metrics (`cyclomatic`, `loop_depth`, `loop_count`, `calls_in_loop`, `recursive_self`). `use Foo\Bar;` imports become `dependency` facts; a resolve pass builds a project-wide FQN→module index and turns namespaced references (inheritance, trait use, static calls, instantiations) and resolvable imports into internal module-coupling edges, classifying the rest as external — so PHP participates in `traverse`, `find_path`, and `impact_analysis`. **Outbound HTTP-client detection** emits a client-role `route` fact for each call made through Guzzle (`$client->get('/x')`, `$client->request('POST', '/x')`), the Laravel `Http` facade (`Http::get(...)`, including `Http::withToken(...)->get(...)` chains), Symfony's `HttpClient` (`$httpClient->request(...)`), raw cURL (`curl_setopt($ch, CURLOPT_URL, …)`, `curl_init`), and `file_get_contents` — relative paths are kept (with a `target_hint` inferred from any nearby base-URL env var), while absolute `http(s)://` URLs and interpolated/concatenated paths are skipped. **Framework route DSLs** add server-role `route` facts. **WordPress awareness** (enabled when a `wp-load.php`/`wp-settings.php`/`wp-config.php` marker is present) emits a `route` fact per hook call: `add_action`/`add_filter` registrations (carrying the `callback` target), the `do_action`/`apply_filters` hook points, and `register_rest_route` endpoints — dynamic (interpolated/variable) hook names are skipped. **Laravel** (detected via `laravel/framework` in `composer.json`, an `artisan` file, or a `routes/web.php`|`api.php`) parses the `Route::` DSL in `routes/*.php`: verb registrations (`Route::get/post/…`), `Route::match`/`any`, `Route::resource`/`apiResource` (expanded into their REST actions), nested group prefixes (both `Route::group(['prefix' => …], …)` and the fluent `Route::prefix('x')->…->group(…)`), and the `->name(…)` modifier — handlers are normalized to `Controller::method`. **Symfony** (detected via `symfony/framework-bundle`, or `bin/console` + `config/`) reads routes from PHP 8 `#[Route('/path', methods: ['GET'], name: '…')]` attributes (a class-level attribute prefixes its methods; `methods` accepts string literals or `Request::METHOD_*` constants) and legacy `@Route(…)` docblock annotations, plus YAML (`config/routes.yaml`, `config/routes/*.yaml`) and XML route configuration — these config files are scanned directly from disk, independently of the main walker (the same approach the OpenAPI extractor uses), so they are found even when `*.yaml` is globally ignored, as the bundled configs do. Once emitted, these client/server routes flow through the cross-repo linker and the `unused-routes` explainer like every other language. *Scope:* Symfony route imports/`resource` includes and bundle-local route configs (`**/Resources/config/routes.*`) are not expanded/discovered.
Copy file name to clipboardExpand all lines: README.md
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -37,7 +37,7 @@ Cursor (add to `mcp.json`):
37
37
38
38
Done. Your agent now has a precise structural map of your code. For configuration options, multi-repo setup, and what to ask next, see [Quick start](#quick-start) below.
0 commit comments