Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
e9f87ef
feat(lsp): add the Ruby per-file resolver
benrfairless Aug 18, 2026
11418b4
feat(pipeline): wire Ruby cross-file LSP resolution
benrfairless Aug 18, 2026
03e4c6b
Merge branch 'main' into feat/ruby-lsp-per-file
benrfairless Aug 18, 2026
d0464f5
Merge branch 'main' into feat/ruby-lsp-cross-file
benrfairless Aug 18, 2026
f397d9e
Merge branch 'main' into feat/ruby-lsp-per-file
benrfairless Sep 30, 2026
9f20492
fix(ruby-lsp): build compact class QNs the way the extractor does
benrfairless Sep 30, 2026
c2d8644
fix(ruby-lsp): make def a scope gate and fail closed on untyped reass…
benrfairless Sep 30, 2026
ffeb06f
fix(ruby-lsp): latch ivar type on any non-constructor write
benrfairless Sep 30, 2026
50f28fb
fix(ruby-lsp): suppress self dispatch inside blocks that rebind self
benrfairless Sep 30, 2026
60801bc
fix(ruby-lsp): fail closed on unresolved ancestry
benrfairless Sep 30, 2026
8bc99a4
fix(ruby-lsp): do not register block-nested defs as class members
benrfairless Sep 30, 2026
0f8b719
test(ruby-lsp): exact-name assertions for nested-class rows
benrfairless Sep 30, 2026
70760c3
Merge branch 'main' into feat/ruby-lsp-cross-file
benrfairless Sep 30, 2026
23f1309
Merge branch 'main' into feat/ruby-lsp-per-file
benrfairless Oct 2, 2026
0c26a3c
Merge branch 'main' into feat/ruby-lsp-per-file
benrfairless Oct 20, 2026
de29b97
Merge branch 'feat/ruby-lsp-per-file' into feat/ruby-lsp-cross-file
benrfairless Oct 20, 2026
82bf4cb
refactor(ruby-lsp): look classes up through a shared project table
benrfairless Oct 20, 2026
3dfd9cd
perf(ruby-lsp): index the project once instead of per file
benrfairless Oct 20, 2026
cf1d858
test(ruby-lsp): gate cross-file cost and cover the shared registry
benrfairless Oct 20, 2026
daa920f
style(ruby-lsp): clang-format the shared registry code
benrfairless Oct 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Makefile.cbm
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,7 @@ TEST_CS_LSP_SRCS = tests/test_cs_lsp.c
TEST_CS_LSP_BENCH_SRCS = tests/test_cs_lsp_bench.c

TEST_PERL_LSP_SRCS = tests/test_perl_lsp.c
TEST_RUBY_LSP_SRCS = tests/test_ruby_lsp.c

TEST_SCOPE_SRCS = tests/test_scope.c

Expand Down Expand Up @@ -955,7 +956,7 @@ TEST_REPRO_SRCS = \
tests/repro/repro_lsp_java_cs.c \
tests/repro/repro_lsp_kt_php_rust.c

