fix(extract): parse PHP past inline HTML so symbols after ?> reach the graph (#2000) - #2389
harshitaajoshi wants to merge 3 commits into
Conversation
…e graph (DeusData#2000) Signed-off-by: harshitaajoshi <j.harshitaa06@gmail.com>
DeusData
left a comment
There was a problem hiding this comment.
Thank you so much, @harshitaajoshi, this is a really careful piece of work. Blanking the markup into a buffer of the same length, rather than touching the vendored grammar, is exactly the right level for #2000. Byte offsets, rows and node text all stay in on-disk coordinates, so nothing downstream (line ranges, snippets, parse-coverage ranges) has to know it happened. We especially appreciated:
- the care with the lexical edge cases: strings, heredoc, nowdoc and block comments versus
///#closing PHP mode, plus#[,<?xmland__halt_compiler; - mapping
?>to;the way PHP itself treats it, and keeping the<?=expression so echo calls survive; - remembering the cross-file LSP entry, so the cached tree and a re-parse agree.
The Automattic/_s before/after table (17 -> 0 partial files, no function or edge lost) is exactly the kind of evidence we love to see.
A few small requests before we merge:
- Please rename the static
cbm_parse_sourceininternal/cbm/cbm.c. Open PR #2340 introduces a publicTSTree *cbm_parse_source(TSParser *, const char *, uint32_t, TSParseOptions)incbm.h/cbm.c. The two branches merge with no text conflict, but the mergedcbm.cthen has both definitions and won't compile, and because the text merge is clean nothing flags it before the build. A distinct name, or inlining thelanguage == CBM_LANG_PHPcheck at the call site, keeps the two PRs independent. Otherwise your masked buffer works fine with #2340. - Please add a test that fails if the
php_lsp.cmasking line is reverted. That line matters most on a path the current tests may not reach. When a file's result has been spilled to disk under the memory budget, it comes back without its cached tree, andcbm_run_php_lsp_crossre-parses the source itself. Without the mask, the tail of an inline-HTML file would lose its cross-file resolution only when that file happened to spill. The graph would then depend on memory pressure, which we treat as a correctness bug. A direct call intests/test_php_lsp.cwithcached_tree = NULLon your #2000 fixture would pin it;tests/test_go_lsp.c(around line 1067) shows the shape. If you find the pipeline test already fails with that line reverted, just tell us and that's fine too. - Please add a negative assertion that markup never becomes graph content. Graph quality is our first priority, so alongside "the tail comes back" we'd like a guard that the HTML itself never produces nodes. For example, put code-shaped markup between
?>and<?php(<p>function fake() { return leak(); }</p>) and assert!has_def(r, "Function", "fake")and!has_call(r, "leak"). That pins the "blanked, never parsed" behaviour and catches any future mistake in open-tag detection.
Small nits, take or leave:
- In
pipeline_php_inline_html_calls_resolve_issue2000, the fixture loop could simply usewrite_temp_file(...). That helper opens files withcbm_fopen(..., "wb"), and binary mode keeps the bytes identical on Windows. cbm_php_mask_inline_htmlallocates and copies the whole file before it knows whether anything will change. Copying on the first write would avoid a throwaway copy when the only?>bytes sit inside strings or comments.
Two shapes we'd expect to still leave a small error region. They're fine for a follow-up, and still far better than today:
<?= $a, $b ?>;<?php switch ($x): ?>followed by<?php case 1: ?>(the leading;before the first case).
Thanks again. This closes a gap that affects most legacy PHP codebases, and it was a pleasure to review.
Signed-off-by: harshitaajoshi <j.harshitaa06@gmail.com>
|
Thanks a lot for the thorough review! All three requests and both nits are addressed in 5ff3e8d.
Nits:
Agreed on Suites run with this commit: |
|
Thanks for opening this — it has been seen, and it is queued. This note is automated, but it is not a brush-off: it exists so you know where your PR stands instead of having to guess from silence. Current review status: working through a backlog. What that means for this PR, concretely:
Things that will genuinely speed it up whenever review does happen:
If this fixes a bug, a reproduction we can run is worth more than a description of the symptom. Thanks for contributing, and sorry in advance for the wait. |
|
Thank you, @harshitaajoshi. This round covers every point from our review, and it's done carefully:
The red One optional nit, only if you're touching the branch anyway: Next, we build and run the extraction, |
…eusData#2000) Signed-off-by: harshitaajoshi <j.harshitaa06@gmail.com>
|
Thanks! I took the optional nit in 43ff3be since it makes the test check what it claims to.
To check it really tells the two apart, I ran the new test against the previous eager-copy |
Fixes #2000
What does this PR do?
The vendored PHP grammar is the
php_onlyvariant, which has no rule for?>or the markup after it. In a file that leaves PHP mode (?> <div>...</div> <?php), everything after the first close tag became an ERROR region, so the declarations and calls there never reached the graph and the file was reportedparse_partial.Before parsing, PHP source now goes through
cbm_php_mask_inline_html()(newinternal/cbm/php_inline_html.c), which blanks the inline-HTML regions:php_tag<?php,<?=and<?tags become spaces; after<?=the echoed expression stays as an expression statement, so its calls are kept?>becomes;, the statement terminator PHP itself treats it asThe rewritten buffer has the same length and every line break, so byte offsets and line numbers still refer to the file on disk, and node text inside PHP code is unchanged.
?>is only treated as a close tag in code and in///#comments; strings, heredoc, nowdoc and block comments are skipped,#[is treated as an attribute,<?xmlin markup is not an open tag, and nothing after__halt_compileris touched. A file that starts with an open tag and has no?>takes a fast path and is parsed exactly as before.It is applied in two places so both parses see the same bytes:
extract_file_ex_bodyincbm.c, via a smallcbm_parse_source()helpercbm_run_php_lsp_crossinphp_lsp.c, which receives the on-disk source and may re-parse itNo grammar, pipeline pass or MCP tool behavior changes.
Tests
Reproduce-first: the extraction tests were added and run before the fix. Three of them failed on
main(deltamissing,twomissing, and the template file flaggedparse_incomplete); all pass with the fix.tests/test_extraction.cphp_inline_html_tail_reaches_graph_issue2000: the fixture from the issue;deltais extracted at line 8 and its call togammais scoped todeltaphp_inline_html_template_shapes_issue2000:<?= f() ?>, short echo without spaces,if/else/endifandforeach { }split across PHP blocksphp_inline_html_close_tag_in_literals_issue2000:?>inside single, double quoted, heredoc, nowdoc and block comment;#[Attr];//comment ended by?>;<?xmlprolog in markupphp_inline_html_trailing_close_tag_crlf_issue2000: trailing?>and CRLF line numberstests/test_pipeline.cpipeline_php_inline_html_calls_resolve_issue2000: end to end,delta -> gamma(same file) andrenderCard -> formatPrice(cross file, both files contain inline HTML) exist as CALLS edges in the storeSuites run with the change:
extraction,pipeline,php_lsp,parse_coverage,grammar_regression,grammar_labels,grammar_imports,grammar_probe_atogrammar_probe_d,edge_imports,edge_structural,lsp_resolution_probe,node_creation_probe,convergence_probe,integration,lang_contract,incremental. No failures. I'm relying on CI for the full matrix.Real repository check
Indexed
Automattic/_s(a783f97) with a build ofmain(66d9c2b) and a build of this branch:parse_partialThe remaining 4 partial files in that repo are SCSS. Recovered:
_s_woocommerce_wrapper_after,_s_woocommerce_cart_link_fragment,_s_woocommerce_cart_linkand the two CALLS edges into_s_woocommerce_cart_link. No function or edge present onmainis missing, and the recovered functions'start_linevalues match the source file.Checklist
git commit -s)make -f Makefile.cbm test): the suites listed above pass; full run left to CImake -f Makefile.cbm lint-ci, pluslint-tidy-diffandscripts/security-audit.sh)Written with the help of Claude Code. I reviewed the change and ran the tests and the repository check above myself.