Files
codebase-memory-mcp/internal/cbm/resolve/capabilities.c
T
Martin Vogel 7a13f401eb wip(resolver): JavaScript and TypeScript modules, scopes and exports
Checkpoint against a usage limit. Proved on express and kilocode subtrees; the
test suites have not run on this tree yet, so later commits may adjust it.

- Real lexical scopes replace the module-wide veto on any local name.
- CommonJS syntax, module extensions, package.json type and TypeScript prove a
  module; a classic browser script stays unproved.
- require bindings and an exported-name table: aliases, default exports, barrels,
  export *, export * as, module.exports and exports.x.
- Directory imports resolve through main and index.
- TS method proofs survive destructuring, catch, generics and decorators, take
  own and inherited methods, this.field and new X() receivers, and have no budget.
- TS annotations give exact type-name occurrences.
- A registered route suppresses the unresolved diagnostic again.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-11-01 14:07:04 +01:00

335 lines
18 KiB
C

#include "capabilities.h"
#include "../lang_specs.h"
#include <string.h>
static bool syntax(const char *const *kinds) {
return kinds && kinds[0];
}
/* Positive rows are audited producer subsets, independent of parser syntax.
* Unlisted languages and relations keep the explicit unsupported default. */
#define EDGE(name) (1u << CBM_RESOLVE_EDGE_##name)
#define CALLABLE_EDGES (EDGE(CALLS) | EDGE(CALL_REFERENCE))
#define C_EDGES (EDGE(IMPORTS) | CALLABLE_EDGES | EDGE(USAGE) | EDGE(READS) | EDGE(WRITES))
#define SOURCE_EDGES (EDGE(IMPORTS) | CALLABLE_EDGES | EDGE(USAGE))
#define TYPED_EDGES (EDGE(IMPORTS) | EDGE(CALLS) | EDGE(USES_TYPE))
typedef struct {
CBMLanguage language;
unsigned supported;
const char *imports;
} edge_row_t;
static const edge_row_t edge_rows[] = {
{CBM_LANG_GO, SOURCE_EDGES,
"Exact project package paths with configured roots; unresolved external packages abstain."},
{CBM_LANG_C, C_EDGES,
"Literal include paths and configured search roots only; unproved preprocessing abstains."},
{CBM_LANG_CPP, C_EDGES,
"Literal include paths and configured search roots only; unproved preprocessing abstains."},
{CBM_LANG_CUDA, C_EDGES,
"Literal include paths and configured search roots only; unproved preprocessing abstains."},
{CBM_LANG_OBJC, EDGE(IMPORTS),
"Literal include paths and configured search roots only; no semantic proof bridge "
"advertised."},
{CBM_LANG_JAVASCRIPT, SOURCE_EDGES | EDGE(WRITES),
"Exact relative modules, directory entries and configured package roots, ES and CommonJS "
"export surfaces; runtime module paths and classic browser scripts abstain."},
{CBM_LANG_TYPESCRIPT, SOURCE_EDGES | EDGE(WRITES) | EDGE(USES_TYPE),
"Exact relative modules, directory entries and configured package roots, ES and CommonJS "
"export surfaces; runtime module paths abstain."},
{CBM_LANG_TSX, SOURCE_EDGES | EDGE(WRITES) | EDGE(USES_TYPE),
"Exact relative modules, directory entries and configured package roots, ES and CommonJS "
"export surfaces; runtime module paths abstain."},
{CBM_LANG_ARKTS, SOURCE_EDGES,
"Source JavaScript-style imports only; containing-language typed dispatch is unsupported."},
{CBM_LANG_VUE, CALLABLE_EDGES,
"One inline script with audited lexical bindings and an inert template; multiple script "
"contexts, template effects and external script sources abstain."},
{CBM_LANG_SVELTE, CALLABLE_EDGES,
"One inline script with audited lexical bindings; only static markup and bare-identifier "
"event references are supported. Multiple contexts and template effects abstain."},
{CBM_LANG_PYTHON, SOURCE_EDGES | EDGE(USES_TYPE),
"Static project imports and bounded reexports; dynamic imports and unknown package roots "
"abstain."},
{CBM_LANG_JAVA, EDGE(IMPORTS) | CALLABLE_EDGES,
"Exact declared package members; incomplete or competing namespace candidates abstain."},
{CBM_LANG_KOTLIN, TYPED_EDGES | EDGE(CALL_REFERENCE) | EDGE(USAGE),
"Exact declared packages and aliases; incomplete or competing namespace candidates abstain."},
{CBM_LANG_CSHARP, TYPED_EDGES,
"Exact declared namespace members; unproved aliases and competing candidates abstain."},
{CBM_LANG_PHP, TYPED_EDGES,
"Declared namespaces, Composer PSR-4 and literal includes; dynamic paths abstain."},
{CBM_LANG_RUST, TYPED_EDGES,
"Explicit crate/module trees and use paths; macros, attributes and unknown roots abstain."},
{CBM_LANG_DART, EDGE(IMPORTS) | EDGE(CALLS),
"Direct URI imports with prefix and show/hide bindings; parts and reexports are unsupported."},
{CBM_LANG_LUA, EDGE(CALLS), NULL},
{CBM_LANG_CSS, EDGE(IMPORTS),
"Top-level quoted literal imports before style rules, relative to the source stylesheet; "
"URL functions, escaping, layer ordering and remote paths abstain."},
{CBM_LANG_BICEP, EDGE(IMPORTS),
"Complete literal local module paths relative to the declaring file; interpolation, "
"registries and template specifications abstain."},
{CBM_LANG_CAPNP, EDGE(IMPORTS),
"Complete literal relative import paths; absolute search paths, escaped and concatenated "
"strings abstain."},
{CBM_LANG_JUST, EDGE(IMPORTS),
"Complete literal imports relative to the declaring file; home expansion, shell expansion, "
"escaping and computed paths abstain."},
{CBM_LANG_FUNC, EDGE(IMPORTS),
"Complete literal #include paths relative to the including source file; escaped strings, "
"computed paths and custom source-provider mappings abstain."},
{CBM_LANG_SCSS, EDGE(IMPORTS),
"Complete initial literal @use/@forward URLs with unique relative file/partial candidates; "
"@import, configured importers, load paths, index directories and effects abstain."},
{CBM_LANG_MESON, EDGE(IMPORTS),
"Complete literal subdir calls in an unconditional initial statement prefix; parent "
"traversal, keyword arguments, conditions and prior unknown effects abstain."},
{CBM_LANG_SCALA, EDGE(IMPORTS),
"Exact declared namespace members only; no semantic binding proof bridge advertised."},
{CBM_LANG_HCL, EDGE(IMPORTS) | EDGE(USAGE),
"Terraform/OpenTofu literal local module sources and Terragrunt literal paths with audited "
"parent-directory evaluation; remote, dynamic and ambiguous inputs abstain."},
{CBM_LANG_YAML, EDGE(IMPORTS) | EDGE(CALLS) | EDGE(USAGE),
"Ansible literal task/playbook paths and repository role paths only; dynamic directives "
"and unsupported YAML values abstain."},
{CBM_LANG_K8S, EDGE(USAGE), NULL},
{CBM_LANG_KUSTOMIZE, EDGE(IMPORTS),
"Literal relative resource paths only; transformed or generated resources are unsupported."},
};
#undef TYPED_EDGES
#undef SOURCE_EDGES
#undef C_EDGES
#undef CALLABLE_EDGES
#undef EDGE
static void edge_capabilities(cbm_resolve_capabilities_t *out) {
static const char *const names[CBM_RESOLVE_EDGE_COUNT] = {
"IMPORTS", "CALLS", "CALL_REFERENCE", "USAGE", "READS", "WRITES", "USES_TYPE"};
static const char *const unavailable[CBM_RESOLVE_EDGE_COUNT] = {
"No audited dependency-path subset is advertised; missing imports are a resolver quality "
"gap.",
"No admitted invocation subset is advertised; missing calls are a resolver quality gap.",
"No admitted callable-reference subset is advertised; syntax alone cannot prove a "
"callable.",
"No admitted declaration-usage subset is advertised; matching spelling cannot prove "
"identity.",
"No admitted read-target subset is advertised; read syntax cannot prove declaration "
"identity.",
"No admitted write-target subset is advertised; assignment syntax cannot prove declaration "
"identity.",
"No admitted type-reference subset is advertised; type spelling cannot prove declaration "
"identity."};
for (int i = 0; i < CBM_RESOLVE_EDGE_COUNT; i++)
out->edges[i] =
(cbm_resolve_edge_capability_t){.relation = names[i],
.availability = CBM_RESOLVE_EDGE_UNSUPPORTED,
.limitation = unavailable[i]};
for (size_t row = 0; row < sizeof(edge_rows) / sizeof(edge_rows[0]); row++) {
const edge_row_t *entry = &edge_rows[row];
if (entry->language != out->language)
continue;
for (int i = 0; i < CBM_RESOLVE_EDGE_COUNT; i++) {
if (!(entry->supported & (1u << i)))
continue;
out->edges[i].availability = CBM_RESOLVE_EDGE_SUPPORTED_SUBSET;
out->edges[i].limitation =
i == CBM_RESOLVE_EDGE_IMPORTS ? entry->imports : out->limitation;
}
break;
}
if (out->language == CBM_LANG_C || out->language == CBM_LANG_CPP ||
out->language == CBM_LANG_CUDA) {
const char *fields =
"Exact field identifier with a proved owner; unknown receiver, ambiguous "
"inheritance and unmodelled effects abstain.";
out->edges[CBM_RESOLVE_EDGE_READS].limitation = fields;
out->edges[CBM_RESOLVE_EDGE_WRITES].limitation = fields;
}
if (out->language == CBM_LANG_HCL)
out->edges[CBM_RESOLVE_EDGE_USAGE].limitation =
"Terraform/OpenTofu exact declarations within a module directory and declared outputs "
"of a literal local module source; duplicate declarations and unsupported inputs "
"abstain.";
if (out->language == CBM_LANG_KOTLIN)
out->edges[CBM_RESOLVE_EDGE_USAGE].limitation =
"Exact source-bound value references, including composite callable-reference "
"arguments; unproved receiver identity and dynamic values abstain.";
if (out->language == CBM_LANG_YAML) {
out->edges[CBM_RESOLVE_EDGE_CALLS].limitation =
"Ansible exact handlers in the current play or default role handlers/main.yml or "
"handlers/main.yaml, and literal listener-topic membership; duplicate named handlers "
"and dynamic names abstain.";
out->edges[CBM_RESOLVE_EDGE_USAGE].limitation =
"Kubernetes raw literal image repositories and core Secret, ConfigMap, ServiceAccount "
"and PersistentVolumeClaim references in explicit flat Kustomize resource lists; exact "
"namespaces and unique raw declarations are required. Aliases, duplicate keys, nested "
"builds and transformed manifests abstain. Compose image repository references "
"require exact source spans and a captured environment input; unsupported images "
"abstain.";
}
if (out->language == CBM_LANG_K8S)
out->edges[CBM_RESOLVE_EDGE_USAGE].limitation =
"Kubernetes raw literal image repositories and core Secret, ConfigMap, ServiceAccount "
"and PersistentVolumeClaim references in explicit flat Kustomize resource lists; "
"unique declarations and exact namespaces are required. Aliases, duplicate keys, "
"nested builds and transformed manifests abstain.";
if (out->language == CBM_LANG_GO)
out->edges[CBM_RESOLVE_EDGE_WRITES].limitation =
"No admitted Go field/write bridge; assignment extraction remains a resolver quality "
"gap.";
}
bool cbm_resolve_capabilities(CBMLanguage language, cbm_resolve_capabilities_t *out) {
if (!out || language < 0 || language >= CBM_LANG_COUNT)
return false;
*out = (cbm_resolve_capabilities_t){
.language = language,
.adapter = "dependency_only",
.limitation = "No semantic binding proof adapter; semantic occurrences abstain."};
const CBMLangSpec *spec = cbm_lang_spec(language);
if (spec) {
out->syntax_declarations =
syntax(spec->function_node_types) || syntax(spec->class_node_types);
out->syntax_calls = syntax(spec->call_node_types);
out->syntax_imports = syntax(spec->import_node_types) || syntax(spec->import_from_types) ||
spec->embedded_imports;
out->syntax_writes = syntax(spec->assignment_node_types);
}
switch (language) {
case CBM_LANG_HCL:
out->adapter = "hcl_infrastructure";
out->proof_subsets = CBM_RESOLVE_CAP_SCOPE | CBM_RESOLVE_CAP_IMPORT_EXPORT;
out->limitation = "Audited Terraform and Terragrunt occurrences only; dynamic evaluation "
"and unsupported or ambiguous declarations abstain.";
break;
case CBM_LANG_YAML:
out->adapter = "yaml_infrastructure";
out->proof_subsets = CBM_RESOLVE_CAP_SCOPE | CBM_RESOLVE_CAP_IMPORT_EXPORT;
out->limitation = "Audited infrastructure occurrences only; dynamic values and "
"unsupported source mappings abstain.";
break;
case CBM_LANG_K8S:
out->adapter = "kubernetes_infrastructure";
out->proof_subsets = CBM_RESOLVE_CAP_SCOPE;
out->limitation = "Audited Kubernetes image and object references only; transformed "
"manifests, dynamic values and ambiguous resource names abstain.";
break;
case CBM_LANG_VUE:
case CBM_LANG_SVELTE:
out->adapter = "embedded_javascript_lexical";
out->proof_subsets = CBM_RESOLVE_CAP_SCOPE | CBM_RESOLVE_CAP_CALLABLE;
out->limitation = language == CBM_LANG_VUE
? "One inline script with audited lexical bindings and an inert "
"template; multiple scripts, template effects and external script "
"sources abstain."
: "One inline script with audited lexical bindings; static markup "
"and bare-identifier event references only. Multiple script "
"contexts and template effects abstain.";
break;
case CBM_LANG_GO:
out->adapter = "go";
out->proof_subsets = CBM_RESOLVE_CAP_SCOPE | CBM_RESOLVE_CAP_IMPORT_EXPORT |
CBM_RESOLVE_CAP_DECLARED_TYPE | CBM_RESOLVE_CAP_MEMBER |
CBM_RESOLVE_CAP_TYPE_FLOW | CBM_RESOLVE_CAP_CALLABLE;
out->limitation = "Exact declarations and typed receivers; unknown owners, effects and "
"competing targets abstain.";
break;
case CBM_LANG_C:
case CBM_LANG_CPP:
case CBM_LANG_CUDA:
out->adapter = "c_family";
out->proof_subsets = CBM_RESOLVE_CAP_SCOPE | CBM_RESOLVE_CAP_IMPORT_EXPORT |
CBM_RESOLVE_CAP_DECLARED_TYPE | CBM_RESOLVE_CAP_MEMBER |
CBM_RESOLVE_CAP_TYPE_FLOW | CBM_RESOLVE_CAP_CALLABLE;
out->limitation = "Exact visibility and owner proofs; unsupported overload, template, ADL "
"and callable-field selection abstain.";
break;
case CBM_LANG_JAVASCRIPT:
case CBM_LANG_TYPESCRIPT:
case CBM_LANG_TSX:
out->adapter = "typescript";
out->proof_subsets = CBM_RESOLVE_CAP_SCOPE | CBM_RESOLVE_CAP_IMPORT_EXPORT |
CBM_RESOLVE_CAP_DECLARED_TYPE | CBM_RESOLVE_CAP_MEMBER |
CBM_RESOLVE_CAP_TYPE_FLOW | CBM_RESOLVE_CAP_CALLABLE;
out->limitation = "Source-audited lexical scopes, annotations, constructions and declared "
"fields; unproved inherited or dynamic lookup and classic browser "
"scripts abstain.";
break;
case CBM_LANG_PYTHON:
out->adapter = "python";
out->proof_subsets = CBM_RESOLVE_CAP_SCOPE | CBM_RESOLVE_CAP_IMPORT_EXPORT |
CBM_RESOLVE_CAP_DECLARED_TYPE | CBM_RESOLVE_CAP_MEMBER |
CBM_RESOLVE_CAP_TYPE_FLOW | CBM_RESOLVE_CAP_CALLABLE;
out->limitation =
"Explicit imports and bounded reexports; ordinary declared receivers and "
"aliases; dynamic/inherited lookup and decorated callable identity abstain.";
break;
case CBM_LANG_JAVA:
case CBM_LANG_KOTLIN:
case CBM_LANG_CSHARP:
case CBM_LANG_PHP:
out->adapter = language == CBM_LANG_JAVA ? "java"
: language == CBM_LANG_KOTLIN ? "kotlin"
: language == CBM_LANG_CSHARP ? "csharp"
: "php";
out->proof_subsets = CBM_RESOLVE_CAP_SCOPE | CBM_RESOLVE_CAP_IMPORT_EXPORT |
CBM_RESOLVE_CAP_DECLARED_TYPE | CBM_RESOLVE_CAP_MEMBER |
CBM_RESOLVE_CAP_TYPE_FLOW;
out->limitation = "Source-audited namespaces and direct declared members; unsupported "
"inheritance, extensions, overloads and effects abstain.";
break;
case CBM_LANG_RUST:
out->adapter = "rust";
out->proof_subsets = CBM_RESOLVE_CAP_SCOPE | CBM_RESOLVE_CAP_IMPORT_EXPORT |
CBM_RESOLVE_CAP_DECLARED_TYPE | CBM_RESOLVE_CAP_MEMBER |
CBM_RESOLVE_CAP_TYPE_FLOW;
out->limitation =
"Declared modules and exact free functions; direct inherent &self methods on &T; "
"macros, attributes, traits and richer receiver search abstain.";
break;
case CBM_LANG_DART:
out->adapter = "dart_source";
out->proof_subsets = CBM_RESOLVE_CAP_SCOPE | CBM_RESOLVE_CAP_IMPORT_EXPORT;
out->limitation = "Direct public source bindings with URI, prefix and show/hide rules; "
"parts, reexports and type propagation unimplemented.";
break;
case CBM_LANG_LUA:
out->adapter = "lua_source";
out->proof_subsets = CBM_RESOLVE_CAP_SCOPE;
out->limitation = "Direct local function bindings; runtime require/export and receiver "
"type proofs unimplemented.";
break;
case CBM_LANG_ARKTS:
out->adapter = "javascript_source";
out->proof_subsets = CBM_RESOLVE_CAP_SCOPE | CBM_RESOLVE_CAP_IMPORT_EXPORT;
out->limitation = "Supported source bindings only; no typed cross-file dispatch for the "
"containing language.";
break;
case CBM_LANG_SWIFT:
out->limitation = "Swift build-module membership and binding proofs unimplemented.";
break;
case CBM_LANG_PERL:
out->limitation =
"Single-file legacy engine has no admitted cross-file semantic proof bridge.";
break;
default:
break;
}
edge_capabilities(out);
return true;
}
bool cbm_resolve_edge_capability(CBMLanguage language, const char *relation,
cbm_resolve_edge_capability_t *out) {
cbm_resolve_capabilities_t capabilities;
if (!out || !relation || !cbm_resolve_capabilities(language, &capabilities))
return false;
for (int i = 0; i < CBM_RESOLVE_EDGE_COUNT; i++) {
if (strcmp(relation, capabilities.edges[i].relation) == 0) {
*out = capabilities.edges[i];
return true;
}
}
return false;
}