Remaining findings from a parser audit of tools/languages/, below the threshold I filed individually. Grouped so they are not lost; each is independent and locally fixable. All reproduced by running the parsers unless noted.
Nesting / context attribution
Call-site class_context is structurally always null in four languages. Downstream, resolution/calls.py:1394-1396 uses class_context[0] to resolve super.* calls and :804 uses it to disambiguate same-named methods — both dead for these languages.
java.py:954 — _get_parent_context (:273-291) takes no types argument and returns the first enclosing method or class. For a call inside a method it always returns the method, so the ctx_type in ("class_declaration", ...) test never passes → (None, None).
csharp.py:409-410 — same root cause; 'class' in 'method_declaration' is always false.
swift.py:567 — hardcoded "class_context": (None, None). Line 564 also hardcodes "args": [] for every call, and :571-572 swallows all exceptions with a bare continue.
php.py:551 — the new X() branch emits a (None, None) tuple while every other PHP call (:502) emits a plain string, so the shape varies within a single list.
cpp.py:583 does this correctly by passing types=("class_specifier",) — a good template.
Nested functions get no CONTAINS edge outside Python.
javascript.py:292-311 — context / context_type are computed at 292 and then never stored; func_data keeps only class_context.
- TypeScript does emit
context_type: "function_declaration", but graph_builder.py:311 and writer.py:358 gate the nested-function batch on the literal Python node type 'function_definition', which only python.py emits. This is a parser-output/indexer-contract mismatch affecting every non-Python language that populates context_type.
rust.py:152-162 — Rust impl methods carry only module_context, no class_context. impl blocks are read for inheritance (struct Point correctly gets bases: ["Draw"]), but graph_builder.py:302 needs class_context, so Point::new and Point::helper end up orphan functions never linked to Point.
Wrong values written to the graph
rust.py:304 — braced use lists keep only the last name: use std::collections::{HashMap, HashSet, BTreeMap}; → a single import named BTreeMap.
rust.py:295 — full_import_name is raw source text including keyword and semicolon: "use std::collections::HashMap;". The cleaned_path computed at :303 is used only for name. writer.py:557-565 writes this verbatim as the Module node name, and graph_builder.py:668 / calls.py:1510 do full_import_name.replace('.', '/') in path, which can never match.
cpp.py:320 — #include "local.h" keeps its quotes (.strip('<>') misses them; c.py:606 correctly uses .strip('"<>')). Yields a Module named '"function_chain.h"', so local-header imports match neither the header file nor the C parser's unquoted form for the same header.
csharp.py:293 — base lists split on a naive ,, shredding multi-arg generics: class Map : Dictionary<string, int> → bases: ['Dictionary<string', 'int>'], creating INHERITS edges to two nonexistent types.
python.py:447 — from . import X yields full_import_name: "..sibling" (and from .. import x → "...x"), one package level too high. No consumer strips leading dots.
dart.py:499 — import 'dart:math' as math; loses the alias (prefix has no identifier field and there is no fallback, unlike dart.py:402/:619), so a math.max(...) call cannot be mapped back to its import.
Missing declarations
scala.py:9-13 — the query makes parameters mandatory, so parameterless defs (getters, abstract members, def name: String) are dropped: in object Utils { def simple = 42; def withParens(): Int = 43 } only withParens is returned.
ruby.py:9-12 (and pre_scan_ruby :548-550) — def self.x singleton methods are never captured (node type is singleton_method, not method). Because the pre-scan omits them too, cross-file resolution cannot find them either.
php.py:21-33, :148 — PHP 8.1 enum is in neither the classes query nor _get_parent_context's owner list, so enums are not indexed and their methods become orphans.
c.py:124-128, cpp.py:95-99 — function-like macros (#define SQUARE(x) ...) are preproc_function_def, not preproc_def, so they are never extracted. The parameter-extraction code at c.py:729-734, commented "Extract parameters for function-like macros", is unreachable.
cpp.py:497, :100-118 — variable type is None for every initialized variable (node.parent is the init_declarator; type lives on the enclosing declaration), and uninitialized declarations (int count;) are never captured at all — c.py:114-122 has the forms cpp.py lacks.
cpp.py:183-217 — inline methods defined in a class body get no class_context (only out-of-line Foo::bar does), so writer.py:1374-1377's C++ post-pass creates no Class-[:CONTAINS]->Function edge for header-defined methods.
Duplicate / spurious records
elixir.py:450-466 — every def/defp head is emitted as a self-recursive call. A definition is a call node whose arguments contain the signature as a nested call; ELIXIR_KEYWORDS filters def itself, but the recursion at :468-469 descends into its arguments and records the signature as an invocation. Every Elixir function gets a false CALLS edge to itself.
ruby.py:428-448 — nested calls steal each other's receiver and args: receiver/args captures are assigned to every containing call node with no field check and no break (the name branch at :438 does verify). logger.info(formatter.render(payload)) records info with full_name: "formatter.info".
csharp.py:398-401 — the name_line dedup key drops distinct calls to the same function on one line: return Math.Max(Clamp(a), Clamp(b)); records one Clamp.
csharp.py:217 — the attribute relationship is inverted (attribute_list is a child of method_declaration, never its parent), so attributes is always [] and line_number points at the attribute line.
c.py:505-517, :498-531 — two enum-member bugs: the * quantifier makes only the first enumerator of each enum match (enum Color { RED, GREEN, BLUE } → [RED]), and _find_enum_members re-runs the whole-file query inside its per-enum loop with a global seen_names, so the first enum claims every member (OK from enum Status is emitted with enum_name: 'Color').
haskell.py _find_calls — duplicate call records: print (addTwo 1 2) emits addTwo twice at the same line.
ruby.py:487-507 — variable value is always None. node.parent returns a fresh Python wrapper each access, so id(current) differs for the same underlying node and the @name/@value captures land in different buckets. (n1 == n2 is True while id(n1) == id(n2) is False.)
Minor
c.py:741, cpp.py:426 — macro end_line off by one; a preproc_def node's end_point includes the trailing newline, so +1 double-counts.
kotlin.py:1223/:1342, php.py:280/:213 — line_number points at the annotation/attribute line rather than the declaration. python.py:274-277 deliberately handles this via decorated_definition, so the declaration line is the established convention.
ruby.py:38-47, :516-522 — @@class_vars and $globals are never captured; separately the startswith("@") test at :517 precedes startswith("@@") at :518, making the "class" branch dead even if the query were fixed.
go.py:93 — curr.child_by_field_name('type_spec'); type_declaration has no such field and there is no fallback, so _get_parent_context returns (None, 'type_declaration', line).
solidity.py:238 — an unreadable file returns a normal empty result dict rather than the {"path":..., "error":...} shape every other parser returns, so an I/O failure is indexed as "file with no symbols" rather than flagged.
Checked and clean
Kotlin imports/aliases and nested/data/sealed class context, extension receiver_type/return_type, parameter and variable scoping, call receiver inference. Ruby require/require_relative and module include→bases merging. PHP class/interface/trait extends/implements and $this->/self:: calls. Java bases extraction (:453 and :472 reference nonexistent fields but both have explicit node-type fallbacks). Lua and Perl overall. Every open() in the directory already passes errors=, so the meta-test is satisfied; no parser crashed on any of the 85 fixture files with index_source=True.
Environment
|
|
| Commit |
c0e0bed (main) |
| Python |
3.12.3, Linux |
From a parser audit of tools/languages/. Reproduced by running the parsers directly unless noted otherwise. Higher-impact findings from the same audit are filed separately (#1520-#1527).
Remaining findings from a parser audit of
tools/languages/, below the threshold I filed individually. Grouped so they are not lost; each is independent and locally fixable. All reproduced by running the parsers unless noted.Nesting / context attribution
Call-site
class_contextis structurally always null in four languages. Downstream,resolution/calls.py:1394-1396usesclass_context[0]to resolvesuper.*calls and:804uses it to disambiguate same-named methods — both dead for these languages.java.py:954—_get_parent_context(:273-291) takes notypesargument and returns the first enclosing method or class. For a call inside a method it always returns the method, so thectx_type in ("class_declaration", ...)test never passes →(None, None).csharp.py:409-410— same root cause;'class' in 'method_declaration'is always false.swift.py:567— hardcoded"class_context": (None, None). Line 564 also hardcodes"args": []for every call, and:571-572swallows all exceptions with a barecontinue.php.py:551— thenew X()branch emits a(None, None)tuple while every other PHP call (:502) emits a plain string, so the shape varies within a single list.cpp.py:583does this correctly by passingtypes=("class_specifier",)— a good template.Nested functions get no CONTAINS edge outside Python.
javascript.py:292-311—context/context_typeare computed at 292 and then never stored;func_datakeeps onlyclass_context.context_type: "function_declaration", butgraph_builder.py:311andwriter.py:358gate the nested-function batch on the literal Python node type'function_definition', which onlypython.pyemits. This is a parser-output/indexer-contract mismatch affecting every non-Python language that populatescontext_type.rust.py:152-162— Rustimplmethods carry onlymodule_context, noclass_context.implblocks are read for inheritance (structPointcorrectly getsbases: ["Draw"]), butgraph_builder.py:302needsclass_context, soPoint::newandPoint::helperend up orphan functions never linked toPoint.Wrong values written to the graph
rust.py:304— braceduselists keep only the last name:use std::collections::{HashMap, HashSet, BTreeMap};→ a single import namedBTreeMap.rust.py:295—full_import_nameis raw source text including keyword and semicolon:"use std::collections::HashMap;". Thecleaned_pathcomputed at:303is used only forname.writer.py:557-565writes this verbatim as theModulenode name, andgraph_builder.py:668/calls.py:1510dofull_import_name.replace('.', '/') in path, which can never match.cpp.py:320—#include "local.h"keeps its quotes (.strip('<>')misses them;c.py:606correctly uses.strip('"<>')). Yields a Module named'"function_chain.h"', so local-header imports match neither the header file nor the C parser's unquoted form for the same header.csharp.py:293— base lists split on a naive,, shredding multi-arg generics:class Map : Dictionary<string, int>→bases: ['Dictionary<string', 'int>'], creating INHERITS edges to two nonexistent types.python.py:447—from . import Xyieldsfull_import_name: "..sibling"(andfrom .. import x→"...x"), one package level too high. No consumer strips leading dots.dart.py:499—import 'dart:math' as math;loses the alias (prefixhas noidentifierfield and there is no fallback, unlikedart.py:402/:619), so amath.max(...)call cannot be mapped back to its import.Missing declarations
scala.py:9-13— the query makesparametersmandatory, so parameterlessdefs (getters, abstract members,def name: String) are dropped: inobject Utils { def simple = 42; def withParens(): Int = 43 }onlywithParensis returned.ruby.py:9-12(andpre_scan_ruby:548-550) —def self.xsingleton methods are never captured (node type issingleton_method, notmethod). Because the pre-scan omits them too, cross-file resolution cannot find them either.php.py:21-33,:148— PHP 8.1enumis in neither the classes query nor_get_parent_context's owner list, so enums are not indexed and their methods become orphans.c.py:124-128,cpp.py:95-99— function-like macros (#define SQUARE(x) ...) arepreproc_function_def, notpreproc_def, so they are never extracted. The parameter-extraction code atc.py:729-734, commented "Extract parameters for function-like macros", is unreachable.cpp.py:497,:100-118— variabletypeisNonefor every initialized variable (node.parentis theinit_declarator;typelives on the enclosingdeclaration), and uninitialized declarations (int count;) are never captured at all —c.py:114-122has the formscpp.pylacks.cpp.py:183-217— inline methods defined in a class body get noclass_context(only out-of-lineFoo::bardoes), sowriter.py:1374-1377's C++ post-pass creates noClass-[:CONTAINS]->Functionedge for header-defined methods.Duplicate / spurious records
elixir.py:450-466— everydef/defphead is emitted as a self-recursive call. A definition is acallnode whose arguments contain the signature as a nestedcall;ELIXIR_KEYWORDSfiltersdefitself, but the recursion at:468-469descends into its arguments and records the signature as an invocation. Every Elixir function gets a falseCALLSedge to itself.ruby.py:428-448— nested calls steal each other's receiver and args:receiver/argscaptures are assigned to every containing call node with no field check and nobreak(thenamebranch at:438does verify).logger.info(formatter.render(payload))recordsinfowithfull_name: "formatter.info".csharp.py:398-401— thename_linededup key drops distinct calls to the same function on one line:return Math.Max(Clamp(a), Clamp(b));records oneClamp.csharp.py:217— the attribute relationship is inverted (attribute_listis a child ofmethod_declaration, never its parent), soattributesis always[]andline_numberpoints at the attribute line.c.py:505-517,:498-531— two enum-member bugs: the*quantifier makes only the first enumerator of each enum match (enum Color { RED, GREEN, BLUE }→[RED]), and_find_enum_membersre-runs the whole-file query inside its per-enum loop with a globalseen_names, so the first enum claims every member (OKfromenum Statusis emitted withenum_name: 'Color').haskell.py_find_calls— duplicate call records:print (addTwo 1 2)emitsaddTwotwice at the same line.ruby.py:487-507— variablevalueis alwaysNone.node.parentreturns a fresh Python wrapper each access, soid(current)differs for the same underlying node and the@name/@valuecaptures land in different buckets. (n1 == n2isTruewhileid(n1) == id(n2)isFalse.)Minor
c.py:741,cpp.py:426— macroend_lineoff by one; apreproc_defnode'send_pointincludes the trailing newline, so+1double-counts.kotlin.py:1223/:1342,php.py:280/:213—line_numberpoints at the annotation/attribute line rather than the declaration.python.py:274-277deliberately handles this viadecorated_definition, so the declaration line is the established convention.ruby.py:38-47,:516-522—@@class_varsand$globalsare never captured; separately thestartswith("@")test at:517precedesstartswith("@@")at:518, making the"class"branch dead even if the query were fixed.go.py:93—curr.child_by_field_name('type_spec');type_declarationhas no such field and there is no fallback, so_get_parent_contextreturns(None, 'type_declaration', line).solidity.py:238— an unreadable file returns a normal empty result dict rather than the{"path":..., "error":...}shape every other parser returns, so an I/O failure is indexed as "file with no symbols" rather than flagged.Checked and clean
Kotlin imports/aliases and nested/data/sealed class context, extension
receiver_type/return_type, parameter and variable scoping, call receiver inference. Rubyrequire/require_relativeand moduleinclude→basesmerging. PHP class/interface/traitextends/implementsand$this->/self::calls. Javabasesextraction (:453and:472reference nonexistent fields but both have explicit node-type fallbacks). Lua and Perl overall. Everyopen()in the directory already passeserrors=, so the meta-test is satisfied; no parser crashed on any of the 85 fixture files withindex_source=True.Environment
c0e0bed(main)From a parser audit of
tools/languages/. Reproduced by running the parsers directly unless noted otherwise. Higher-impact findings from the same audit are filed separately (#1520-#1527).