ALL_TEST_SRCS =$(TEST_FOUNDATION_SRCS) $(TEST_EXTRACTION_SRCS) $(TEST_STORE_SRCS) $(TEST_CYPHER_SRCS) $(TEST_MCP_SRCS) $(TEST_DAEMON_SRCS) $(TEST_DISCOVER_SRCS) $(TEST_GRAPH_BUFFER_SRCS) $(TEST_PIPELINE_SRCS) $(TEST_WATCHER_SRCS) $(TEST_LZ4_SRCS) $(TEST_ZSTD_SRCS) $(TEST_ARTIFACT_SRCS) $(TEST_SQLITE_WRITER_SRCS) $(TEST_GO_LSP_SRCS) $(TEST_C_LSP_SRCS) $(TEST_PHP_LSP_SRCS) $(TEST_CS_LSP_SRCS) $(TEST_CS_LSP_BENCH_SRCS) $(TEST_PERL_LSP_SRCS) $(TEST_SCOPE_SRCS) $(TEST_TYPE_REP_SRCS) $(TEST_PY_LSP_SRCS) $(TEST_PY_LSP_BENCH_SRCS) $(TEST_PY_LSP_STRESS_SRCS) $(TEST_PY_LSP_SCALE_SRCS) $(TEST_TS_LSP_SRCS) $(TEST_JAVA_LSP_SRCS) $(TEST_KOTLIN_LSP_SRCS) $(TEST_RUST_LSP_SRCS) $(TEST_TRACES_SRCS) $(TEST_CLI_SRCS) $(TEST_MEM_SRCS) $(TEST_UI_SRCS) $(TEST_HTTPD_SRCS) $(TEST_SECURITY_SRCS) $(TEST_YAML_SRCS) $(TEST_SEMANTIC_SRCS) $(TEST_AST_PROFILE_SRCS) $(TEST_SLAB_ALLOC_SRCS) $(TEST_SIMHASH_SRCS) $(TEST_STACK_OVERFLOW_SRCS) $(TEST_INTEGRATION_SRCS)
ALL_TEST_SRCS =$(TEST_FOUNDATION_SRCS) $(TEST_EXTRACTION_SRCS) $(TEST_STORE_SRCS) $(TEST_CYPHER_SRCS) $(TEST_MCP_SRCS) $(TEST_DAEMON_SRCS) $(TEST_DISCOVER_SRCS) $(TEST_GRAPH_BUFFER_SRCS) $(TEST_PIPELINE_SRCS) $(TEST_WATCHER_SRCS) $(TEST_LZ4_SRCS) $(TEST_ZSTD_SRCS) $(TEST_ARTIFACT_SRCS) $(TEST_SQLITE_WRITER_SRCS) $(TEST_GO_LSP_SRCS) $(TEST_C_LSP_SRCS) $(TEST_PHP_LSP_SRCS) $(TEST_CS_LSP_SRCS) $(TEST_CS_LSP_BENCH_SRCS) $(TEST_PERL_LSP_SRCS) $(TEST_RUBY_LSP_SRCS) $(TEST_SCOPE_SRCS) $(TEST_TYPE_REP_SRCS) $(TEST_PY_LSP_SRCS) $(TEST_PY_LSP_BENCH_SRCS) $(TEST_PY_LSP_STRESS_SRCS) $(TEST_PY_LSP_SCALE_SRCS) $(TEST_TS_LSP_SRCS) $(TEST_JAVA_LSP_SRCS) $(TEST_KOTLIN_LSP_SRCS) $(TEST_RUST_LSP_SRCS) $(TEST_TRACES_SRCS) $(TEST_CLI_SRCS) $(TEST_MEM_SRCS) $(TEST_UI_SRCS) $(TEST_HTTPD_SRCS) $(TEST_SECURITY_SRCS) $(TEST_YAML_SRCS) $(TEST_SEMANTIC_SRCS) $(TEST_AST_PROFILE_SRCS) $(TEST_SLAB_ALLOC_SRCS) $(TEST_SIMHASH_SRCS) $(TEST_STACK_OVERFLOW_SRCS) $(TEST_INTEGRATION_SRCS)


# ── Build directories ────────────────────────────────────────────
Expand Down
4 changes: 4 additions & 0 deletions internal/cbm/cbm.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "lsp/php_lsp.h"
#include "lsp/perl_lsp.h"
#include "lsp/py_lsp.h"
#include "lsp/ruby_lsp.h"
#include "lsp/ts_lsp.h"
#include "lsp/cs_lsp.h"
#include "lsp/java_lsp.h"
Expand Down Expand Up @@ -3943,6 +3944,9 @@ static CBMFileResult *extract_file_ex_body(const char *source, int source_len, C
if (language == CBM_LANG_PERL) {
cbm_run_perl_lsp(a, result, source, source_len, root);
}
if (language == CBM_LANG_RUBY) {
cbm_run_ruby_lsp(a, result, source, source_len, root);
}
if (language == CBM_LANG_PYTHON) {
cbm_run_py_lsp(a, result, source, source_len, root);
}
Expand Down
461 changes: 461 additions & 0 deletions internal/cbm/lsp/generated/ruby_stdlib_data.c

Large diffs are not rendered by default.

2,020 changes: 2,020 additions & 0 deletions internal/cbm/lsp/ruby_lsp.c

Large diffs are not rendered by default.

229 changes: 229 additions & 0 deletions internal/cbm/lsp/ruby_lsp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
#ifndef CBM_LSP_RUBY_LSP_H
#define CBM_LSP_RUBY_LSP_H

#include "type_rep.h"
#include "scope.h"
#include "type_registry.h"
#include "../cbm.h"
#include "go_lsp.h" /* CBMLSPDef reused across languages */

/* Ruby mixin kinds. Lookup order for an instance method on class C:
* prepended modules (last prepended first) → C's own methods →
* included modules (last included first) → superclass chain.
* `extend` adds a module's instance methods to C's singleton (class-side). */
typedef enum {
RUBY_MIXIN_INCLUDE = 0,
RUBY_MIXIN_PREPEND,
RUBY_MIXIN_EXTEND,
} RubyMixinKind;

/* One class/module discovered in the file (or, cross-file, in the project).
* `path` is the dotted constant path used ONLY for constant lookup ("Admin.User"
* for both `module Admin; class User` and `class Admin::User`). `qn` is the
* graph QN built exactly the way the structural extractor builds it: each
* declaration's raw name text appended to the enclosing class QN (or the
* module QN at top level), so a compact `class Admin::User` yields
* "<module_qn>.Admin::User" while the lexically nested form yields
* "<module_qn>.Admin.User". Emitted rows must carry the extractor spelling
* or the pipeline's exact-name join silently drops them. `superclass_qn` is
* resolved against the class table + stdlib after collection; NULL when
* unknown. */
typedef struct {
const char *path;
const char *qn;
const char *superclass_ref; /* raw dotted superclass spelling, or NULL */
const char *superclass_qn; /* resolved QN (table/stdlib), or NULL */
bool is_module; /* `module` (mixin source) vs `class` */
/* Shared (project-wide) table only. A class reopened in another file has
* a different QN there (the QN carries the file's module QN) but the same
* constant path; the entries for one path form a group so a method defined
* in any reopening is found. `group_head` is the first entry of the group,
* `shard_next` the next one; both are table index + 1, 0 = none. Entries
* of a per-file table leave both 0. */
int group_head;
int shard_next;
} RubyClassInfo;

struct CBMRubyCrossRegistry;

/* One `include`/`prepend`/`extend` record: owner class QN → module ref. */
typedef struct {
const char *owner_qn; /* class/module whose body held the call */
const char *module_ref; /* raw dotted module spelling */
const char *module_qn; /* resolved QN, or NULL */
RubyMixinKind kind;
} RubyMixinInfo;

/* One inferred instance-variable type: (class QN, @name) → type QN.
* `conflicted` latches when two assignments disagree — lookup then returns
* nothing (zero-edge guarantee beats a coin-flip type). */
typedef struct {
const char *class_qn;
const char *ivar_name; /* including the leading '@' */
const char *type_qn;
bool conflicted;
} RubyIvarInfo;

/* RubyLSPContext — per-file state for Ruby type-aware call resolution.
* Mirrors PerlLSPContext / PHPLSPContext structure.
*
* Ruby specifics that shape this context:
* - Classes/modules nest lexically (`module A; class B`), and constants
* resolve through the lexical nesting outward (A::B sees A's constants
* before top-level ones). `nesting` tracks the current dotted path.
* - Inheritance is `class C < Base`; mixins arrive via include/prepend/
* extend and participate in method lookup (see RubyMixinKind).
* - `Foo.new` constructs a Foo and dispatches to Foo#initialize.
* - `def m` defines an instance method; `def self.m` a singleton (class)
* method. Singleton methods are registered under the receiver key
* "<class_qn>.self" — constants cannot be lowercase, so the suffix can
* never collide with a real nested-constant path. */
typedef struct {
CBMArena *arena;
const char *source;
int source_len;
const CBMTypeRegistry *registry;
/* Mutable alias of `registry`, set only while PASS 1 populates method
* tables (mirrors perl_lsp's mutable-reg build phase). NULL afterwards. */
CBMTypeRegistry *build_reg;
CBMScope *current_scope;

/* Lexical nesting: one frame per enclosing class/module declaration,
* frames joined by '.', each frame keeping its raw source spelling ("" at
* top level, "M.A::B" inside `module M; class A::B`). Ruby's constant
* lookup walks the frames outward — inside `class A::B` the nesting is
* [A::B] only, so A's constants are NOT probed. */
const char *nesting;

/* Project-wide read-only class table + method registry (cross-file
* resolution). NULL for per-file extraction. Consulted after this file's
* own table, so a class declared here always wins. */
const struct CBMRubyCrossRegistry *shared;

/* Class/module table (pass 1) — this file's own declarations only. */
RubyClassInfo *classes;
int class_count;
int class_cap;

/* Mixin table (pass 1). */
RubyMixinInfo *mixins;
int mixin_count;
int mixin_cap;

/* Instance-variable type table (pass 1). */
RubyIvarInfo *ivars;
int ivar_count;
int ivar_cap;

/* Cross-file import map (require/require_relative resolved by the
* pipeline). Unused per-file: Ruby constants are file-global. */
const char **import_names;
const char **import_qns;
int import_count;

/* Current enclosing context during the resolution walk. */
const char *enclosing_class_qn; /* class QN, or NULL at top level */
const char *enclosing_func_qn; /* enclosing method QN, or NULL */
bool in_singleton_method; /* `def self.m` / class << self scope */
/* Inside a block that rebinds `self` (instance_eval/exec, class_eval/
* exec, module_eval/exec, define_method, Class.new/Module.new): the
* receiver of bare and `self.` calls is NOT the enclosing class, and
* the resolver cannot know what it is — so they emit nothing. */
bool self_unknown;
/* Number of enclosing blocks (`{ }`, `do end`, `-> { }`) since the
* nearest class/module body. A `def` met here is NOT a method of the
* enclosing class — ActiveSupport's `class_methods do … end`,
* `included do … end`, RSpec groups — so PASS 1 registers nothing for
* it and collects no mixins from it. */
int block_depth;
const char *module_qn;

/* Output: resolved calls accumulate here. */
CBMResolvedCallArray *resolved_calls;

/* Recursion guards. */
int eval_depth;
int walk_depth;

/* Debug mode (CBM_LSP_DEBUG env). */
bool debug;
} RubyLSPContext;

/* Initialize a RubyLSPContext for processing one file. */
void ruby_lsp_init(RubyLSPContext *ctx, CBMArena *arena, const char *source, int source_len,
const CBMTypeRegistry *registry, const char *module_qn,
CBMResolvedCallArray *out);

/* Resolve a dotted constant path (e.g. "Foo", "A.B") against the class
* table using the current lexical nesting, then the stdlib. Returns the
* resolved QN or NULL. */
const char *ruby_resolve_constant(RubyLSPContext *ctx, const char *path);

/* Look up an instance method on a class, walking prepends → own → includes →
* superclass chain. Returns the resolved CBMRegisteredFunc or NULL. */
const CBMRegisteredFunc *ruby_lookup_instance_method(RubyLSPContext *ctx, const char *class_qn,
const char *method_name);

/* Look up a singleton (class-side) method: own singleton methods →
* `extend`ed modules' instance methods → superclass singleton chain. */
const CBMRegisteredFunc *ruby_lookup_singleton_method(RubyLSPContext *ctx, const char *class_qn,
const char *method_name);

/* Evaluate a Ruby expression's type. May return CBM_TYPE_UNKNOWN. */
const CBMType *ruby_eval_expr_type(RubyLSPContext *ctx, TSNode node);

/* Entry point: build registry from file defs + stdlib, then run resolution.
* Called from cbm_extract_file() via the language dispatch in cbm.c. */
void cbm_run_ruby_lsp(CBMArena *arena, CBMFileResult *result, const char *source, int source_len,
TSNode root);

/* Register Ruby core stdlib types/methods + a curated Rails surface
* (ActiveRecord/ActiveSupport/ActionController) into a registry. */
void cbm_ruby_stdlib_register(CBMTypeRegistry *reg, CBMArena *arena);

/* --- Cross-file LSP resolution ---
*
* Ruby constants live in one global namespace and Rails/Zeitwerk autoloads
* them with no require, so a file may name any class in the project. The
* project's definitions are therefore indexed ONCE (cbm_ruby_build_cross_
* registry) into a sealed, read-only registry: a finalized method registry
* plus a hashed class table. Each file then resolves against it with O(1)
* lookups and builds nothing proportional to the project. */
typedef struct CBMRubyCrossRegistry {
CBMTypeRegistry reg; /* stdlib + every project method; finalized, sealed */
RubyClassInfo *classes;
int class_count;
/* Open-addressing hash indexes over `classes` (values are index + 1, 0 =
* empty slot): by QN, and by dotted constant path (group head only). */
int *by_qn;
int *by_path;
unsigned slot_mask;
} CBMRubyCrossRegistry;

/* Build the shared registry from every project def. Strings are borrowed from
* `defs` (as the other languages' cross registries do) or copied into `arena`.
* Returns NULL on allocation failure; callers then fall back to the
* per-file path. */
CBMRubyCrossRegistry *cbm_ruby_build_cross_registry(CBMArena *arena, CBMLSPDef *defs,
int def_count);

/* Resolve one file against a prebuilt shared registry. Reads `shared` only;
* everything it allocates lives in `arena`. */
void cbm_run_ruby_lsp_cross_with_registry(CBMArena *arena, const char *source, int source_len,
const char *module_qn, const CBMRubyCrossRegistry *shared,
const char **import_names, const char **import_qns,
int import_count,
TSTree *cached_tree, /* NULL = parse internally */
CBMResolvedCallArray *out);

/* Convenience for callers with a def list but no prebuilt registry (the
* per-file fallback path and tests): builds a registry in a scratch arena for
* this one call. Cost is proportional to the def list, so the pipeline uses
* the prebuilt path instead. */
void cbm_run_ruby_lsp_cross(CBMArena *arena, const char *source, int source_len,
const char *module_qn, CBMLSPDef *defs, int def_count,
const char **import_names, const char **import_qns, int import_count,
TSTree *cached_tree, /* NULL = parse internally */
CBMResolvedCallArray *out);

#endif /* CBM_LSP_RUBY_LSP_H */
2 changes: 2 additions & 0 deletions internal/cbm/lsp_all.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
#include "lsp/generated/php_stdlib_data.c"
#include "lsp/perl_lsp.c"
#include "lsp/generated/perl_stdlib_data.c"
#include "lsp/ruby_lsp.c"
#include "lsp/generated/ruby_stdlib_data.c"
#include "lsp/generated/python_stdlib_data.c"
#include "lsp/py_lsp.c"
#include "lsp/ts_lsp.c"
Expand Down
6 changes: 5 additions & 1 deletion scripts/check-lsp-originality.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
# USAGE
# bash scripts/check-lsp-originality.sh [--lang NAME] [--refresh]
# [--list-candidates] [--help]
# --lang NAME scan against ONE reference only (py|ts|go|cs|c|java|kotlin|rust|php|perl)
# --lang NAME scan against ONE reference only (py|ts|go|cs|c|java|kotlin|rust|php|perl|ruby)
# --refresh re-fetch reference sources even if cached
# --list-candidates print the extracted local tokens and exit (no fetch; self-test)
# Exit 0 = no verbatim overlap found. Exit 1 = overlap(s) to review by a human.
Expand Down Expand Up @@ -64,6 +64,10 @@ REFS=(
# Perl (PR #461) was authored clean-room. We scan against PerlNavigator (MIT)
# — the leading OSS Perl language server — for defensive copy-detection.
"perl|https://github.com/bscan/PerlNavigator|server/src"
# Ruby was authored clean-room from the tree-sitter-ruby grammar + Ruby
# language semantics. We scan against Shopify's ruby-lsp (MIT) — the
# leading OSS Ruby language server — for defensive copy-detection.
"ruby|https://github.com/Shopify/ruby-lsp|lib"
# Infrastructure hybrid LSPs (K8s YAML, Kustomize bundles, Flux/Argo deploy
# scopes, the Go operator extension) were written from the Kubernetes, Flux,
# Argo, Cluster API, cert-manager and Kubermatic API types. Scanned against
Expand Down
24 changes: 22 additions & 2 deletions src/pipeline/pass_lsp_cross.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "lsp/php_lsp.h"
#include "lsp/java_lsp.h"
#include "lsp/kotlin_lsp.h"
#include "lsp/ruby_lsp.h"
#include "lsp/rust_lsp.h"
#include "lsp/rust_cargo.h"
#include "graph_buffer/graph_buffer.h"
Expand Down Expand Up @@ -1652,6 +1653,7 @@ bool cbm_pxc_has_cross_lsp(CBMLanguage lang) {
case CBM_LANG_JAVA: /* fallback cbm_pxc_run_one path */
case CBM_LANG_KOTLIN: /* fallback cbm_pxc_run_one path */
case CBM_LANG_RUST: /* fallback cbm_pxc_run_one path (manifest-aware) */
case CBM_LANG_RUBY: /* fallback cbm_pxc_run_one path */
return true;
default:
return false;
Expand Down Expand Up @@ -2146,6 +2148,10 @@ void cbm_pxc_run_one_with_php_bindings(CBMLanguage lang, CBMFileResult *r, const
cbm_run_kotlin_lsp_cross(&scratch, source, source_len, module_qn, defs, def_count,
imp_names, imp_qns, imp_count, tree, &out);
break;
case CBM_LANG_RUBY:
cbm_run_ruby_lsp_cross(&scratch, source, source_len, module_qn, defs, def_count, imp_names,
imp_qns, imp_count, tree, &out);
break;
case CBM_LANG_RUST: {
/* The Rust resolver wants CBMRustLSPDef (rust_lsp.h), not the
* pipeline's CBMLSPDef — the structs share their first 9 fields
Expand Down Expand Up @@ -2333,6 +2339,15 @@ void cbm_pxc_dispatch_file_with_php_bindings(
imp_vals, imp_count, result->cached_tree, &out);
used_prebuilt = true;
break;
case CBM_LANG_RUBY:
/* The shared registry is sealed and read-only: methods and the
* hashed class table are looked up in it, nothing is built per
* file, so no per-file work scales with the project. */
cbm_run_ruby_lsp_cross_with_registry(&scratch, source, source_len, def_module,
cross_registries->ruby, imp_keys, imp_vals,
imp_count, result->cached_tree, &out);
used_prebuilt = true;
break;
case CBM_LANG_JAVA:
/* Java builds its own-module overlay on top of the one it is
* handed; both live in scratch now. */
Expand Down Expand Up @@ -2390,11 +2405,15 @@ void cbm_pxc_dispatch_file_with_php_bindings(
* crate — a module that is in neither own_module nor the import map, so
* the filter starves cross-crate resolution (#56 repro red). Rust
* therefore always resolves against the FULL def universe: the lazily
* built shared registry when available, else a full per-file build. */
* built shared registry when available, else a full per-file build.
* RUBY is exempt for the same reason with different mechanics: Ruby
* constants live in one global namespace and Rails/Zeitwerk autoloads
* them WITHOUT require statements, so an own+imports filter starves
* essentially all cross-file resolution in real Ruby apps. */
CBMLSPDef *filtered = NULL;
CBMLSPDef *file_defs = all_defs;
int file_def_count = all_def_count;
if (module_def_index && lang != CBM_LANG_RUST) {
if (module_def_index && lang != CBM_LANG_RUST && lang != CBM_LANG_RUBY) {
int filtered_count = 0;
bool filter_succeeded = false;
filtered = cbm_pxc_filter_defs_for_file(module_def_index, all_defs, lang,
Expand Down Expand Up @@ -2592,6 +2611,7 @@ int cbm_pipeline_pass_lsp_cross(cbm_pipeline_ctx_t *ctx, const cbm_file_info_t *
cross_registries.c = cbm_c_build_cross_registry(xa, all_defs, def_count);
cross_registries.cs = cbm_cs_build_cross_registry(xa, all_defs, def_count);
cross_registries.ts = cbm_ts_build_cross_registry(xa, all_defs, def_count);
cross_registries.ruby = cbm_ruby_build_cross_registry(xa, all_defs, def_count);
}

int processed = 0;
Expand Down
Loading
Loading