The #913 GLR recursion cap patches vendored ts_runtime/src/stack.c;
document it in THIRD_PARTY.md so the vendored modification is on record
and re-applied on the next ts_runtime re-vendor (Stage 4: 0.26.x). The
patch is marked inline; this adds the provenance-level note the policy
requires for any change to vendored code.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
tree-sitter's GLR parser merges ambiguous parse-stack heads recursively in
stack_node_add_link (internal/cbm/vendored/ts_runtime/src/stack.c), once per
nesting level. On pathologically nested input whose grammar is ambiguous
(Perl's paren-optional calls, f(f(f(...)))), that recursion overflows the
native stack during the parse — a small ~1 MB Windows thread stack, and even
an 8 MB POSIX stack at extreme depth — before any extraction runs. Grammars
that are unambiguous here (Java, C++) never trigger the recursive merge.
Cap the recursive merge at CBM_TS_STACK_MERGE_MAX_DEPTH (512) via a
depth-tracked inner worker; the public stack_node_add_link is a thin wrapper
seeding depth 0, so every existing call site is unchanged. Past the cap the
ambiguity is left on the GLR stack instead of merged — a valid parse, never a
wrong one. 512 frames is well within a 1 MB stack while far exceeding any
realistic source nesting.
Add perl_glr_deep_parse_recursion_capped: a crash-isolating regression that
parses deep ambiguous Perl directly (a forked child on POSIX, in-process on
Windows), bypassing cbm_extract_file's CBM_PERL_MAX_PARSE_NESTING pre-parse
guard so the vendored cap itself is exercised rather than the guard.
Narrow vendored-runtime change; no other parsing behavior is affected. The
higher-level Perl pre-parse guard (issue #913 context) stays in place and can
be retired in a follow-up once this cap is proven in the field.
Signed-off-by: Shane McCarron <shane.mccarron@corvexconnect.com>
github/issue-labeler compiles patterns with JavaScript RegExp, which
rejects PCRE-only inline (?i) groups with SyntaxError: Invalid group, so
the workflow failed before evaluating any rule and no issue ever got an
area label (#764).
Replace the third-party action with first-party actions/github-script:
the same config rules are compiled with the i flag applied centrally
(case-insensitivity preserved), labels stay additive-only via addLabels,
and a pattern that fails to compile now fails the run loudly instead of
silently no-oping. Strip (?i) from all 7 config patterns and document
the line format the workflow parses.
Verified locally: all 7 rules compile and match expected labels across
sample issue texts; the old pattern reproduces the SyntaxError.
Closes#764
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Perl LSP landed in #461; scan its implementation against PerlNavigator
(MIT, the leading OSS Perl language server) for defensive
copy-detection, same rationale as the phpactor entry. Verified clean:
no verbatim string/comment overlap.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
glibc _FORTIFY_SOURCE aborts realpath() into a sub-PATH_MAX buffer
regardless of the actual path length, and Windows has no realpath at
all. Size the buffers at PATH_MAX and skip the guard on Windows (the
#794 canonicalization logic is platform-independent; the four Unix
legs carry it).
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
On v0.8.1, index_repository with repo_path "." flowed the literal dot
through everything derived from the path: the project was named
"root", the projects table stored root_path="." verbatim, the
post-dump integrity check flagged bad_root_path and auto-deleted the
entire DB — while the response still reported status "indexed" with
full node counts. Verified fixed on current main (the path is
canonicalized before derivation: correct project name, absolute
root_path, persistent and queryable DB).
The guard indexes with repo_path "." from inside the repo and asserts
the three things the old failure mode broke: path-derived project name
(never "root"), absolute canonical root_path in the projects table,
and a DB that survives the run and answers queries.
Closes#794
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
The previous commit's clang-format pass reformatted the whole file
(tests are not in the lint-enforced LINT_SRCS set, so the committed
file was never format-clean under the local formatter). Restore the
original formatting; the branch's net diff against main is now exactly
the two new regression guards.
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Two reported indexer OOMs reduce to the same self-flattening loop the
trait-uses-itself fix closed (self-flatten short-circuit + snapshotted
loop bound in flatten_trait_into_class):
- a trait whose name collides with an aliased `use ... as` import it
composes (#765's exact single-file reproducer) — the alias
canonicalizes onto the enclosing trait via the short-name fallback
- a class sharing its short name with an aliased trait import (#951's
reproducer, reduced to one file: the trait definition being
unavailable is what forces the short-name fallback onto the class)
Both fixtures were verified RED against the pre-guard code (each hangs
the suite within the 90s alarm when the two guards are reverted, each
independently as the first test run) and GREEN with the guards in
place. Landing them as permanent regression guards; the issues close as
fixed-by-the-#920-pair.
Closes#765Closes#951
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Two O(n^2) patterns in the Perl LSP, the class the ARM
extract_wide_flat_file_is_linear guard flags:
- Every child-iteration loop used indexed ts_node_child(node, i), which is
O(i) per call and O(n^2) over a wide flat node. Route them all through a new
perl_collect_children() helper mirroring wd_collect_children() in
extract_defs.c: a forward TSTreeCursor collect for wide nodes (>= 64
children), NULL (cheap indexed access) for the small common case.
- perl_attach_methods rebuilt each package's whole NULL-terminated method
array on every sub via perl_type_add_method, O(subs^2) on a single-package
file. Replace with a collect -> qsort-by-package -> build-once batch
(O(S log S)); source order within a package is preserved so first-defined
still wins on redefinition.
No behavior change: all perllsp_* resolution tests and
lsp_perl_deep_expression_no_crash pass; check-lsp-originality.sh reports clean.
Signed-off-by: Shane McCarron <shane.mccarron@corvexconnect.com>
A deeply nested Perl call chain f(f(f(...))) overflowed the stack while
parsing on small-stack platforms: tree-sitter's GLR parser recurses once
per nesting level in stack_node_add_link (vendored ts_runtime/src/stack.c)
merging the ambiguous parse-stack heads that Perl's f(...) grammar produces.
The overflow happens during ts_parser_parse, before any LSP walk runs, so
the CBM_LSP_PERL_MAX_WALK_DEPTH guards can never fire. This crashed
test-windows (CLANG64) with an AddressSanitizer stack-overflow and hung
test-unix (ubuntu-24.04-arm) to a 4h timeout on lsp_perl_deep_expression_no_crash.
Add cbm_source_nesting_exceeds(): an O(n) early-exit bracket-depth scan, and
skip extraction (clean has_error, zero edges) for Perl input whose nesting
exceeds CBM_PERL_MAX_PARSE_NESTING (128) before it reaches tree-sitter.
Scoped to Perl because only its ambiguous call grammar drives the GLR
recursion to the nesting depth; C/Java/Python parse the same shape with a
single stack head. 128 is ~10x above any real Perl nesting and well below
the depth where the parser overflows a 1MB stack.
Correct the lsp_perl_deep_expression_no_crash comment to describe the actual
mechanism (parser recursion, not the LSP walk).
Signed-off-by: Shane McCarron <shane.mccarron@corvexconnect.com>
Perl gained a full Hybrid LSP tier in this PR but was absent from the Hybrid LSP docs. Add a capabilities row to the "Languages with full Hybrid LSP" table, list Perl in the intro + features language lists, and bump the badge from 9 to 10 languages. Perl was already in the general language-support list and the resolution-quality tier (grammar-level); this covers the new type-resolution tier.
Signed-off-by: Shane McCarron <shane.mccarron@corvexconnect.com>
Optional, non-blocking follow-ups from the #461 review:
- perl_eval_function_call_type: split qualified static calls on the LAST
"::" (mirroring perl_resolve_function_call) so return-type chaining on
Foo::Bar::sub() resolves instead of yielding unknown.
- cbm_run_perl_lsp: finalize the type registry into a per-call scratch
arena (as php_lsp/java_lsp do) before resolution, so method/func/symbol
lookups use O(1) hash buckets instead of a per-file linear scan.
- perl_infer_self_type: match the `shift` invocant idiom at word
boundaries and accept `$_[0]`, so `my $self = shifty();` no longer
mis-binds the receiver (tightens the zero-edge guarantee).
Signed-off-by: Shane McCarron <shane.mccarron@corvexconnect.com>
Remove .vbw-planning/phases/22-perl-lsp-semantic-resolution/22-03-SUMMARY.md
per maintainer request — internal planning/scratch doc that should not land
in the repo.
Signed-off-by: Shane McCarron <shane.mccarron@corvexconnect.com>
perl_resolve_function_call split a package-qualified call name on the FIRST
"::" via strstr. For multi-level packages (Foo::Bar::sub) this yielded
pkg "Foo" and a sub name still containing "::" ("Bar::sub"), which never
resolved. The call then fell through to the generic bare-name fallback, which
matches on the bare sub name only — collapsing distinct packages' same-named
subs onto a single winner and orphaning the rest. Single-level calls
(Foo::sub) were unaffected, which is why the existing test missed it.
Split on the LAST "::" instead so the full package name reaches
perl_lookup_method, which resolves to the correct fully-qualified sub.
Adds perllsp_static_multilevel_package_call covering the multi-level form.
Signed-off-by: Shane McCarron <shane.mccarron@corvexconnect.com>
F1: dot the Exporter import target so seeded CPAN exported subs resolve.
perl_collect_qw_imports built colon-form targets (Scalar::Util::blessed)
but the stdlib registry keys curated CPAN subs in dotted form
(Scalar.Util.blessed) and lookup is exact-match. Wire in perl_pkg_to_dot
to dot the module portion and drop the now-unnecessary (void) cast.
F2: add a recursion-depth guard (CBM_LSP_PERL_MAX_WALK_DEPTH=512) to both
AST walkers (perl_resolve_calls_in_node, perl_pass1_scan) via a depth-
guarded wrapper + inner split, mirroring java_lsp's JAVA_LSP_MAX_WALK_DEPTH.
Past the cap a subtree is skipped (graceful degradation, no wrong edge),
preventing stack overflow on pathologically nested input.
F3: lock the shared last-"::"-segment normalization in lsp_resolve.h with a
direct regression test over cbm_pipeline_find_lsp_resolution: a qualified
static call still resolves AND the cross-namespace mis-attribution edge case
is bounded by caller-QN equality + the confidence floor.
F4: implement SUPER:: dispatch. Populate enclosing_parent_qn from the
enclosing package's first @ISA parent and resolve $self->SUPER::method() to
that parent's method (strategy perl_method_super). No known parent or
unresolved method emits no edge (zero-edge guarantee preserved).
Tests: perllsp_cpan_exported_function, perllsp_super_dispatch,
perllsp_super_no_parent_no_edge, lsp_perl_deep_expression_no_crash,
lsp_resolve_qualified_static_call_normalizes_colons,
lsp_resolve_misattribution_is_bounded.
Signed-off-by: Shane McCarron <shane.mccarron@corvexconnect.com>
Replace the plan-01 placeholder with the full Perl LSP test suite covering the
ten foundational resolution scenarios from 22-RESEARCH.md, plus the extract_perl
/ find_resolved / require_resolved / find_resolved_with_strategy helpers cloned
from test_php_lsp.c:
1. method via bless-assignment 6. use parent MRO
2. constructor class-method type 7. use base MRO
3. static package call 8. Exporter import (use Mod qw(f))
4. $self method dispatch 9. require fallback
5. @ISA inheritance 10. unresolvable receiver -> zero edges
Assertions match the resolver's actual QN scheme (module_qn.subname, dotted —
the Perl package governs dispatch, not the emitted QN), and the negative test
confirms the zero-edge guarantee for untyped scalar and unindexed package
receivers. Register suite_perl_lsp in test_main.c so scripts/test.sh runs it.
All 10 perl_lsp tests pass (3563 passed, 1 pre-existing unrelated failure:
search_code_multi_word in tests/test_mcp.c).
Signed-off-by: Shane McCarron <shane.mccarron@corvexconnect.com>
perl_collect_isa_assignment relied on the assignment's `right` field, but
tree-sitter-perl flattens a parenthesized RHS (`our @ISA = ('Base')`) so the
`right` field points at the `(` token while the parent string literals are
sibling children of the assignment. The single form `@ISA = 'Base'` worked but
the common parenthesized form silently collected zero parents, so @ISA
inheritance never populated embedded_types and method dispatch could not walk
the MRO.
Scan every named child after the `=` instead of only the `right` field, covering
both `@ISA = 'Base'` and `@ISA = ('Base', 'Other')`. use parent / use base were
already handled via a separate path and are unaffected.
Signed-off-by: Shane McCarron <shane.mccarron@corvexconnect.com>
- Add method_call_expression to perl_call_types in lang_specs.c so the
structural tier emits a method-call edge for the LSP bridge to refine
(parity with PHP member_call_expression). callee_name is the bare method
via the field-based extractor's `method` branch.
- Normalize the textual callee in cbm_pipeline_find_lsp_resolution to its
last "::"-separated segment so qualified static Pkg::sub() calls match the
resolved sub's dotted short-name (parity with PHP scoped_call_expression).
- Zero-edge guarantee preserved: untyped receivers still emit no edge.
Resolves DEVN-04 from plan 22-03. Verified on a Base/Derived/main fixture:
run_typed->{greet,describe}, run_static->helper, run_classcall->greet,
describe->greet (inherited), run_untyped->none.
Signed-off-by: Shane McCarron <shane.mccarron@corvexconnect.com>
- cbm.c: include lsp/perl_lsp.h and dispatch CBM_LANG_PERL -> cbm_run_perl_lsp
- lsp_all.c: unity-include perl_lsp.c + generated/perl_stdlib_data.c
- Makefile.cbm: register TEST_PERL_LSP_SRCS and append to ALL_TEST_SRCS
- tests/test_perl_lsp.c: placeholder suite so the Makefile var resolves
(full suite + test_main.c registration land in plan 22-04)
Signed-off-by: Shane McCarron <shane.mccarron@corvexconnect.com>
Follow-up to #787/#828, disclosed during #828's review: the same clobber
class also affects two call sites #828 deliberately left out of scope.
create_env_configures_for_file (pass_definitions.c) and resolve_file_throws
(pass_parallel.c) both resolved their edge source via a direct
cbm_gbuf_find_by_qn(enclosing_func_qn) lookup with no guard against the
lookup landing on the shared Folder/Project node for directory-module
languages (Java/Go) -- the exact #787 root cause, just unguarded at two
more sites. A class-level construct (no enclosing function) has its
enclosing_func_qn fall back to the MODULE QN, which for a directory-based
module collides with the package's Folder node, conflating every
same-package file's CONFIGURES/THROWS edges onto one arbitrary source.
- create_env_configures_for_file: added the same
cbm_pipeline_node_is_dir_container guard already used by the three
#828-fixed finders, falling back to the per-file File node on a
directory-container hit.
- resolve_file_throws: was missing the `rel` parameter its sibling
resolve_file_calls/usages/rw already carry, so it inlined a raw lookup
instead of calling the shared, already-guarded find_source_node. Added
`rel` and switched to find_source_node, matching its siblings exactly.
Regression guard: tests/repro/repro_issue842.c, reproduce-first per the
issue's request for "an #828-style multi-order red fixture":
- CONFIGURES reproduces on a small 5-file sequential fixture (Go, since
Java's System.getenv detection is separately broken -- extract_env_key_from_call
reads a "function" field Java's method_invocation node doesn't have; noted
in the test, out of scope here).
- THROWS only reproduces past MIN_FILES_FOR_PARALLEL=50 (the worker-merge
path) -- confirmed empirically that the small sequential fixture already
attributes correctly even on unfixed HEAD, mirroring #787's own two-faced
(sequential/parallel) bug. Padded with 60 filler classes, following
repro_issue787.c's build_parallel_fixture precedent.
RED on unfixed HEAD (both tests): 0 passed, 2 failed. GREEN with this fix:
2 passed, 0 failed. Full scripts/test.sh (5958 cases) passes clean with
this change; scripts/lint.sh's two pre-existing failures (pass_lsp_cross.h
parameter-name mismatch, extract_defs.c cppcheck AST parse) are unrelated
and present identically on unfixed main.
Fixes#842
Signed-off-by: Dhruvy0804 <dhruvgarg553@gmail.com>
The per-language chapters were LLM-drafted to establish format and
dimension mapping — their symbol targets are not yet ground-truth-derived
and must be regenerated at execution time per §3.1/§12. Make that status
explicit at the top of Appendix A so the drafted symbol names are not
read as validated.
Supersedes #420 (same diff; recreated for DCO author/sign-off).
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>