autoindex: capture PHP enums and class constants - #286
Conversation
PHP_Q matched functions, methods, classes, interfaces and traits. `enum_declaration` was absent, and so was every form of named constant. The enum gap is the more severe of the two, because it is not a missing kind on an otherwise-indexed file. A PHP 8.1 enum declares no class, no interface and no trait, so a file holding one extracted ZERO symbols: the type could not be found by its own name through symbol search at all. That is xerj-org#170's failure mode. Backed enums are the idiomatic way to express a closed set of values in modern PHP, and they are exactly the declarations a caller looks up by name. Class constants are PHP's only way to write a named constant inside a type. Without them a class consisting mostly of constants — a limits table, a status registry — carried only its own name into `defs`, and the constant a caller was searching for was not there. The same `const_declaration` node covers a file-scope `const`, so one pattern serves both; `const_element` holds the name in either position. Unlike the C, Go and Rust queries, these patterns are deliberately not anchored. PHP has no function-local `const` statement, so there is no locals trap to guard against, and anchoring would only cost recall on constants declared inside a namespace block. `define()` is a function call — a different node — and stays uncaptured. Enum cases are captured as `@const`, matching how Go's `const_spec` members are treated: a case is a named constant of the enum type, and it is what a caller greps for when they know the value but not the type.
|
Thanks for this contribution — we'd like to merge it. Before we can, we need a signed Contributor License Agreement from: @pradaev It is a one-time thing per contributor, not per pull request. Read CLA.md, then open a small pull request adding your GitHub username to Once it is merged, comment |
There was a problem hiding this comment.
Pull request overview
This PR improves xerj-autoindex’s PHP symbol extraction so PHP 8.1+ enums and named constants become discoverable via symbol search (addressing the “zero extracted symbols” failure mode for enum-only files).
Changes:
- Extends the Tree-sitter PHP query to capture
enum_declaration,enum_case, andconst_declarationsymbols. - Adds a targeted regression test (
php_enum_and_const) covering enums, enum cases, class/interface constants, and top-levelconst.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
@cla-bot check |
|
Re-checking the CLA status for every commit author on this pull request… |
Cut from main at 83971fd after the pre-cut PR sweep. Nineteen pull requests had merged since v1.0.0-rc.15 when the sweep started; five more were open and every one got an explicit decision: - merged after review: #305 (autoindex journal-lock release, fork-inherited flock), #307 (dynamic-mapping #292 close-out), #309 (default _search omits embedding companions), #308 (CLA co-author gate, CI-only), #306 (landing hero, already deployed) - excluded: #274 (author-marked WIP, failing checks), #258 (in-progress #204 sweep, failing check) Review residuals from #305/#307/#309 are filed as #310, #311, #312 and disclosed in their CHANGELOG entries rather than left in review text. CHANGELOG: the [Unreleased] section moves under 1.0.0-rc.16 (2026-08-11). Eight merged PRs had filed no entry — #296, #300, #287, #284, #285/#304, #286, #305 — written here, alongside the #309 entry expanded with its copy-path guarantees and disclosed residuals, and the #292/#307 entry extended with the field-budget tightening (multi-fields now count, matching ES total_fields semantics). Both rc.15 known issues (progress-stream forgery, outer-.gitignore reach into nested checkouts) are fixed in this release and the section header says so. Structural repairs: duplicate ### Changed heading merged, two missing blank lines restored (one eaten next to the rc.10 heading by #309's diff). Version: engine workspace 1.0.0-rc.15 -> 1.0.0-rc.16; Cargo.lock refreshed via scoped cargo check -p xerj-common. Tag follows only after CI and the 8-target Release matrix both report success on this exact commit.
Written by an AI coding agent (Claude Opus 5 via Claude Code), run by @pradaev.
@pradaev has signed the CLA (#289) and approved this for review.
Defect
PHP_Qmatched functions, methods, classes, interfaces and traits.enum_declarationwas absent, and so was every form of named constant.The enum gap is the severe one, because it is not a missing kind on an
otherwise-indexed file. A PHP 8.1 enum declares no class, no interface and no
trait, so a file holding one extracted zero symbols — the type could not be
found by its own name through symbol search at all. That is #170's failure mode.
Backed enums are the idiomatic way to express a closed set of values in modern
PHP, and they are exactly what a caller looks up by name.
Class constants are PHP's only way to write a named constant inside a type.
Without them, a class made mostly of constants — a limits table, a status
registry — carried only its own name into
defs, and the constant the caller wassearching for was not there.
Fix
const_declarationpattern serves both a class/interface/enum constant anda file-scope
const— the grammar uses the same node for both, withconst_elementholding the name. Verified againstto_sexp()output, includingthe modifier-and-type form
public const int X = 1, where the name still sitsin
const_element.function-local
conststatement, so there is no locals trap to guard against,and anchoring would only cost recall on constants declared inside a namespace
block.
define()is a function call — a different node — and stays uncaptured.@const, matching how Go'sconst_specmembers are treated: acase is a named constant of the enum type, and it is what a caller greps for
when they know the value but not the type.
Failing test first
php_enum_and_const, run against unfixedmain. Note what is in the output: theclass and the interface, and nothing else — the enum
Suitis absent entirely,along with all five constants:
Verified — commands run in this environment, output observed
cargo test -p xerj-autoindex --lib extract::codeon unfixed main → the failure abovecargo test -p xerj-autoindex --lib extract::codeon this branch →ok. 25 passed; 0 failedcargo clippy -p xerj-autoindex --all-targets -- -D warnings→ cleancargo fmt --all -- --check→ clean./target/release/es-yaml-runner --dir tests/es-compat-yaml/yaml->
1366 passed · 0 failed · 3 skipped · 1369 total, runner exit 0(release binary built from this branch, node started on a fresh
mktemp -d)to_sexp()before writing the patterns:(enum_declaration name: (name) … body: (enum_declaration_list (enum_case name: (name) …)))and
(const_declaration (visibility_modifier) type: (primitive_type) (const_element (name) …)).Assumed — NOT tested
enum_casenode as a backed enum. The test covers the backed form only.Falsified by a grammar that gives pure cases a different node.
very large enum will now contribute many
@constentries. Go's groupedconst (…)sets the precedent, but I did not measure the distribution of PHPenum sizes.
Evidence boundary
First observed while indexing a private PHP monolith: 571 files whose top-level
declaration is an
enumproduced nodefs/symbols, confirmed by fetching onesuch document from a running node and finding
language: phpwith both fieldsabsent. That corpus is not shareable, so the committed test is the reproducible
evidence.
Not run
(
content::tests::lossy_display_collisions_keep_distinct_alias_identities,Os { code: 92, "Illegal byte sequence" }), reproduced on unmodifiedmainin this environment and unrelated to this change.