Skip to content

autoindex: capture PHP enums and class constants - #286

Merged
xerj-org merged 1 commit into
xerj-org:mainfrom
pradaev:fix/php-enum-and-const
Aug 11, 2026
Merged

autoindex: capture PHP enums and class constants#286
xerj-org merged 1 commit into
xerj-org:mainfrom
pradaev:fix/php-enum-and-const

Conversation

@pradaev

@pradaev pradaev commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

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_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 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 was
searching for was not there.

Fix

(enum_declaration name: (name) @enum)
(enum_case name: (name) @const)
(const_declaration (const_element (name) @const))
  • One const_declaration pattern serves both a class/interface/enum constant and
    a file-scope const — the grammar uses the same node for both, with
    const_element holding the name. Verified against to_sexp() output, including
    the modifier-and-type form public const int X = 1, where the name still sits
    in const_element.
  • Deliberately not anchored, unlike the C, Go and Rust queries. 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 @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.

Failing test first

php_enum_and_const, run against unfixed main. Note what is in the output: the
class and the interface, and nothing else — the enum Suit is absent entirely,
along with all five constants:

---- extract::code::tests::php_enum_and_const stdout ----
thread 'extract::code::tests::php_enum_and_const' panicked at
crates/xerj-autoindex/src/extract/code.rs:513:9:
got [("C", "class", 3), ("I", "interface", 4)]

test result: FAILED. 24 passed; 3 failed

Verified — commands run in this environment, output observed

  • cargo test -p xerj-autoindex --lib extract::code on unfixed main → the failure above
  • cargo test -p xerj-autoindex --lib extract::code on this branch → ok. 25 passed; 0 failed
  • cargo clippy -p xerj-autoindex --all-targets -- -D warnings → clean
  • cargo fmt --all -- --check → clean
  • ES-YAML conformance suite, on this branch's tree:
    ./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)
  • Node shapes confirmed by dumping 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 cases of a pure enum (no backing type) are assumed to use the same
    enum_case node as a backed enum. The test covers the backed form only.
    Falsified by a grammar that gives pure cases a different node.
  • Capturing every enum case is assumed to be net-positive rather than noise. A
    very large enum will now contribute many @const entries. Go's grouped
    const (…) sets the precedent, but I did not measure the distribution of PHP
    enum sizes.

Evidence boundary

First observed while indexing a private PHP monolith: 571 files whose top-level
declaration is an enum produced no defs/symbols, confirmed by fetching one
such document from a running node and finding language: php with both fields
absent. That corpus is not shareable, so the committed test is the reproducible
evidence.

Not run

  • Pre-existing macOS-only failure in the crate's wider suite
    (content::tests::lossy_display_collisions_keep_distinct_alias_identities,
    Os { code: 92, "Illegal byte sequence" }), reproduced on unmodified main
    in this environment and unrelated to this change.

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.
@cla-bot

cla-bot Bot commented Aug 10, 2026

Copy link
Copy Markdown

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 .contributors — that pull request is your signature, since it is made from your own account.

Once it is merged, comment @cla-bot check here and this check will go green. If you believe you are already covered, comment @cla-bot check and it will re-verify.

@pradaev
pradaev marked this pull request as ready for review August 10, 2026 20:24
Copilot AI lite review requested due to automatic review settings August 10, 2026 20:24

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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, and const_declaration symbols.
  • Adds a targeted regression test (php_enum_and_const) covering enums, enum cases, class/interface constants, and top-level const.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@pradaev

pradaev commented Aug 10, 2026

Copy link
Copy Markdown
Contributor Author

@cla-bot check

@cla-bot cla-bot Bot added the cla-signed label Aug 10, 2026
@cla-bot

cla-bot Bot commented Aug 10, 2026

Copy link
Copy Markdown

Re-checking the CLA status for every commit author on this pull request…

@xerj-org
xerj-org merged commit 2a7829d into xerj-org:main Aug 11, 2026
1 check passed
xerj-org added a commit that referenced this pull request Aug 11, 2026
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants