Commit Graph
4256 Commits
Author SHA1 Message Date
Martin Vogel bec015461c fix(go-lsp): split the raw Go result list, keep a single result raw
The first D1 change fed the Go cross-file registrar the extractor's
return_types. Those are cleaned names for type-reference edges:
clean_type_name strips `[]` and `*`. A cross-file single result `[]T`
therefore reached the registrar as `T`. `xs := pkg.Generate(); xs[0].M()`
lost its typing and fell back to a name guess. On prometheus the base
binds `sample[0].T()` to chunks.Sample.T, but the first D1 change bound
it to tsdb.T.

pass_lsp_cross.c now leaves a single result's raw text alone, as before
D1; the registrar already parses `*T` and `[]T`. Only a result LIST
`(...)` is split, at its top-level commas, into raw element types:
- `(*T, error)` -> `*T|error`;
- the named `(t *T, err error)` -> `*T|error`;
- the grouped `(a, b int)` -> `int|int`.
`chan`, `func`, `map`, `struct` and `interface` never count as result
names. A list that does not split cleanly keeps its raw text.

Tests: the D1 fixture gains three call sites, each with two same-named
decoys:
- a single slice result indexed (`xs[0].TS()`);
- a tuple with a slice element;
- grouped named results.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 03:29:05 +02:00
Martin Vogel 9129c48f48 test(pipeline): the surface envelope probe expects codec version 3
The Python surface-envelope test (pipeline round 0) checks that the
persisted row is the versioned codec envelope by looking for the
literal "v":2. With SURFACE_CODEC_VERSION at 3, the envelope reads
"v":3. The assertion keeps its meaning (the row is the versioned
envelope and the def is present) and follows the version.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 02:55:24 +02:00
Martin Vogel 36bce46e68 fix(go-lsp): cross-file multi-value and named results type their variables
The cross-file pass handed every registrar the raw result text of a
def (pass_lsp_cross.c: `dst->return_types = src->return_type`). The Go
registrar splits that string on '|' (go_lsp.c split_pipe_types), so it
got `(*Encoder, error)` as one piece. That piece did not parse, and a
cross-file callee with more than one result, or with a named result
`(e *Encoder)`, had no usable return type. As a result,
`e, err := enc.NewEncoder()`, `h, _ := newHead()` and
`_, d := diag.Run()` all left the variable untyped, and calls on it fell
to name guesses. Same-file calls were fine, because they read the AST.

Go defs now pass the parsed result list (extract_go_multi_return),
'|'-joined: `*Encoder|error`, and `*Encoder` for the named result. Other
languages keep the single raw string.

The persisted LSP surface carries this string, so SURFACE_CODEC_VERSION
goes from 2 to 3: existing caches re-extract Go surfaces once. The
literal version-2 surfaces in the codec tests move to 3, so that they
keep testing the envelope rule they name rather than the version
mismatch.

