diff --git a/internal/cbm/resolve/rust_flow.c b/internal/cbm/resolve/rust_flow.c index 10f9bbaa0..a87a09cf0 100644 --- a/internal/cbm/resolve/rust_flow.c +++ b/internal/cbm/resolve/rust_flow.c @@ -657,9 +657,8 @@ static rs_env_t rs_env_here(rs_walk_t *w) { static bool rs_inst_binds_trait(rs_walk_t *w, rs_inst_t *inst, const rs_item_t *trait) { const rs_mod_t *syn = inst->syn; - for (int i = 0; i < syn->nitems; i++) - if (syn->items[i] == trait) - return true; + if (trait->owner == syn) + return true; for (const rs_use_t *use = syn->uses; use; use = use->next) { if (!use->nseg || strcmp(use->segs[use->nseg - 1], trait->name) != 0) continue; @@ -2766,8 +2765,7 @@ static const rs_type_t *rs_element_of(rs_walk_t *w, const rs_type_t *iterable) { } else if (type->kind == RT_ITEM) { /* A project iterator names its element in `type Item = X`. */ for (const rs_impl_t *impl = type->item->impls; impl; impl = impl->next_self) { - if (!impl->trait_syn || impl->trait_syn->kind != RS_TS_PATH || !impl->trait_syn->nseg || - strcmp(impl->trait_syn->segs[impl->trait_syn->nseg - 1], "Iterator") != 0) + if (!rs_iterator_trait_name(rs_tsyn_leaf(impl->trait_syn))) continue; for (int i = 0; i < impl->nassoc; i++) { if (strcmp(impl->assoc_names[i], "Item") != 0) diff --git a/internal/cbm/resolve/rust_names.c b/internal/cbm/resolve/rust_names.c index 81507ad1f..475d4d7d7 100644 --- a/internal/cbm/resolve/rust_names.c +++ b/internal/cbm/resolve/rust_names.c @@ -1561,10 +1561,15 @@ static void rs_package_crates(cbm_rust_project_t *project, rs_package_t *package rs_join(arena, package->dir, package->build_path ? package->build_path : "build.rs"); if (build_rel) (void)rs_crate_add(project, package, "build_script_build", build_rel, RS_CRATE_AUX); +} + +/* src/bin, tests, benches and examples of whichever package owns each file. */ +static void rs_auto_crates(cbm_rust_project_t *project) { + CBMArena *arena = &project->arena; for (int i = 0; i < project->nfiles; i++) { const char *rel = project->files[i]->rel, *rest = NULL; - if (rs_owning_package(project, rel) != package || !rs_under(rel, package->dir, &rest) || - rs_target_listed(package, rel, arena)) + rs_package_t *package = (rs_package_t *)rs_owning_package(project, rel); + if (!package || !package->name || !rs_under(rel, package->dir, &rest)) continue; const char *name = NULL; rs_crate_kind_t kind = RS_CRATE_AUX; @@ -1574,7 +1579,8 @@ static void rs_package_crates(cbm_rust_project_t *project, rs_package_t *package !(package->auto_benches && (name = rs_auto_target(arena, rest, "benches/"))) && !(package->auto_examples && (name = rs_auto_target(arena, rest, "examples/")))) continue; - (void)rs_crate_add(project, package, rs_folded(arena, name), rel, kind); + if (!rs_target_listed(package, rel, arena)) + (void)rs_crate_add(project, package, rs_folded(arena, name), rel, kind); } } @@ -1686,6 +1692,7 @@ bool cbm_rust_project_seal(cbm_rust_project_t *project) { if (package->has_package && package->name) rs_package_crates(project, package); } + rs_auto_crates(project); if (!any_manifest) { /* Without a manifest an entry file is an explicit rustc root. */ for (int i = 0; i < project->nfiles; i++) { diff --git a/tests/test_extraction_imports.c b/tests/test_extraction_imports.c index 8099512ab..f98fccd5c 100644 --- a/tests/test_extraction_imports.c +++ b/tests/test_extraction_imports.c @@ -503,15 +503,9 @@ TEST(javascript_import_table) { } /* ═══════════════════════════════════════════════════════════════════════════ - * Rust — parse_rust_imports (text-based: strips "use "/";" from whole node). - * - * EXPECTED: basic and namespaced forms GREEN; grouped/braced forms may be RED - * because the whole `use std::io::{self, Write};` text becomes the module_path, - * so has_import("std::io") would still match as a substring — likely GREEN even - * for grouped. The alias form `use X as Y;` leaves "std::collections::HashMap as H" - * as module_path — has_import("std::collections::HashMap") would still match. - * So most forms should be GREEN at extraction. Graph-level zero edges are - * downstream. + * Rust — parse_rust_imports: one import per use-tree leaf, each with its whole + * path. `use std::io::{self, Write};` is "std::io" and "std::io::Write", and an + * alias changes the local name, never the path. * ═══════════════════════════════════════════════════════════════════════════ */ static const import_case_t rust_cases[] = {