"""PHP 8.5 `(void)` casts and casts applied to `match (...)` (#4202). tree-sitter-php has no `(void)` cast and cannot parse `(string) match (...)`, so those statements came out as ERROR nodes: the call behind the cast was lost and the file got a partial-extraction warning. extract_php blanks just the cast (same length, code only) before parsing. """ from __future__ import annotations import tempfile from pathlib import Path from graphify.extract import ( _PHP_CONFIG, _extract_generic, _php_blank_unparsed_casts, extract, extract_php, ) CACHE = ( "ready();\n" " }\n" " private function ready(): void {}\n}\n" ) def test_void_cast_keeps_the_call_and_parses_cleanly(tmp_path): """`(void) X::m()` yields the same call edges as plain `X::m()`.""" (tmp_path / "a").mkdir() (tmp_path / "b").mkdir() calls, _ = _extract(tmp_path / "a", {"Cache.php": CACHE, "Kernel.php": KERNEL.replace("{cast}", "(void) ")}) plain, _ = _extract(tmp_path / "b", {"Cache.php": CACHE, "Kernel.php": KERNEL.replace("{cast}", "")}) assert (".boot()", ".ready()") in calls assert any(s == ".boot()" and t != ".ready()" for s, t in calls) # the Cache::warm call assert calls == plain assert "parse_errors" not in extract_php(tmp_path / "a" / "Kernel.php") def test_cast_on_match_parses_and_keeps_what_follows(tmp_path): calls, _ = _extract(tmp_path, {"Picker.php": ( " $this->first(),\n" " default => $this->second(),\n" " };\n" " }\n" " public function first(): int { return 1; }\n" " public function second(): int { return 2; }\n" " public function after(): string { return $this->label(1); }\n}\n")}) assert {(".label()", ".first()"), (".label()", ".second()"), (".after()", ".label()")} <= calls assert "parse_errors" not in extract_php(tmp_path / "Picker.php") def test_strings_and_comments_are_never_blanked(): src = ( b"\n

(void) html

\n" ) assert _php_blank_unparsed_casts(src) == src def test_blanking_keeps_offsets_and_line_numbers(): src = b" 2 };\n" out = _php_blank_unparsed_casts(src) assert len(out) == len(src) assert [i for i, ch in enumerate(out) if ch == 10] == [i for i, ch in enumerate(src) if ch == 10] assert b"VOID" not in out and b"(string)" not in out and b"match (1)" in out def test_files_without_the_pattern_extract_exactly_as_before(tmp_path): p = tmp_path / "Plain.php" p.write_text("