Repository navigation
Scratch/riel/identity v12 - #107
Merged
rikvanriel merged 10 commits intoNov 7, 2026
Merged
Conversation
Version 12 changes what a row means: line_start joins the functions merge key and types gains a qualifier column, so rows an older build wrote cannot be merged correctly and start again. The drop happens only when a processed record still names an older extractor, which proves old rows are in the tables; a lost mark on its own restamps without dropping, so current rows are never left without the files that produced them. The staleness decision is taken before the processed migration empties the table: a real v11 index carries an Int64 extractor_version, which the migration drops, so checking after it would find nothing and keep stale rows beside their re-read replacements. Covered by a test rebuilding the real v11 Int64 shape and asserting both tables start again.
Two definitions at different lines are two rows now: the merge key grows line_start, and the within-file dedup folds a declaration into its same-key definition -- unconditionally, since no language here overloads -- while never merging two definitions. Names differ between a prototype and its body anyway, so `baz()` meets `baz(void)` and `int a[]` meets `int *a`. Rows that share a guard and qualifier coexist instead of collapsing; the chain walk learns the same distinction, gathering only arms whose guard differs from the chosen row's. one_per_file keeps one voice per file per owner by line order, so a stub arm with a body still cannot outrank the file's first definition; arms beside it carry the guard rule instead, with siblings reported next to the chosen row rather than dropped.
C++ files now parse with the C++ grammar, so member declarations the C grammar never read are extracted. .cpp and friends map to a new Language::Cpp with its own parser, queries and header routing; headers stay C by default, reading as C++ only where the index records it.
Rust keeps its module-and-owner qualifier. C++ takes the owner from the qualified-id out of line and from the enclosing namespaces and classes in line, keeping explicit-specialisation arguments and stripping a primary template's own parameters so the definition folds into its declaration. Python carries pkg.mod.Class with <locals> for nested functions, Zig carries mod.Struct, and C carries none.
Only C++ overloads, so only there does a declaration match the definition it announces, comparing canonicalised parameter types (*/\& spacing folded away); an overload with no body in this file survives beside the definitions. C++ reference declarators carry no declarator field, so the name is stripped from them explicitly: const Box &o in the class folds into const Box& o out of line. A declarator returning a pointer or a reference wraps the function (char *name(), S &self()), so each arm repeats its declarator bare and wrapped in a pointer once and twice and in a reference; the name, return type and parameters capture identically in each, and the qualified-id scope read walks through the wrapper the same way. Both declaration arms capture the return type instead of storing the void default. A brace inside the parameter list is a default argument rather than a body, so the prototype test ignores parenthesised groups; a const or volatile after the last * or & is top-level and canonicalises away. A friend is a non-member: the enclosing walk skips the class a friend is written in, so the declaration takes the namespace owner and folds into its definition. A conversion operator has no operator_name, so the operator_cast itself is captured as the name with its target as the return type. A function-try-block body reads as a definition, and reference-to-pointer declarators (int *&slot()) have their own arm. A C++20 constrained declaration keeps its requirement braces outside every parenthesis and still reads as a definition; that limit is documented and pinned.
A types row gains a qualifier column in its merge key, filled from the same owner function that writes functions.qualifier: one file's ns1::Box and ns2::Box coexist, and a receiver type joins to its methods' qualifier. The column is appended and read by name, so older rows read back without it.
Rust reads its #[cfg] predicates from the attributes above each item, C++ shares the C preprocessor reading, Python records if conditions and try arms opaquely, and Zig records comptime conditions. Guards filter only: nothing but Kconfig atoms ever contributes a fact, so opaque guards never prune a chain.
A call stores as written: qualified paths and template arguments kept, so the full path matches first and the last segment after. The calls column becomes an array of typed edges; this commit writes plain spellings only, and lookups project each edge to the callee it names.
The sole rewrite is Rust Self::, resolved through the enclosing impl. A member call through this or self, and a bare call in scope, carries its enclosing owner beside it in the same JSON column; lookups project the tag away or read both halves, with arguments optional.
Each file records the version of the language that read it, so a tree-sitter bump re-reads only its language while shared logic still moves them all together. A header records the language it was read as, which is what makes flipping the routing rescan the headers; the run's choice is recorded beside the other build options in schema_meta. Header routing is not configurable yet: the analyzer always reads .h as C, so the record pins false, the same value the row versions consult. When the option is wired, plumb that value to both the analyzer and the record.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Semcode conflated same-named functions across lines, overloads, preprocessor arms, and languages because the functions merge key couldn't tell them apart. This series bumps the schema to v12 and fixes identity end to end: line_start joins the merge key so definitions on different lines coexist while declarations still fold into the definition they announce; each language fills qualifiers in its own spelling ( Rust modules, C++ namespaces/classes, Python pkg.mod.Class, Zig mod.Struct) with the C++ frontend moving to tree-sitter-cpp; declarations match definitions by canonicalised parameter types; preprocessor arms are recorded as written; callee spellings are stored verbatim in typed edges with this/self calls tagged by their enclosing owner; and each extractor records its version with header routing. Verified with cargo fmt --check and cargo clippy --all-targets clean and the full suite passing