Tests: pipeline_go_multi_return_receiver_is_typed (+_parallel). A tuple
result, a named result, a tuple-bound local reached through a field,
and a named slice type each must bind the right method with an lsp_*
strategy and none of its two same-named decoys.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-19 02:54:41 +02:00
Martin Vogel 658639ec56 Merge pull request #2685 from DeusData/fix/rust-generic-impl-call-scope
fix(rust): generic impl methods source their own calls (#763 Rust share)
2026-10-19 01:42:18 +02:00
Martin Vogel ea5c1f7250 Merge pull request #2689 from DeusData/fix/data-flows-every-caller
fix(pipeline): DATA_FLOWS link every caller and handler of a route
2026-10-19 01:20:34 +02:00
Martin Vogel 1a7839db2e Merge pull request #2687 from DeusData/fix/def-props-buffer-every-field
fix(pipeline): size definition properties for every field
2026-10-18 23:56:35 +02:00
Martin Vogel 166167384d Merge remote-tracking branch 'origin/main' into fix/rust-generic-impl-call-scope
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>

# Conflicts:
#	tests/test_pipeline.c
2026-10-18 21:42:15 +02:00
Martin Vogel 1346ac4ea2 Merge pull request #2686 from DeusData/fix/issue-1548-python-nested-defs-v2
fix(python): extract defs nested in a function body and resolve bare names lexically (#1548)
2026-10-18 21:40:25 +02:00
Martin Vogel 08ee95a82b Merge pull request #2681 from DeusData/fix/rust-registry-rust-defs-only
fix(rust-lsp): Rust calls never bind definitions of other languages
2026-10-18 21:39:55 +02:00
Martin Vogel bddacfed36 Merge pull request #2682 from DeusData/fix/issue-1555-kotlin-property-receivers
fix(kotlin): resolve calls through typed class properties (#1555)
2026-10-18 21:12:38 +02:00
Martin Vogel 9cc3b762f9 Merge remote-tracking branch 'origin/main' into fix/def-props-buffer-every-field
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-18 21:09:22 +02:00
Martin Vogel 5f66c867ed Merge pull request #2679 from DeusData/fix/py-dotted-base-qn
fix(python): resolve a base class spelled through a root-binding import
2026-10-18 21:01:14 +02:00
Martin Vogel b87e0c0bed Merge remote-tracking branch 'origin/main' into fix/def-props-buffer-every-field
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-18 19:06:37 +02:00
Martin Vogel ef90708e70 Merge origin/main into fix/issue-1548-python-nested-defs-v2 (pick up the Smart CI selection fix #2684)
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-18 19:02:47 +02:00
Martin Vogel 9dc0ab6b3e Merge origin/main into fix/rust-generic-impl-call-scope (pick up the Smart CI selection fix #2684)
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-18 19:02:42 +02:00
Martin Vogel 3db701f5ef Merge origin/main into fix/issue-1555-kotlin-property-receivers (pick up the Smart CI selection fix #2684)
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-18 19:02:36 +02:00
Martin Vogel 5ae2a34614 Merge origin/main into fix/rust-registry-rust-defs-only (pick up the Smart CI selection fix #2684)
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-18 19:02:32 +02:00
Martin Vogel a24e09105c Merge origin/main into fix/py-dotted-base-qn (pick up the Smart CI selection fix #2684)
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-18 19:02:19 +02:00
Martin Vogel c23f16262b Merge pull request #2684 from DeusData/fix/runner-conditional-suites
fix(test-impact): let a build drop the suites only some runners register
2026-10-18 19:01:22 +02:00
Martin Vogel 7c5112953f test(pipeline): DATA_FLOWS reach every caller and infra handler of a route
A route with 70 callers must link all 70 to its handler, a route whose
INFRA_MAPS endpoint has 40 handlers must link its caller to all 40, and
the flow set must not change when the callers' HTTP_CALLS edges are
inserted in reverse order. Before the fix the first 64 callers and the
first 32 infra handlers in insertion order were kept, so all three fail.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-18 18:31:05 +02:00
Martin Vogel 2a57f1dc08 style: clang-format the DATA_FLOWS walk
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-18 18:31:05 +02:00
Martin Vogel f04df4a887 fix(pipeline): DATA_FLOWS link every caller and handler of a route
create_data_flows copied a route's callers (HTTP_CALLS + ASYNC_CALLS)
into a 64-slot buffer and its INFRA_MAPS handlers into a 32-slot one,
each keeping the first entries in edge-insertion order. In a parallel
index that order is worker interleaving, so which DATA_FLOWS existed
changed between two indexes of the same tree with the same binary
(thingsboard, 2026-10-17), and every caller past the 64th lost its flow
outright.

The pass now walks the graph buffer's own edge arrays and creates the
full caller x handler product per route. A (caller, handler) pair that
is reached twice keeps cbm_gbuf_insert_edge's order-free property
winner, so the flow set and its properties no longer depend on the
order edges were inserted in.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-18 18:28:11 +02:00
Martin Vogel 5992ea61a3 fix(pipeline): size definition properties for every field
A definition's properties buffer was sized for its docstring, variants and
variant_key only; every other field had to share the remaining 2 KB. Fields
that do not fit are dropped whole, so a long signature, decorator list or
parameter list silently squeezed out the MinHash fingerprint (fp), the
structural profile (sp) and the body tokens (bt) appended behind them. Those
definitions got no SIMILAR_TO edge and fewer SEMANTICALLY_RELATED edges, and
any newly added property (route_path, route_method) pushed more of them out.

pd_props_buf and pp_props_buf (twins, kept in lockstep) now count the fixed
metrics prefix plus the whole serialized form of every field
build_def_props appends, including the reserve the fp/sp/bt room checks
use. Definitions that fit keep the stack buffer.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-18 17:14:04 +02:00
Martin Vogel fe19efb8b5 test(test-impact): make the conditional-path fixture a valid coverage map
A coverage map must cover every function it lists, and five of the seven
variants covered only function 1, so the map failed to parse and the test
stopped at its setup ("map is NULL") with the fix applied or not. The
changed function (0) is now covered by the setup row of a suite no runner
registers, so alpha's own rows stay quiet and each variant still reaches
alpha by exactly one path. RED with the selection fix reverted (alpha
lacks CONDITIONAL), green with it.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-18 17:10:45 +02:00
Martin Vogel 7ec13add98 fix(test-impact): mark conditionally registered runner suites CONDITIONAL on every selection path
A runner suite registered only under a preprocessor condition (the four
coverage_* suites under `#if defined(CBM_TEST_COVERAGE) && !defined(_WIN32)`
in tests/test_main.c) is absent from builds without that condition. The
selection marked such a suite CONDITIONAL only on the per-registration path,
and only when a registration of its own was conditional and contributed a
reason. Every suite-level path (setup unknown or hit, suite-body, changed and
rule triggers, inventory, rejected artifact) made the suite whole without it,
and the runner registration's own condition was never recorded.

PR CI on the x86 Linux gcc leg then failed with "the test selection names
suites this build does not register: coverage_setup_success
coverage_setup_uncertain" (#2679, #2680): the suite-body trigger and an
incomplete setup row selected both suites whole without CONDITIONAL, so
selection.py left them out of optional-suites.txt.

The model now records whether a runner registration sits under a condition
(conditional only when every site of the suite is), and the selection marks
any selected conditionally registered suite whole with CONDITIONAL after every
whole-suite path. Unconditionally registered suites keep the strict contract.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-18 16:52:36 +02:00
Martin Vogel fed5cbf9cc fix(extract): test the empty Rust impl name before the cut
cppcheck 2.20 reports the post-cut `if (!name[0])` as always false
(knownConditionTrueFalse): it cannot see that writing through `lt`
may empty `name`. The name is non-empty here, so the cut empties it
only when `<` is the first character. Test exactly that before the
cut. The behaviour is unchanged.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-18 16:49:36 +02:00
Martin Vogel 67ff40d5cb fix(kotlin): resolve calls through typed class properties (#1555)
The cross-file Kotlin walk resolved a call on a typed method parameter by
type, but a call on a class property - a primary-constructor val, a body
property or `this.prop` - fell to the name guess. The cross registry held
each own-file class from its extraction defs, whose properties carry no type
(constructor vals none at all), and the walk's typed AST registration of the
same class was added behind that entry under the same QN, so every lookup
returned the field-less one. Constructor injection is the common Kotlin
dependency shape, so `repository.inserir(...)` on an injected port bound to
whichever same-named method the guess found, including an implementation of
another port (#1555).

The walk now registers the file's own declarations into an overlay chained
to the cross registry and checked first, indexed once the top-level
declarations are collected. The sole-definer fallback walks the whole chain
so its candidate set is unchanged.

Typing more receivers exposed resolution gaps that the untyped receivers had
hidden; each would otherwise turn a typed call into a wrong name guess:
- a member of the receiver class wins over a same-named extension of the
  same file (Kotlin resolution order; the overlay holds both);
- `x.hashCode()` / `toString()` / `equals()` on a receiver typed by another
  file: the per-file pass emits the kotlin.Any placeholder, the cross pass
  the declared override, and two targets at one occurrence are ambiguous to
  the call join. The per-file placeholder is dropped when the cross pass
  resolves the same occurrence;
- the same members on a stdlib or JDK type (`count.toString()` on an Int)
  resolve to the kotlin.Any builtin instead of a seeded stdlib member that
  has no graph node;
- a property READ on a typed receiver whose QN the graph spells differently
  (a nested class's property) never becomes a CALL_REFERENCE to a
  same-named function: the reference join falls back by name to
  non-callable targets only, which binds the property's own node;
- a typed value without an `invoke` operator (`private val handshake:
  Handshake?`) no longer shadows the same-named member function;
- inside a lambda whose typed receiver becomes `this` (`repo.let { .. }`),
  and inside a member extension, the enclosing class's members stay
  callable bare (the implicit-receiver chain); a call naming a class stays
  a constructor call.

Fixes #1555

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-18 16:42:30 +02:00
Martin Vogel 090580b3c9 refactor(python): scope the nested-def counter to the Python branch
cppcheck 2.20 variableScope on defs_before; same behaviour.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-18 16:36:35 +02:00
Martin Vogel f6b505914a Merge origin/main into fix/issue-1548-python-nested-defs-v2
Conflict in pass_definitions.c: process_def gained main's link_parents and this branch's module_prefix_len; both kept.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-18 16:34:04 +02:00
Martin Vogel 8469fed960 test(rust-lsp): tag the shared-registry fixtures as Rust defs
The shared Tier-2 build now registers Rust definitions only. Two
fixtures that feed cbm_rust_build_cross_registry left CBMLSPDef.lang
zero, which the pipeline never does (pass_lsp_cross.c tags every def
with its file's language). The other shared-registry fixtures in this
file already set CBM_LANG_RUST. Set it on these two as well.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-18 06:55:57 +02:00
Martin Vogel eec3418fef fix(python): a test's nested helper keeps TESTS for its calls (#1548)
Since #1548 a def declared inside a Python test function is extracted, so its calls start at the helper instead of the test FILE node, whose name passed the test-name check. 4,641 of 6,315 TESTS edges lost on the corpus were this shape. TESTS for a call from such a helper is now attributed to the nearest enclosing test function of the same file; a helper nested in a non-test function stays without TESTS, like a module-level helper.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-18 06:18:09 +02:00
Martin Vogel f2534d1e3d Merge origin/main into fix/py-dotted-base-qn
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-18 05:00:49 +02:00
Martin Vogel d4beec3c58 test(python): local classes are extracted defs now (#1548)
pylsp_nested_and_local_classbody_calls_join_extracted_caller asserted that a class declared in a function body is NOT a def, which is the behaviour #1548 fixes. Their class-body calls still join the enclosing function; that part is unchanged.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-18 04:48:27 +02:00
Martin Vogel 5bfa7b53bf test(python): note that H1 may move the branch-def case to an lsp strategy
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-18 04:11:16 +02:00
Martin Vogel 0bab94ce1c test(python): a def under a branch is bound by the lexical step, not the LSP
At a compound statement's join the Python LSP invalidates what the branch bound, so the local def under if True: is resolved by the registry's lexical_scope step; pin that producer instead of an lsp_ strategy. Found at the branch's first build.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-18 04:08:24 +02:00
Martin Vogel 9b5139d73d fix(python): bound the function-local walks at the symbol's own file
A package __init__.py can define a function whose QN equals a submodule's
(poetry: layouts/__init__.py `def layout` is the QN prefix of every symbol in
layouts/layout.py). The prefix walks that recognise a def declared inside a
function body accepted that other file's function as the enclosing def, so
class Layout and its methods read as locals of layout(): they left the
short-name index and the import symbol fallback, and the answer depended on
which file registered first (discovery order on a full index, node-id order on
an incremental one).

Every walk now stops at the symbol's own module:
- registration (pass_definitions.c, pass_parallel.c, and the incremental seed)
  passes the defining file's symbol prefix length,
  cbm_pipeline_fqn_symbol_prefix_len, computed without allocating, to
  cbm_registry_add_lang_in_module; cbm_registry_add_lang keeps its signature and
  does no local detection;
- cbm_registry_resolve_lexical stops at the caller's module QN;
- the import fallback's local test only counts an enclosing def in the same file.

Also moves the #1548 registry tests out of the test they had been inserted into.

Tests: registry_python_local_bound_at_own_file_issue1548 (both registration
orders), fqn_symbol_prefix_len_matches_fqn_compute,
pipeline_python_init_function_shares_submodule_qn{,_parallel}_issue1548 (src/
layout: the import and calls onto Layout survive, then again after an
incremental re-index).

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-18 03:41:31 +02:00
Martin Vogel f75af00a8b fix(rust-lsp): a generic impl's self type drops its type arguments
The extractor fix above names a generic impl's methods `Gen.in_generic`
on both the def and the call-scope side. The Rust LSP still built its
self type from the impl's raw text, `Gen<T>`. That broke three things:

- its caller QN (`Gen<T>.in_generic`) no longer joined the pipeline's
  call scope, so every LSP row inside a generic or lifetime impl was
  dropped for a registry guess. On 9 Rust repos that turned 1,927
  lsp_* edges into suffix_match/unique_name;
- `self.` and `Self::` dispatch never found the impl's own methods;
  `Self::assoc_g()` had no edge at all, on main as well;
- the return-type harvest never matched those methods, so chained
  calls on them stayed untyped.

Strip the generic arguments from the impl's self type in both places.
Text that starts with `<` (a qualified path) is left alone.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-18 03:03:38 +02:00
Martin Vogel 6b1cd8aa0d style(python): clang-format the nested-def binding and lexical step
Whitespace only; the token stream is unchanged.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-18 03:02:50 +02:00
Martin Vogel ec4f0eaae6 fix(rust-lsp): a type-qualified call never takes the free-function fallback
`Opts::write(true)` is an associated function. When the Rust LSP cannot
place it, the registry can (import_map or qualified_suffix onto
fsx.Opts.write). The two free-function short-name fallbacks (the
cross-crate member lookup and the project-wide lsp_short_name_unique)
ran first, though. They bound the call to the project's only free
`write`, and that LSP row pre-empted the right answer.

While the Rust registry also held other languages' definitions, a
same-named JS or Perl function often made the free name ambiguous and
hid this. With Rust-only registries the hijack replaced 516
qualified_suffix and 84 import_map edges on 7 Rust repos.

A call whose qualifying segment names a type now skips both fallbacks.
That covers an ASCII-uppercase initial, `Self`, and a qualified
`<T as Tr>::f` path.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-18 02:59:00 +02:00
Martin Vogel cc79e1d63f style(pipeline): clang-format the dotted-base resolution
Whitespace only; the token stream is unchanged.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-18 02:51:25 +02:00
Martin Vogel 2fbbecc550 Merge remote-tracking branch 'origin/main' into fix/rust-generic-impl-call-scope
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-18 02:49:30 +02:00
Martin Vogel 42769c2d6d Merge remote-tracking branch 'origin/main' into fix/rust-registry-rust-defs-only
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-18 02:49:30 +02:00
Martin Vogel 354ccecbe6 Merge pull request #2431 from kaNEGY/test/ts-lsp-allocation-failures
test(ts-lsp): fail when fixture allocation fails
2026-10-18 02:32:00 +02:00
Martin Vogel 0533beb7c6 Merge pull request #2678 from DeusData/fix/seq-link-methods-after-registration
fix(pipeline): link methods to their type after every file is registered
2026-10-18 02:31:56 +02:00
Martin Vogel bb650ca45a Merge branch 'main' into test/ts-lsp-allocation-failures 2026-10-18 00:17:58 +02:00
Martin Vogel d40f8e6be1 test(pipeline): generic Rust impl methods source their own calls (both resolvers)
Sequential and parallel pipeline twin of extract_rust_generic_impl_call_scope_matches_def: the calls in a type-generic and a lifetime-generic impl method are sourced from the method, not from the File node.

Refs #763

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-18 00:06:42 +02:00
Martin Vogel 09c4885e11 fix(extract): a Rust generic impl's call scope drops its type arguments
The def side names a Rust impl method after the implementing type with
its generic arguments stripped (`impl<T> Gen<T>` -> `Gen`, extract_rust_impl).
The call-scope QN in extract_unified.c used the impl's type text verbatim,
`Gen<T>` / `Life<'a>`. So every method of a generic or lifetime-parameterised
impl had a scope QN that no node carries, and every call in its body was
sourced from the File node instead of the method. Base census, 9f85f104 par
(lane L7's #763 measurement): 87,855 Rust CALLS start at a File/Module node,
84,331 of them (96%) on a line inside a function span. Go is 0%.

Strip the generic arguments on the call side exactly as the def side does.
Plain impls, trait impls, free functions and inline modules were already
consistent (checked on a fixture with the base binary).

Test: extract_rust_generic_impl_call_scope_matches_def (plain, type-generic
and lifetime-generic impls; def QN == call scope).

Refs #763

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-18 00:05:47 +02:00
Martin Vogel 9dba7fe1af Merge pull request #2676 from DeusData/feat/smart-ci-live
ci(test-impact): make PR test selection narrow for real
2026-10-17 23:56:49 +02:00
Martin Vogel bfe53f344b style: clang-format the new lines
Formatting only, with the repo's clang-format (Homebrew LLVM).

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
2026-10-17 23:34:24 +02:00
Martin Vogel 2ac9c33e18 fix(rust-lsp): a Rust call never binds another language's definition
Both Rust registries were built from the pipeline's mixed-language def
set. The shared Tier-2 build (cbm_rust_build_cross_registry) and the
per-file conversion (pxc_lspdefs_to_rust) copied every def, so a Perl
`sub new` or any C function became a Rust free function. The
lsp_short_name_unique fallback then bound `Store::new()` to the Perl
constructor at confidence 0.90 on both venues: its "crate scope" check
compares only the first module-QN segment, which is the project name.
(Found by the generic-engine lane on a 12-language fixture.)

Register Rust defs only, at both conversion points, exactly as the Go
builder filters to Go. Rust impl-relation records already carry
CBM_LANG_RUST. As a side effect, the shared Rust build no longer
registers the C defs of a mixed C+Rust tree.

Tests: pipeline_rust_call_never_binds_another_language (sequential and
parallel): no CALLS from the Rust caller into the Perl file, and the
Rust associated function still binds.

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