Resolve a Rust *path expression* (e.g. `Foo::bar` or `crate::x::y`) * into a canonical QN. The resolver cascades through these rules, * matching what `rust-analyzer`'s name resolver does at the path level: * * 1. `Self::X` → ` .X` * 2. `crate::a::b` → ` .a.b` * 3. `super::a` → strip last segment of `module_qn` and prepend * 4. Single-segment + matches
| 615 | * The returned string is arena-owned; in case (6) we return the input |
| 616 | * with `::` already converted to `.`. */ |
| 617 | static const char *rust_resolve_path_expr(RustLSPContext *ctx, const char *path) { |
| 618 | if (!ctx || !path || !path[0]) { |
| 619 | return path; |
| 620 | } |
| 621 | |
| 622 | /* Self:: handling — we treat the receiver type's QN as the head. */ |
| 623 | if (strncmp(path, "Self::", 6) == 0 && ctx->self_type_qn) { |
| 624 | return cbm_arena_sprintf(ctx->arena, "%s.%s", ctx->self_type_qn, |
| 625 | convert_path_to_qn(ctx->arena, path + 6)); |
| 626 | } |
| 627 | if (strcmp(path, "Self") == 0 && ctx->self_type_qn) { |
| 628 | return ctx->self_type_qn; |
| 629 | } |
| 630 | |
| 631 | /* crate:: → <root>. We approximate the crate root as the first dotted |
| 632 | * segment of `module_qn` after the project prefix. The pipeline |
| 633 | * forms `module_qn` as `<project>.<crate>.<rel-path-segments>`, so |
| 634 | * the first two segments are project + crate root. */ |
| 635 | if (strncmp(path, "crate::", 7) == 0 && ctx->module_qn) { |
| 636 | const char *p = ctx->module_qn; |
| 637 | int dots = 0; |
| 638 | const char *second_dot = NULL; |
| 639 | for (; *p; p++) { |
| 640 | if (*p == '.') { |
| 641 | if (++dots == 2) { |
| 642 | second_dot = p; |
| 643 | break; |
| 644 | } |
| 645 | } |
| 646 | } |
| 647 | size_t crate_len = |
| 648 | second_dot ? (size_t)(second_dot - ctx->module_qn) : strlen(ctx->module_qn); |
| 649 | char *crate_buf = cbm_arena_strndup(ctx->arena, ctx->module_qn, crate_len); |
| 650 | return cbm_arena_sprintf(ctx->arena, "%s.%s", crate_buf, |
| 651 | convert_path_to_qn(ctx->arena, path + 7)); |
| 652 | } |
| 653 | |
| 654 | /* super:: → drop last segment of module_qn. */ |
| 655 | if (strncmp(path, "super::", 7) == 0 && ctx->module_qn) { |
| 656 | const char *dot = strrchr(ctx->module_qn, '.'); |
| 657 | if (dot) { |
| 658 | char *parent = |
| 659 | cbm_arena_strndup(ctx->arena, ctx->module_qn, (size_t)(dot - ctx->module_qn)); |
| 660 | return cbm_arena_sprintf(ctx->arena, "%s.%s", parent, |
| 661 | convert_path_to_qn(ctx->arena, path + 7)); |
| 662 | } |
| 663 | } |
| 664 | |
| 665 | /* Find first "::" — split into head + tail. */ |
| 666 | const char *sep = strstr(path, "::"); |
| 667 | if (!sep) { |
| 668 | const char *full = rust_resolve_use(ctx, path); |
| 669 | if (full) { |
| 670 | return convert_path_to_qn(ctx->arena, full); |
| 671 | } |
| 672 | /* Prelude name (e.g. `String`, `Vec`)? */ |
| 673 | const char *prelude = rust_lookup_prelude(path); |
| 674 | if (prelude) { |
no test coverage detected