mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-10 09:13:53 +08:00
Maintainer decision (option B) closing the Kotlin property-reference repro's platform divergence at its root instead of tie-breaking the fallback. `holder::handler` (parsed as navigation by the vendored grammar) produced a USAGE edge whose target was chosen by the name-only registry fallback: with a same-named property (Holder.handler) and function (Functions.handler) in the project, the winner was registration order -- readdir order -- so Windows (lexicographic NTFS) borrowed the FUNCTION while macOS happened to pick the property. The graph's answer must not depend on directory enumeration. The occurrence is now claimed by exact, receiver-typed resolution. Five links, each of which was missing: 1. extract side: a Kotlin navigation member read is a semantic-reference candidate at the member occurrence, so an LSP row can claim it. With no row the join finds nothing -- but a candidate no longer falls back to the name-only registry guess, which is the fail-closed direction this PR is built on. 2. kotlin cross resolution: a property READ with a proven receiver emits a CALL_REFERENCE row against the property. The property is not a callable target, so the join can only produce USAGE, never a fabricated CALL_REFERENCE. Value reads only; calls stay with the invocation machinery. 3. receiver typing across files: kotlin_resolve_class_name composed <this module>.<name> for an unimported cross-file type, which can never name a type defined in another file. A per-call unique-short-name map (built from the project defs, ambiguous names fail closed) resolves the annotation to the real registered QN. Hash lookup, no scans. 4. cross registry fields: pxc_map_label dropped Variable defs entirely, so no cross registry ever saw a property. They now flow through (every language's registrar filters by explicit label, so only Kotlin consumes them) and the Kotlin registrar attaches them as fields of their receiver type, hash-bucketed -- a per-type scan would be the registry-tail-scan quadratic pattern. 5. def side: class-body variables now record their declaring class (parent_class) -- previously only methods did. The QN stays module-level, so this is additive metadata: the only structural parent_class consumer is Method-gated (DEFINES_METHOD), verified in pass_definitions/pass_parallel/ pipeline_incremental. Emission targets each field's REAL def QN carried through the field map: kotlin class properties are minted with module-level QNs (proj.Holder.handler, not proj.Holder.Holder.handler), so the composed form would name a node that does not exist and the join would silently drop the edge. The repro now proves the property edge on every platform for the same reason, not by racing readdir. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
757 lines
29 KiB
C
757 lines
29 KiB
C
#ifndef CBM_H
|
|
#define CBM_H
|
|
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include "arena.h"
|
|
#include "tree_sitter/api.h"
|
|
|
|
// Language enum mirrors lang.Language in Go.
|
|
// Order must match lang_specs.c tables.
|
|
typedef enum {
|
|
CBM_LANG_GO = 0,
|
|
CBM_LANG_PYTHON,
|
|
CBM_LANG_JAVASCRIPT,
|
|
CBM_LANG_TYPESCRIPT,
|
|
CBM_LANG_TSX,
|
|
CBM_LANG_RUST,
|
|
CBM_LANG_JAVA,
|
|
CBM_LANG_CPP,
|
|
CBM_LANG_CSHARP,
|
|
CBM_LANG_PHP,
|
|
CBM_LANG_LUA,
|
|
CBM_LANG_SCALA,
|
|
CBM_LANG_KOTLIN,
|
|
CBM_LANG_RUBY,
|
|
CBM_LANG_C,
|
|
CBM_LANG_BASH,
|
|
CBM_LANG_ZIG,
|
|
CBM_LANG_ELIXIR,
|
|
CBM_LANG_HASKELL,
|
|
CBM_LANG_OCAML,
|
|
CBM_LANG_OBJC,
|
|
CBM_LANG_SWIFT,
|
|
CBM_LANG_DART,
|
|
CBM_LANG_PERL,
|
|
CBM_LANG_GROOVY,
|
|
CBM_LANG_ERLANG,
|
|
CBM_LANG_R,
|
|
CBM_LANG_HTML,
|
|
CBM_LANG_CSS,
|
|
CBM_LANG_SCSS,
|
|
CBM_LANG_YAML,
|
|
CBM_LANG_TOML,
|
|
CBM_LANG_HCL,
|
|
CBM_LANG_SQL,
|
|
CBM_LANG_DOCKERFILE,
|
|
// New languages (v0.5 expansion)
|
|
CBM_LANG_CLOJURE,
|
|
CBM_LANG_FSHARP,
|
|
CBM_LANG_JULIA,
|
|
CBM_LANG_VIMSCRIPT,
|
|
CBM_LANG_NIX,
|
|
CBM_LANG_COMMONLISP,
|
|
CBM_LANG_ELM,
|
|
CBM_LANG_FORTRAN,
|
|
CBM_LANG_CUDA,
|
|
CBM_LANG_COBOL,
|
|
CBM_LANG_VERILOG,
|
|
CBM_LANG_EMACSLISP,
|
|
CBM_LANG_JSON,
|
|
CBM_LANG_XML,
|
|
CBM_LANG_MARKDOWN,
|
|
CBM_LANG_MAKEFILE,
|
|
CBM_LANG_CMAKE,
|
|
CBM_LANG_PROTOBUF,
|
|
CBM_LANG_GRAPHQL,
|
|
CBM_LANG_VUE,
|
|
CBM_LANG_SVELTE,
|
|
CBM_LANG_MESON,
|
|
CBM_LANG_GLSL,
|
|
CBM_LANG_INI,
|
|
// Scientific/math languages
|
|
CBM_LANG_MATLAB,
|
|
CBM_LANG_LEAN,
|
|
CBM_LANG_FORM,
|
|
CBM_LANG_MAGMA,
|
|
CBM_LANG_WOLFRAM,
|
|
CBM_LANG_SOLIDITY,
|
|
CBM_LANG_TYPST,
|
|
CBM_LANG_GDSCRIPT,
|
|
CBM_LANG_GLEAM,
|
|
CBM_LANG_POWERSHELL,
|
|
CBM_LANG_PASCAL,
|
|
CBM_LANG_DLANG,
|
|
CBM_LANG_NIM,
|
|
CBM_LANG_SCHEME,
|
|
CBM_LANG_FENNEL,
|
|
CBM_LANG_FISH,
|
|
CBM_LANG_AWK,
|
|
CBM_LANG_ZSH,
|
|
CBM_LANG_TCL,
|
|
CBM_LANG_ADA,
|
|
CBM_LANG_AGDA,
|
|
CBM_LANG_RACKET,
|
|
CBM_LANG_ODIN,
|
|
CBM_LANG_RESCRIPT,
|
|
CBM_LANG_PURESCRIPT,
|
|
CBM_LANG_NICKEL,
|
|
CBM_LANG_CRYSTAL,
|
|
CBM_LANG_TEAL,
|
|
CBM_LANG_HARE,
|
|
CBM_LANG_PONY,
|
|
CBM_LANG_LUAU,
|
|
CBM_LANG_JANET,
|
|
CBM_LANG_SWAY,
|
|
CBM_LANG_NASM,
|
|
CBM_LANG_ASSEMBLY,
|
|
CBM_LANG_ASTRO,
|
|
CBM_LANG_BLADE,
|
|
CBM_LANG_JUST,
|
|
CBM_LANG_GOTEMPLATE,
|
|
CBM_LANG_TEMPL,
|
|
CBM_LANG_LIQUID,
|
|
CBM_LANG_JINJA2,
|
|
CBM_LANG_PRISMA,
|
|
CBM_LANG_HYPRLANG,
|
|
CBM_LANG_DOTENV,
|
|
CBM_LANG_DIFF,
|
|
CBM_LANG_WGSL,
|
|
CBM_LANG_KDL,
|
|
CBM_LANG_JSON5,
|
|
CBM_LANG_JSONNET,
|
|
CBM_LANG_RON,
|
|
CBM_LANG_THRIFT,
|
|
CBM_LANG_CAPNP,
|
|
CBM_LANG_PROPERTIES,
|
|
CBM_LANG_SSHCONFIG,
|
|
CBM_LANG_BIBTEX,
|
|
CBM_LANG_STARLARK,
|
|
CBM_LANG_BICEP,
|
|
CBM_LANG_CSV,
|
|
CBM_LANG_REQUIREMENTS,
|
|
CBM_LANG_HLSL,
|
|
CBM_LANG_VHDL,
|
|
CBM_LANG_SYSTEMVERILOG,
|
|
CBM_LANG_DEVICETREE,
|
|
CBM_LANG_LINKERSCRIPT,
|
|
CBM_LANG_GN,
|
|
CBM_LANG_KCONFIG,
|
|
CBM_LANG_BITBAKE,
|
|
CBM_LANG_SMALI,
|
|
CBM_LANG_TABLEGEN,
|
|
CBM_LANG_ISPC,
|
|
CBM_LANG_CAIRO,
|
|
CBM_LANG_MOVE,
|
|
CBM_LANG_SQUIRREL,
|
|
CBM_LANG_FUNC,
|
|
CBM_LANG_REGEX,
|
|
CBM_LANG_JSDOC,
|
|
CBM_LANG_RST,
|
|
CBM_LANG_BEANCOUNT,
|
|
CBM_LANG_MERMAID,
|
|
CBM_LANG_PUPPET,
|
|
CBM_LANG_PO,
|
|
CBM_LANG_GITATTRIBUTES,
|
|
CBM_LANG_GITIGNORE,
|
|
CBM_LANG_SLANG,
|
|
CBM_LANG_LLVM_IR,
|
|
CBM_LANG_SMITHY,
|
|
CBM_LANG_WIT,
|
|
CBM_LANG_TLAPLUS,
|
|
CBM_LANG_PKL,
|
|
CBM_LANG_GOMOD,
|
|
CBM_LANG_APEX,
|
|
CBM_LANG_SOQL,
|
|
CBM_LANG_SOSL,
|
|
CBM_LANG_KUSTOMIZE, // kustomization.yaml — Kubernetes overlay tool
|
|
CBM_LANG_K8S, // Generic Kubernetes manifest (apiVersion: detected)
|
|
CBM_LANG_PINE, // Pine Script (TradingView indicator / strategy language)
|
|
CBM_LANG_QML, // Qt QML (Qt Modeling Language — declarative UI + embedded JS)
|
|
CBM_LANG_CFSCRIPT, // CFML script dialect (.cfc components — Lucee/ColdFusion)
|
|
CBM_LANG_CFML, // CFML tag dialect (.cfm templates — Lucee/ColdFusion)
|
|
CBM_LANG_MOJO, // Mojo
|
|
CBM_LANG_OBJECTSCRIPT_UDL, // InterSystems ObjectScript UDL (.cls class files)
|
|
CBM_LANG_OBJECTSCRIPT_ROUTINE, // InterSystems ObjectScript routine (.mac/.int/.rtn/.inc)
|
|
CBM_LANG_OBJECTSCRIPT_EXPORT, // InterSystems Studio Export XML (<Export generator="Cache">)
|
|
CBM_LANG_COUNT
|
|
} CBMLanguage;
|
|
|
|
// --- Extraction result structs ---
|
|
|
|
typedef struct {
|
|
const char *name; // short name
|
|
const char *qualified_name; // project.path.name
|
|
const char *label; // "Function", "Method", "Class", "Variable", "Module"
|
|
const char *file_path; // relative path
|
|
uint32_t start_line;
|
|
uint32_t end_line;
|
|
const char *signature; // parameter text (NULL if none)
|
|
const char *return_type; // return type text (NULL if none)
|
|
const char *receiver; // Go method receiver (NULL if none)
|
|
const char *docstring; // leading doc comment (NULL if none)
|
|
const char *parent_class; // enclosing class QN for methods (NULL if none)
|
|
const char **decorators; // NULL-terminated array (NULL if none)
|
|
const char **base_classes; // NULL-terminated array (NULL if none)
|
|
const char **param_names; // NULL-terminated array (NULL if none)
|
|
const char **param_types; // NULL-terminated array (NULL if none)
|
|
const char **signature_param_types; // ordered internal signature types; "?" means unknown
|
|
int signature_param_count; // number of entries in signature_param_types
|
|
const char **return_types; // NULL-terminated array (NULL if none)
|
|
const char *route_path; // HTTP route path from decorator (e.g., "/api/users") or NULL
|
|
const char *route_method; // HTTP method from decorator (e.g., "POST") or NULL
|
|
int complexity; // cyclomatic complexity
|
|
int cognitive; // cognitive complexity (nesting-weighted)
|
|
int loop_count; // number of loop constructs in the body
|
|
int loop_depth; // max nested-loop depth (bottleneck proxy)
|
|
bool is_recursive; // body contains a direct self-call (seed for "recursive")
|
|
int param_count; // number of parameters (large = complexity smell)
|
|
int max_access_depth; // deepest chained member/subscript access (a.b.c.d)
|
|
int linear_scan_in_loop; // count of linear-scan calls (find/contains/indexOf) inside loops
|
|
int alloc_in_loop; // count of allocation/append calls inside loops
|
|
bool recursion_in_loop; // a self-call occurs inside a loop body
|
|
bool unguarded_recursion; // recursive with no self-call guarded by a conditional
|
|
int lines; // body line count
|
|
uint32_t *fingerprint; // MinHash fingerprint (arena-allocated, K values) or NULL
|
|
int fingerprint_k; // number of hash values (CBM_MINHASH_K or 0)
|
|
bool is_exported;
|
|
bool is_abstract;
|
|
bool is_test;
|
|
bool is_entry_point;
|
|
const char *structural_profile; // AST structural profile (arena-allocated) or NULL
|
|
const char *body_tokens; // space-separated raw identifier tokens from body (arena) or NULL
|
|
/* Rust only: raw trait path from the exact `impl Trait for Type` block
|
|
* that declared this method. Kept at the tail so zero-initialised
|
|
* callers in every other language remain ABI/source compatible. */
|
|
const char *impl_trait;
|
|
} CBMDefinition;
|
|
|
|
/* Argument captured from a call expression */
|
|
typedef struct {
|
|
const char *expr; // raw expression text ("payload.info", "MY_URL", "'hello'")
|
|
const char *value; // resolved string value or NULL (constant propagation)
|
|
const char *keyword; // keyword name if keyword arg ("url", "topic_id"), NULL if positional
|
|
int index; // positional index (0-based)
|
|
} CBMCallArg;
|
|
|
|
#define CBM_MAX_CALL_ARGS 8
|
|
|
|
/* Byte offsets are meaningful only within the source buffer that produced
|
|
* them. C/C++/CUDA run both raw and preprocessed extraction passes, and those
|
|
* buffers can contain unrelated occurrences at the same numeric span. */
|
|
typedef enum {
|
|
CBM_SOURCE_ORIGIN_RAW = 0,
|
|
CBM_SOURCE_ORIGIN_PREPROCESSED,
|
|
} CBMSourceOrigin;
|
|
|
|
typedef struct {
|
|
const char *callee_name; // raw callee text ("pkg.Func", "foo")
|
|
const char *enclosing_func_qn; // QN of enclosing function (or module QN)
|
|
const char *first_string_arg; // first string literal argument (URL, topic, key) or NULL
|
|
const char *second_arg_name; // second argument identifier (handler ref) or NULL
|
|
CBMCallArg args[CBM_MAX_CALL_ARGS]; // first N arguments with expressions
|
|
int arg_count; // number of captured arguments
|
|
int loop_depth; // enclosing loop nesting at the call site
|
|
int branch_depth; // enclosing branch nesting at the call site
|
|
int start_line; // 1-based source line of the call (for def range-match)
|
|
uint32_t site_start_byte; // exact AST occurrence span; end > start when present
|
|
uint32_t site_end_byte; // exclusive byte offset in the source file
|
|
CBMSourceOrigin source_origin; // raw source or C-family preprocessed buffer
|
|
bool is_method; // method/member call with a non-self receiver. Perl:
|
|
// arrow/method call ($obj->m). TS/JS/TSX: member call
|
|
// x.foo() whose receiver is not this/super. Default false.
|
|
bool requires_lsp_resolution; // synthetic semantic candidate (for example an implicit
|
|
// C++ operator). Never fall back to textual resolution.
|
|
} CBMCall;
|
|
|
|
typedef struct {
|
|
const char *local_name; // local alias or name
|
|
const char *module_path; // resolved module path / QN
|
|
} CBMImport;
|
|
|
|
typedef enum {
|
|
CBM_USAGE_VALUE = 0,
|
|
CBM_USAGE_CALL_REFERENCE,
|
|
} CBMUsageKind;
|
|
|
|
typedef struct {
|
|
const char *ref_name; // referenced identifier
|
|
const char *enclosing_func_qn; // QN of enclosing function (or module QN)
|
|
CBMUsageKind kind; // ordinary USAGE or explicit callable reference
|
|
bool may_be_call_reference; // syntactic candidate; exact LSP proof may upgrade its edge
|
|
bool semantic_reference_blocked; // lexical evidence blocks only unproven textual fallback
|
|
bool semantic_reference_local_shadow; // blocker belongs to a non-module lexical scope
|
|
uint32_t lexical_scope_id; // extraction-local scope instance; never graph identity
|
|
uint32_t site_start_byte; // exact reference-token span; end > start when present
|
|
uint32_t site_end_byte; // exclusive byte offset in the source file
|
|
CBMSourceOrigin source_origin; // raw source or C-family preprocessed buffer
|
|
} CBMUsage;
|
|
|
|
typedef struct {
|
|
const char *exception_name; // exception class/type name
|
|
const char *enclosing_func_qn; // QN of enclosing function
|
|
} CBMThrow;
|
|
|
|
typedef struct {
|
|
const char *var_name; // variable name
|
|
const char *enclosing_func_qn; // QN of enclosing function
|
|
bool is_write; // true = write, false = read
|
|
} CBMReadWrite;
|
|
|
|
typedef struct {
|
|
const char *type_name; // referenced type/class name
|
|
const char *enclosing_func_qn; // QN of enclosing function
|
|
} CBMTypeRef;
|
|
|
|
typedef struct {
|
|
const char *env_key; // environment variable key
|
|
const char *enclosing_func_qn; // QN of enclosing function
|
|
} CBMEnvAccess;
|
|
|
|
typedef struct {
|
|
const char *var_name; // variable being assigned
|
|
const char *type_name; // class/type name of RHS constructor
|
|
const char *enclosing_func_qn; // QN of enclosing function
|
|
} CBMTypeAssign;
|
|
|
|
// String reference: URL, config key, or async target found in source.
|
|
// Extracted from string literals during AST walk.
|
|
typedef enum {
|
|
CBM_STRREF_URL = 0, // REST path or full URL
|
|
CBM_STRREF_CONFIG = 1, // config file path or env var key
|
|
} CBMStringRefKind;
|
|
|
|
typedef struct {
|
|
const char *value; // the string literal content
|
|
const char *enclosing_func_qn; // QN of enclosing function
|
|
const char *key_path; // dotted key path from YAML/JSON nesting (NULL if flat)
|
|
CBMStringRefKind kind; // URL, CONFIG
|
|
} CBMStringRef;
|
|
|
|
/* Infrastructure binding: topic/queue → endpoint URL.
|
|
* Extracted from YAML/HCL/JSON subscription/scheduler configs.
|
|
* Used by pass_route_nodes to connect async Route nodes to handler services. */
|
|
typedef struct {
|
|
const char *source_name; // topic, queue, or schedule name
|
|
const char *target_url; // push_endpoint, uri, or http_target URL
|
|
const char *broker; // "pubsub", "cloud_tasks", "cloud_scheduler", "sqs", "kafka"
|
|
} CBMInfraBinding;
|
|
|
|
/* Pub/sub channel participation. One record per emit() or on()/addListener()
|
|
* call detected in source — the receiver (e.g. Socket.IO client, EventEmitter
|
|
* instance) is intentionally NOT identified; matching is by channel_name
|
|
* across files, which captures the common pattern of one logical bus per
|
|
* service. Transport disambiguates Socket.IO vs EventEmitter vs future
|
|
* detectors (Kafka, Cloud Pub/Sub, etc.). */
|
|
typedef enum {
|
|
CBM_CHANNEL_EMIT = 0,
|
|
CBM_CHANNEL_LISTEN = 1,
|
|
} CBMChannelDirection;
|
|
|
|
typedef struct {
|
|
const char *channel_name; // literal channel name (e.g. "user.created")
|
|
const char *transport; // "socketio", "event_emitter", ...
|
|
const char *enclosing_func_qn; // QN of the function containing the emit/on call
|
|
CBMChannelDirection direction;
|
|
} CBMChannel;
|
|
|
|
// Rust: impl Trait for Struct
|
|
typedef struct {
|
|
const char *trait_name; // trait name (raw text)
|
|
const char *struct_name; // struct/type name (raw text)
|
|
/* Exact extracted QN of the implementing type. Unlike struct_name this
|
|
* does not need a later leaf-name guess, and the relation exists even for
|
|
* an empty `impl Trait for Type {}` block. */
|
|
const char *struct_qn;
|
|
} CBMImplTrait;
|
|
|
|
typedef enum {
|
|
CBM_RESOLVED_INVOCATION = 0,
|
|
CBM_RESOLVED_CALL_REFERENCE,
|
|
} CBMResolvedKind;
|
|
|
|
// LSP-resolved invocation/reference: high-confidence type-aware resolution.
|
|
typedef struct {
|
|
const char *caller_qn; // enclosing function QN
|
|
const char *callee_qn; // resolved target QN (fully qualified)
|
|
const char *strategy; // "lsp_type_dispatch", "lsp_direct", etc.
|
|
float confidence; // 0.90-0.95
|
|
const char *reason; // diagnostic label for unresolved calls (NULL if resolved)
|
|
CBMResolvedKind kind; // invocation (CALLS) or explicit callable reference
|
|
uint32_t site_start_byte; // exact source occurrence; end > start when present
|
|
uint32_t site_end_byte; // exclusive byte offset in the source file
|
|
CBMSourceOrigin source_origin; // raw source or C-family preprocessed buffer
|
|
} CBMResolvedCall;
|
|
|
|
typedef struct {
|
|
CBMResolvedCall *items;
|
|
int count;
|
|
int cap;
|
|
} CBMResolvedCallArray;
|
|
|
|
// Growable arrays used during extraction.
|
|
typedef struct {
|
|
CBMDefinition *items;
|
|
int count;
|
|
int cap;
|
|
} CBMDefArray;
|
|
|
|
typedef struct {
|
|
CBMCall *items;
|
|
int count;
|
|
int cap;
|
|
} CBMCallArray;
|
|
|
|
typedef struct {
|
|
CBMImport *items;
|
|
int count;
|
|
int cap;
|
|
} CBMImportArray;
|
|
|
|
typedef struct {
|
|
CBMUsage *items;
|
|
int count;
|
|
int cap;
|
|
} CBMUsageArray;
|
|
|
|
typedef struct {
|
|
CBMThrow *items;
|
|
int count;
|
|
int cap;
|
|
} CBMThrowArray;
|
|
|
|
typedef struct {
|
|
CBMReadWrite *items;
|
|
int count;
|
|
int cap;
|
|
} CBMRWArray;
|
|
|
|
typedef struct {
|
|
CBMTypeRef *items;
|
|
int count;
|
|
int cap;
|
|
} CBMTypeRefArray;
|
|
|
|
typedef struct {
|
|
CBMEnvAccess *items;
|
|
int count;
|
|
int cap;
|
|
} CBMEnvAccessArray;
|
|
|
|
typedef struct {
|
|
CBMTypeAssign *items;
|
|
int count;
|
|
int cap;
|
|
} CBMTypeAssignArray;
|
|
|
|
typedef struct {
|
|
CBMStringRef *items;
|
|
int count;
|
|
int cap;
|
|
} CBMStringRefArray;
|
|
|
|
typedef struct {
|
|
CBMInfraBinding *items;
|
|
int count;
|
|
int cap;
|
|
} CBMInfraBindingArray;
|
|
|
|
typedef struct {
|
|
CBMImplTrait *items;
|
|
int count;
|
|
int cap;
|
|
} CBMImplTraitArray;
|
|
|
|
typedef struct {
|
|
CBMChannel *items;
|
|
int count;
|
|
int cap;
|
|
} CBMChannelArray;
|
|
|
|
// Full extraction result for one file.
|
|
typedef struct CBMFileResult {
|
|
CBMArena arena; // owns local memory; composites may also retain child arenas below
|
|
|
|
CBMDefArray defs;
|
|
CBMCallArray calls;
|
|
CBMImportArray imports;
|
|
CBMUsageArray usages;
|
|
CBMThrowArray throws;
|
|
CBMRWArray rw;
|
|
CBMTypeRefArray type_refs;
|
|
CBMEnvAccessArray env_accesses;
|
|
CBMTypeAssignArray type_assigns;
|
|
CBMImplTraitArray impl_traits; // Rust: impl Trait for Struct pairs
|
|
CBMResolvedCallArray resolved_calls; // LSP-resolved invocations/references (high confidence)
|
|
CBMStringRefArray string_refs; // URL/config string literals from AST
|
|
CBMInfraBindingArray infra_bindings; // topic→URL pairs from IaC configs
|
|
CBMChannelArray channels; // Socket.IO / EventEmitter pub/sub participation
|
|
|
|
const char *module_qn; // module qualified name
|
|
const char *namespace_name; // declared namespace/package (Java/Kotlin/C#/PHP), NULL if none
|
|
const char **exports; // NULL-terminated (NULL if none)
|
|
const char **constants; // NULL-terminated (NULL if none)
|
|
const char **global_vars; // NULL-terminated (NULL if none)
|
|
const char **macros; // NULL-terminated, C/C++ only (NULL if none)
|
|
|
|
bool has_error;
|
|
const char *error_msg;
|
|
/* Best-effort parse-coverage signal (experimental). parse_incomplete is true
|
|
* when the parse tree contains tree-sitter ERROR/MISSING nodes — constructs
|
|
* in those regions are silently absent from the graph. error_ranges is a
|
|
* compact "start-end,start-end" list of 1-based line ranges (arena-owned) or
|
|
* NULL. This only marks what we can DETECT: the absence of a flag is NOT a
|
|
* completeness guarantee. Callers should treat a flagged file as "prefer
|
|
* grep here", never treat an unflagged file as provably complete. */
|
|
bool parse_incomplete;
|
|
const char *error_ranges;
|
|
int error_region_count;
|
|
bool is_test_file;
|
|
int imports_count;
|
|
TSTree *cached_tree; // retained parse tree (caller frees via cbm_free_tree)
|
|
CBMLanguage cached_lang; // language of cached tree (for parser selection)
|
|
|
|
// Retained source bytes — copied into `arena` by the parallel
|
|
// extract pass so the fused cross-file LSP step in resolve_worker
|
|
// can run without re-reading the file from disk. NULL when the
|
|
// file exceeded the per-file (100 MB) or total (2 GB) retention
|
|
// cap; in that case the cross-file LSP step is skipped for this
|
|
// file (defs/calls already extracted are unaffected).
|
|
const char *source;
|
|
int source_len;
|
|
|
|
// Composite extraction results (currently ObjectScript Studio Export)
|
|
// retain their per-unit results so shallow-copied carrier strings remain
|
|
// valid for the composite's full lifetime. Owned and recursively released
|
|
// by cbm_free_result(); ordinary single-file results leave these zeroed.
|
|
struct CBMFileResult **owned_results;
|
|
int owned_result_count;
|
|
} CBMFileResult;
|
|
|
|
// --- Enclosing function cache ---
|
|
// Avoids repeated parent-chain walks for nodes within the same function body.
|
|
// Each entry records a function's byte range and its precomputed QN.
|
|
#define EFC_SIZE 64 // power of 2 for fast modulo
|
|
|
|
typedef struct {
|
|
uint32_t start_byte;
|
|
uint32_t end_byte;
|
|
const char *qn;
|
|
} EFCEntry;
|
|
|
|
typedef struct {
|
|
EFCEntry entries[EFC_SIZE];
|
|
int count;
|
|
} EFCache;
|
|
|
|
// --- Extraction context passed to sub-extractors ---
|
|
|
|
// Module-level string constant map (for constant propagation)
|
|
#define CBM_MAX_STRING_CONSTANTS 256
|
|
typedef struct {
|
|
const char *names[CBM_MAX_STRING_CONSTANTS];
|
|
const char *values[CBM_MAX_STRING_CONSTANTS];
|
|
int count;
|
|
} CBMStringConstantMap;
|
|
|
|
// Forward declaration: ObjectScript macro table (defined in macro_table.h).
|
|
typedef struct CBMMacroTable CBMMacroTable;
|
|
|
|
// Method-return-type table for ObjectScript variable type inference. Populated
|
|
// from definition nodes (method QN -> declared return type) so a later
|
|
// `Set x = obj.Method()` can resolve x's class.
|
|
#define CBM_RETURN_TYPE_TABLE_CAP 2048
|
|
|
|
typedef struct {
|
|
const char *method_qn;
|
|
const char *return_type;
|
|
} CBMReturnTypeEntry;
|
|
|
|
typedef struct {
|
|
CBMReturnTypeEntry entries[CBM_RETURN_TYPE_TABLE_CAP];
|
|
int count;
|
|
} CBMReturnTypeTable;
|
|
|
|
typedef struct {
|
|
CBMArena *arena;
|
|
CBMFileResult *result;
|
|
const char *source;
|
|
int source_len;
|
|
CBMLanguage language;
|
|
const char *project;
|
|
const char *rel_path;
|
|
const char *module_qn;
|
|
TSNode root;
|
|
EFCache ef_cache; // enclosing function cache
|
|
const char *enclosing_class_qn; // for nested class QN computation
|
|
CBMStringConstantMap string_constants; // module-level NAME = "value" pairs
|
|
const CBMMacroTable *macro_table; // ObjectScript $$$macro table (NULL if none)
|
|
const CBMReturnTypeTable *return_type_table; // ObjectScript method return types (NULL if none)
|
|
/* Set by extract_class_variables around its extract_var_names calls, so a
|
|
* class-body variable def records which class declares it (parent_class)
|
|
* without changing its module-level qualified name. NULL elsewhere. */
|
|
const char *var_parent_class;
|
|
} CBMExtractCtx;
|
|
|
|
// --- Public API ---
|
|
|
|
// Bind third-party allocators (tree-sitter, sqlite3) to mimalloc as
|
|
// defense-in-depth, so they never depend on the fragile MI_OVERRIDE symbol
|
|
// override (#424). MUST be called as the very first statement of main(), before
|
|
// any sqlite3_open*/sqlite3_initialize (SQLITE_CONFIG_MALLOC returns
|
|
// SQLITE_MISUSE once sqlite has initialized).
|
|
// Idempotent (static guard); intended for single-threaded startup. cbm_init()
|
|
// also calls it so non-main entry points (pipeline passes) still get the binds.
|
|
// In the test build (no CBM_BIND_TS_ALLOCATOR) this is a no-op.
|
|
void cbm_alloc_init(void);
|
|
|
|
// Initialize the library. Call once at startup. Returns 0 on success.
|
|
int cbm_init(void);
|
|
|
|
// True when rel_path is in the crash-quarantine set — the newline-delimited list
|
|
// of files (CBM_INDEX_QUARANTINE_FILE) the crash supervisor pinned as crashers
|
|
// during its single-threaded recovery re-run. Loaded once, lazily; read-only
|
|
// after load. cbm_extract_file short-circuits such files to an empty result so no
|
|
// pass can crash on them; the pipeline extract loops call this to also REPORT the
|
|
// skip as phase="crash". Always false (cheap no-op) when the env var is unset.
|
|
bool cbm_index_is_quarantined(const char *rel_path);
|
|
|
|
// Phase a quarantined file was pinned under: "crash" (a fault signal) or "hang"
|
|
// (killed for making no progress). Returns NULL when rel_path is not quarantined.
|
|
// Drives the same lazy once-load as cbm_index_is_quarantined. Used by the pipeline
|
|
// extract loops to report the skip's phase in skipped[] (falls back to "crash").
|
|
const char *cbm_index_quarantine_phase(const char *rel_path);
|
|
|
|
// Crash-supervisor marker journal (parallel-safe): appends "S <rel_path>" /
|
|
// "D <rel_path>" to CBM_INDEX_MARKER_FILE. Files with an S but no D form the
|
|
// parent's crash/hang suspect set. No-ops when the env var is unset.
|
|
// cbm_extract_file journals its own start/done; long-running per-file phases
|
|
// (cross-LSP resolve) call these around their per-file work so a hang there
|
|
// is attributed to the RIGHT file instead of a stale extraction marker.
|
|
void cbm_index_mark_start(const char *rel_path);
|
|
void cbm_index_mark_done(const char *rel_path);
|
|
|
|
// Extract all data from one file. Caller must call cbm_free_result().
|
|
// source must remain valid for the duration of the call.
|
|
// timeout_micros: per-file parse timeout in microseconds (0 = no timeout).
|
|
CBMFileResult *cbm_extract_file(const char *source, int source_len, CBMLanguage language,
|
|
const char *project, const char *rel_path, int64_t timeout_micros,
|
|
const char **extra_defines, // NULL-terminated, or NULL
|
|
const char **include_paths // NULL-terminated, or NULL
|
|
);
|
|
|
|
// Pipeline-internal variant of cbm_extract_file() carrying ObjectScript
|
|
// per-project tables (macro table + method-return-type table). The public
|
|
// cbm_extract_file() is a thin wrapper that passes NULL, NULL for both.
|
|
CBMFileResult *cbm_extract_file_ex(
|
|
const char *source, int source_len, CBMLanguage language, const char *project,
|
|
const char *rel_path, int64_t timeout_micros,
|
|
const char **extra_defines, // NULL-terminated, or NULL
|
|
const char **include_paths, // NULL-terminated, or NULL
|
|
const CBMMacroTable *macro_table, // ObjectScript macros, or NULL
|
|
const CBMReturnTypeTable *return_type_table // OS return types, or NULL
|
|
);
|
|
|
|
// Free all memory associated with a result.
|
|
void cbm_free_result(CBMFileResult *result);
|
|
|
|
// Free only the cached tree from a result (caller retained it for reuse).
|
|
void cbm_free_tree(CBMFileResult *result);
|
|
|
|
// Free a standalone TSTree pointer (for Go layer cleanup).
|
|
void cbm_free_tree_ptr(TSTree *tree);
|
|
|
|
// Reset the thread-local parser's internal state, releasing slab-allocated
|
|
// subtrees. Must be called BEFORE cbm_slab_reset_thread() so the slab rebuild
|
|
// doesn't corrupt live parser state.
|
|
void cbm_reset_thread_parser(void);
|
|
|
|
// Destroy the thread-local parser. Call on worker thread exit.
|
|
void cbm_destroy_thread_parser(void);
|
|
|
|
// Shutdown the library. Call once at exit.
|
|
void cbm_shutdown(void);
|
|
|
|
// Profiling: get accumulated parse/extraction times and file count.
|
|
typedef struct {
|
|
uint64_t *parse_ns;
|
|
uint64_t *extract_ns;
|
|
uint64_t *files;
|
|
} cbm_profile_out_t;
|
|
void cbm_get_profile(cbm_profile_out_t out);
|
|
uint64_t cbm_get_lsp_ns(void);
|
|
uint64_t cbm_get_preprocess_ns(void);
|
|
uint64_t cbm_get_files_preprocessed(void);
|
|
void cbm_reset_profile(void);
|
|
|
|
#if defined(CBM_KOTLIN_DEDUP_TEST_API) && CBM_KOTLIN_DEDUP_TEST_API
|
|
// Test-build-only operation counter for Kotlin operator-carrier deduplication.
|
|
// Production builds do not expose or retain this instrumentation.
|
|
void cbm_kotlin_operator_dedup_test_reset(void);
|
|
uint64_t cbm_kotlin_operator_dedup_test_comparisons(void);
|
|
#endif
|
|
|
|
#if defined(CBM_CALL_REFERENCE_LOOKUP_TEST_API) && CBM_CALL_REFERENCE_LOOKUP_TEST_API
|
|
// Test-build-only work counter for resolving a node's field role while
|
|
// classifying value references. Production builds retain no instrumentation.
|
|
void cbm_usage_field_lookup_test_reset(void);
|
|
uint64_t cbm_usage_field_lookup_test_work(void);
|
|
uint64_t cbm_usage_slow_parent_fallback_test_count(void);
|
|
#endif
|
|
|
|
// Toggle C/C++ preprocessor Macro-node extraction (#375). The pipeline enables
|
|
// it only for full/advanced index modes (it dominates extraction on macro-dense
|
|
// codebases). Default ON. Set before extraction; read-only during.
|
|
void cbm_set_macro_extraction(int enabled);
|
|
int cbm_macro_extraction_enabled(void);
|
|
|
|
// --- Internal helpers used by extractors ---
|
|
|
|
// Growable array push functions (arena-allocated, no individual free needed).
|
|
void cbm_defs_push(CBMDefArray *arr, CBMArena *a, CBMDefinition def);
|
|
void cbm_calls_push(CBMCallArray *arr, CBMArena *a, CBMCall call);
|
|
void cbm_imports_push(CBMImportArray *arr, CBMArena *a, CBMImport imp);
|
|
void cbm_usages_push(CBMUsageArray *arr, CBMArena *a, CBMUsage usage);
|
|
void cbm_throws_push(CBMThrowArray *arr, CBMArena *a, CBMThrow thr);
|
|
void cbm_rw_push(CBMRWArray *arr, CBMArena *a, CBMReadWrite rw);
|
|
void cbm_typerefs_push(CBMTypeRefArray *arr, CBMArena *a, CBMTypeRef tr);
|
|
void cbm_envaccess_push(CBMEnvAccessArray *arr, CBMArena *a, CBMEnvAccess ea);
|
|
void cbm_typeassign_push(CBMTypeAssignArray *arr, CBMArena *a, CBMTypeAssign ta);
|
|
void cbm_stringref_push(CBMStringRefArray *arr, CBMArena *a, CBMStringRef sr);
|
|
void cbm_infrabinding_push(CBMInfraBindingArray *arr, CBMArena *a, CBMInfraBinding ib);
|
|
void cbm_impltrait_push(CBMImplTraitArray *arr, CBMArena *a, CBMImplTrait it);
|
|
void cbm_resolvedcall_push(CBMResolvedCallArray *arr, CBMArena *a, CBMResolvedCall rc);
|
|
void cbm_channels_push(CBMChannelArray *arr, CBMArena *a, CBMChannel ch);
|
|
|
|
// --- Sub-extractor entry points ---
|
|
|
|
void cbm_extract_definitions(CBMExtractCtx *ctx);
|
|
void cbm_extract_imports(CBMExtractCtx *ctx);
|
|
void cbm_extract_usages(CBMExtractCtx *ctx);
|
|
void cbm_extract_semantic(CBMExtractCtx *ctx);
|
|
void cbm_extract_type_refs(CBMExtractCtx *ctx);
|
|
void cbm_extract_env_accesses(CBMExtractCtx *ctx);
|
|
void cbm_extract_type_assigns(CBMExtractCtx *ctx);
|
|
void cbm_extract_channels(CBMExtractCtx *ctx);
|
|
|
|
// Single-pass unified extraction (replaces the 7 calls above except defs+imports).
|
|
void cbm_extract_unified(CBMExtractCtx *ctx);
|
|
|
|
// K8s / Kustomize semantic extractor (called when language is CBM_LANG_K8S or CBM_LANG_KUSTOMIZE).
|
|
void cbm_extract_k8s(CBMExtractCtx *ctx);
|
|
|
|
// --- Label predicates ---
|
|
|
|
// True when `label` names a TYPE-LIKE container definition — a node that can own
|
|
// methods/fields, be a base/embedded type, satisfy/declare an interface, and be a
|
|
// target of name→type resolution. The canonical set is:
|
|
// Class, Struct, Interface, Enum, Type, Trait.
|
|
// Single source of truth for every type-resolution / registry-seeding /
|
|
// INHERITS·IMPLEMENTS / LSP-type-registrar consumer, so adding a new type-like
|
|
// label (e.g. "Struct" for Rust/Go/Swift/D structs) updates them all at once
|
|
// instead of scattering `|| strcmp(label,"Struct")==0` across the tree.
|
|
// `label` may be NULL (returns false). Defined in helpers.c.
|
|
bool cbm_label_is_type_like(const char *label);
|
|
|
|
#endif // CBM_H
|