mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-04 10:48:20 +00:00
Overloads that share a base QN (Java/C#/C++/Kotlin/Swift/Scala/ObjC) merge
into one graph node today, and that node collects the CALLS of every
overload. The agreed cure is one signature-qualified callable identity for
all overloading languages:
callable_qn := base_qn [<tparams>] (params) [cvref]
This change lands the plumbing with every language still at
CBM_CALLABLE_ID_NONE, so the graph is byte-identical; each language is
switched on later in its own change (with its index-format bump).
- internal/cbm/callable_sig.{c,h}: the per-language mode side table
(NONE|TYPED|LABELED_TYPED|LABELED|ARITY; kept out of CBMLangSpec like
cbm_string_dispatch_suffixes, since ~160 positional rows would otherwise
all need the member under -Wmissing-field-initializers), the shared
builder cbm_callable_sig[_mode] for the def and the call-scope QN, and
the inverse cbm_qn_callable_base_len[_named]. Suffix contract: no '.',
'::' or '->'; whitespace-normalized; comments/annotations/attributes
dropped; qualified types keep their last segment; '->' spelled '=>';
variadic '[]' (Java) / '~'; C++ top-level cv dropped, arrays decay,
cvref kept; <= 200 bytes, longer ones keep whole leading entries and end
in '#<fnv1a64 of the full suffix>)'. The builder is iterative (cursor
walk, bounded declarator loop) and self-checks against the inverse.
- The inverse is anchored to the node's bare name wherever the name is
known: real QNs already end in ')' without being callables (a Java field
whose name is its declarator `X = f()`, make `$(VAR)` targets), and only
the anchor makes the inverse exact on them.
- Leaf splitters strip a suffix first (no-op today): registry by-name key
(indexed by the bare name for a suffixed callable), cbm_lsp_bare_segment,
cbm_pipeline_qn_class_method_tail, pxc_last_component, pxc_qn_leaf,
go_index_visit_node, collect_prod_defs, sg_qn_prefix_len.
- New unused fields: CBMDefinition.qn_sig_off, CBMResolvedCall.callee_sig
(relocated by result compaction), CBMRegisteredFunc.sig, CBMLSPDef.sig.
The lsp_surface codec is unchanged (no format bump in this change).
- get_code_snippet and trace_path gain base-QN tiers
(cbm_store_find_nodes_by_qn_base) that find nothing until suffixes exist.
- store: cbm_store_find_nodes_by_qn_suffix built its LIKE pattern in a
512-byte buffer (a long suffix was truncated into a pattern that matched
nothing) and did not escape '_'/'%' (`my_func` also matched `myXfunc`).
The pattern is now heap-built, escaped, and matched with ESCAPE '\'.
Proof: full sorted node+edge+lsp_surface dumps are byte-identical before
and after on 7 corpora (this repo, elasticsearch libs, Exposed, dotnet
System.Linq, System.Collections, coreclr/gc, a C corpus).
Refs #2061
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
84 lines
4.2 KiB
C++
84 lines
4.2 KiB
C++
/*
|
|
* callable_sig.h — signature-qualified callable identity (#2061).
|
|
*
|
|
* A callable's graph identity is
|
|
*
|
|
* callable_qn := base_qn [<tparams>] (params) [cvref]
|
|
*
|
|
* so overloads that share a base QN (Java/C#/C++/Kotlin/Swift/Scala/ObjC)
|
|
* become distinct nodes instead of merging into one node that collects every
|
|
* overload's CALLS edges. The `name` column stays the bare name.
|
|
*
|
|
* Every language selects a mode (cbm_callable_identity). A language whose
|
|
* mode is CBM_CALLABLE_ID_NONE keeps its historical QN byte for byte; the
|
|
* suffix is applied only where a mode is enabled, and the SAME builder
|
|
* (cbm_callable_sig) serves the definition QN and the call-scope QN so the two
|
|
* can never disagree.
|
|
*
|
|
* Suffix contract (what cbm_qn_callable_base_len inverts):
|
|
* - it never contains '.', "::" or "->" — the separators every QN leaf
|
|
* splitter uses — so a splitter that first strips the suffix finds the
|
|
* same leaf as before;
|
|
* - it is whitespace-normalized: one space only between two identifier
|
|
* characters ("unsigned long", "const T&"), none elsewhere;
|
|
* - type spelling: comments, annotations and attributes are dropped;
|
|
* qualified type paths keep their last segment (java.util.List -> List,
|
|
* std::vector -> vector); "->" in function types is spelled "=>"; a
|
|
* variadic/rest marker is "[]" for Java (the JVM array it is) and "~"
|
|
* everywhere else; C++ top-level cv-qualifiers are dropped and array
|
|
* parameters decay to pointers, as the language itself does;
|
|
* - it is at most CBM_CALLABLE_SIG_MAX bytes. A longer signature keeps the
|
|
* leading whole parameter entries that fit and ends in "#<16 hex>)", the
|
|
* FNV-1a 64 of the full uncapped suffix, so identity is preserved.
|
|
*/
|
|
#ifndef CBM_CALLABLE_SIG_H
|
|
#define CBM_CALLABLE_SIG_H
|
|
|
|
#include "cbm.h"
|
|
#include "arena.h"
|
|
#include "tree_sitter/api.h"
|
|
#include <stddef.h>
|
|
|
|
typedef enum {
|
|
CBM_CALLABLE_ID_NONE = 0, /* historical base QN, no suffix */
|
|
CBM_CALLABLE_ID_TYPED, /* (int,String) */
|
|
CBM_CALLABLE_ID_LABELED_TYPED, /* (_:Data,to:URL) Swift */
|
|
CBM_CALLABLE_ID_LABELED, /* (_:style:) ObjC selector */
|
|
CBM_CALLABLE_ID_ARITY, /* (2) dynamic langs */
|
|
} CBMCallableIdentity;
|
|
|
|
enum { CBM_CALLABLE_SIG_MAX = 200 };
|
|
|
|
/* The identity mode a language uses for callable QNs. Every language is
|
|
* CBM_CALLABLE_ID_NONE until its enable change lands (with its index-format
|
|
* bump). Kept as a side table, like cbm_string_dispatch_suffixes, rather than
|
|
* a CBMLangSpec field: the ~160 positional lang_specs rows would all have to
|
|
* spell the new member under -Wmissing-field-initializers. */
|
|
CBMCallableIdentity cbm_callable_identity(CBMLanguage lang);
|
|
|
|
/* The identity suffix for the callable at `func_node` in the language's own
|
|
* mode, or NULL when that mode is NONE (or the node carries no parameter
|
|
* information the mode can use). Arena-owned. */
|
|
const char *cbm_callable_sig(CBMArena *a, TSNode func_node, const char *source, CBMLanguage lang);
|
|
|
|
/* Same, with an explicit mode — the builder the enable changes and the golden
|
|
* tables exercise before a language is switched on. */
|
|
const char *cbm_callable_sig_mode(CBMArena *a, TSNode func_node, const char *source,
|
|
CBMLanguage lang, CBMCallableIdentity mode);
|
|
|
|
/* Length of the base QN, i.e. `qn` without its callable identity suffix; equal
|
|
* to strlen(qn) when there is none. Purely syntactic (the inverse of the
|
|
* builder's contract above); a bare C++ `operator()` leaf is recognised as a
|
|
* name, not a suffix. NULL -> 0. */
|
|
size_t cbm_qn_callable_base_len(const char *qn);
|
|
|
|
/* The inverse anchored to the callable's bare `name` (the node's name
|
|
* column): the suffix is stripped only when the base QN is `name` or ends in
|
|
* "." / "::" + `name`. Other QN text can end in ')' as well — a Java field
|
|
* whose name is its declarator `X = f()`, a make `$(VAR)` target — and only
|
|
* the anchor makes the inverse exact on it. Use this form wherever the name
|
|
* is known; NULL name -> the unanchored form. */
|
|
size_t cbm_qn_callable_base_len_named(const char *qn, const char *name);
|
|
|
|
#endif /* CBM_CALLABLE_SIG_H */
|