Files
Martin Vogel 42a9320200 graph: one identity per conditional definition, SPAWNS edges, no cross-language guesses
The test-impact engine selects tests by walking the graph, so it can only
be as right as the graph. Three gaps made it wrong:

One identity for a definition's mutually exclusive variants
- A definition written once per #if/#else branch, per #[cfg], per
  platform file (foo_linux.c / foo_win.c, platform directories) or per Go
  build tag is ONE node. The graph buffer keeps every variant's span in a
  `variants` node property (definition labels only), so a change in any
  variant maps to the definition.
- Extraction now sees definitions inside conditional blocks where it
  skipped them: C++/C#/ObjC class members under #if (one cursor walk,
  conditional block types per language in lang_specs), Elixir defs under
  if/unless, Fortran .F90/.F95/.F03/.F08, Rust #[cfg] twins (no more
  `name#cfg(...)` QNs that lost every call), C platform files and
  directories (non-static file-scope functions).
- Haskell: the vendored scanner reads only an #if group's first branch.
  The grammar stays upstream; every further branch is parsed as its own
  same-length view (cpp_branch_views.c) and adds what the raw parse
  missed. haskell/bytestring: no new partial parses, +27 functions,
  IMPORTS 322 -> 329, 131 definitions with variant spans.
- Incremental closure repair re-parses every file of such a definition
  when one of them changes (variant partners through DEFINES), so a
  repair equals a full index.

No cross-language name guesses
- A name-only resolution (unique_name, suffix_match, qualified_suffix,
  field_type_hint, and same_module across files that only share a path
  stem) never binds a symbol another language defines.
- Bash imports are the files a script sources: every top-level command
  was read as an import of its name (`set -e` imported "set", bound to a
  TSX method in 137 scripts here).
- This repository: cross-language CALLS 5,916 -> 5,091; the remainder are
  C/C++ pairs, builtins and same-stem module collisions (recorded).

SPAWNS edges (per language, like the HTTP client patterns)
- A call that starts a program (subprocess.run, exec.Command,
  child_process.spawn, posix_spawn, system, Process.Start, Command::new,
  ... about 30 languages, through the file's own imports) gets a SPAWNS
  edge to a Process node named after the program (literal first word, or
  <dynamic>) instead of a CALLS guess. No target resolution. Both
  resolvers ask at the same point (parity test).

The engine follows the graph
- Seeds map changed lines through every variant span; a test written
  once per #if branch is one test; when a change reaches a non-test
  `main`, the tests that spawn processes are reached too (the process
  runs main, and main reaches everything).

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-06 02:44:56 +02:00

66 lines
3.1 KiB
C

#ifndef CBM_LANG_SPECS_H
#define CBM_LANG_SPECS_H
#include "cbm.h"
// CBMEmbeddedLangSpec describes a sub-language embedded inside a host AST.
// Used by host grammars whose tree-sitter parser does not recurse into the
// embedded content (e.g. Svelte, Vue, Astro: script_element -> raw_text holds
// JavaScript verbatim that the host grammar leaves unparsed). The generic
// embedded-imports walker locates each script_node_type in the host AST,
// finds its content_node_type child, and re-parses that slice with the
// embedded_language's grammar so existing extractors run on the inner AST.
typedef struct {
const char *script_node_type; // e.g. "script_element"
const char *content_node_type; // e.g. "raw_text"
CBMLanguage embedded_language; // grammar used to re-parse the content slice
} CBMEmbeddedLangSpec;
// CBMLangSpec mirrors Go's lang.LanguageSpec with NULL-terminated string arrays.
typedef struct {
CBMLanguage language;
const char **function_node_types;
const char **class_node_types;
const char **field_node_types;
const char **module_node_types;
const char **call_node_types;
const char **import_node_types;
const char **import_from_types;
const char **branching_node_types;
const char **variable_node_types;
const char **assignment_node_types;
const char **throw_node_types;
const char *throws_clause_field; // NULL if none
const char **decorator_node_types;
const char **env_access_functions; // NULL-terminated (NULL if none)
const char **env_access_member_patterns; // NULL-terminated (NULL if none)
const TSLanguage *(*ts_factory)(void); // Tree-sitter grammar factory (NULL if shared)
// NULL-terminated list of embedded sub-languages (NULL if host grammar has
// no embedded content to re-parse). The terminator is an entry whose
// script_node_type is NULL.
const CBMEmbeddedLangSpec *embedded_imports;
} CBMLangSpec;
// Returns a NULL-terminated list of callee-name suffixes that indicate a
// string-dispatch call for a given language (e.g. ".classMethodValue" for
// Python/IRIS), or NULL if the language has no such dispatch pattern.
// Kept out of CBMLangSpec to avoid -Wmissing-field-initializers across 155
// language rows; the table lives in extract_calls.c next to the dispatch code.
const char **cbm_string_dispatch_suffixes(CBMLanguage lang);
// Returns a NULL-terminated list of node types that wrap conditionally
// compiled alternatives (#if/#ifdef/#else/#elif blocks) for a language, or
// NULL when the grammar has none. A definition inside one is still a member
// of its enclosing scope; its #else twin is a variant of the same definition.
// Side table for the same -Wmissing-field-initializers reason as above.
const char **cbm_conditional_block_types(CBMLanguage lang);
// Get the language spec for a given language. Returns NULL for unsupported.
const CBMLangSpec *cbm_lang_spec(CBMLanguage lang);
// Get the TSLanguage* for a given language. Returns NULL for unsupported.
// These resolve at link time to grammar symbols from Go tree-sitter modules.
const TSLanguage *cbm_ts_language(CBMLanguage lang);
#endif // CBM_LANG_SPECS_H