Find a sole concrete in-project implementer of interface `iface_qn` that * declares method `mname`. Returns the implementer's QN when exactly ONE * exists (else NULL), and sets *out_count to the number found (capped at 2, * so 2 means "two or more"). Walks the registered-type parent chain to * confirm true subtyping. Mirrors the inline detection that used to live in * resolve_method_call so b
| 1821 | * resolve_method_call so both the f-found and f-absent interface paths share |
| 1822 | * identical semantics. */ |
| 1823 | static const char *java_find_sole_impl(JavaLSPContext *ctx, const char *iface_qn, const char *mname, |
| 1824 | int *out_count) { |
| 1825 | const char *first = NULL; /* first distinct impl QN seen */ |
| 1826 | int distinct = 0; /* distinct impl classes (capped at 2) */ |
| 1827 | const char *iface_dot = strrchr(iface_qn, '.'); |
| 1828 | const char *iface_bare = iface_dot ? iface_dot + 1 : iface_qn; |
| 1829 | for (int ti = 0; ti < ctx->registry->type_count && distinct < 2; ti++) { |
| 1830 | const CBMRegisteredType *cand = &ctx->registry->types[ti]; |
| 1831 | if (cand->is_interface || !cand->qualified_name || cand->alias_of) |
| 1832 | continue; |
| 1833 | /* Does cand declare `mname`? The method-name array is often empty for |
| 1834 | * fixture classes; the method REGISTRY is the authoritative source the |
| 1835 | * dispatch path already uses, so consult it first and fall back to the |
| 1836 | * name array. */ |
| 1837 | bool has = cbm_registry_lookup_method(ctx->registry, cand->qualified_name, mname) != NULL; |
| 1838 | if (!has && cand->method_names) { |
| 1839 | for (int mi = 0; cand->method_names[mi]; mi++) { |
| 1840 | if (strcmp(cand->method_names[mi], mname) == 0) { |
| 1841 | has = true; |
| 1842 | break; |
| 1843 | } |
| 1844 | } |
| 1845 | } |
| 1846 | if (!has) |
| 1847 | continue; |
| 1848 | /* Subtype check: walk cand's supertype chain, matching iface by FULL |
| 1849 | * QN or BARE name. The registry holds duplicate type entries whose |
| 1850 | * `embedded_types` list a supertype sometimes by short name ("Shape") |
| 1851 | * and sometimes by full QN ("proj.Shape"); a full-QN-only comparison |
| 1852 | * silently misses the short-name form, so compare both. */ |
| 1853 | const char *cur = cand->qualified_name; |
| 1854 | bool subtype = false; |
| 1855 | for (int hops = 0; hops < JAVA_LSP_MAX_INHERIT_HOPS && cur && !subtype; hops++) { |
| 1856 | const CBMRegisteredType *ct = cbm_registry_lookup_type(ctx->registry, cur); |
| 1857 | if (!ct || !ct->embedded_types) |
| 1858 | break; |
| 1859 | const char *next = NULL; |
| 1860 | for (int pi = 0; ct->embedded_types[pi]; pi++) { |
| 1861 | const char *e = ct->embedded_types[pi]; |
| 1862 | const char *edot = strrchr(e, '.'); |
| 1863 | const char *ebare = edot ? edot + 1 : e; |
| 1864 | if (strcmp(e, iface_qn) == 0 || strcmp(ebare, iface_bare) == 0) { |
| 1865 | subtype = true; |
| 1866 | break; |
| 1867 | } |
| 1868 | if (!next) |
| 1869 | next = e; /* first supertype → continue the walk upward */ |
| 1870 | } |
| 1871 | cur = next; |
| 1872 | } |
| 1873 | if (!subtype) |
| 1874 | continue; |
| 1875 | /* Count DISTINCT impl classes: the registry duplicates entries per |
| 1876 | * class, so dedup by QN — two entries of one class must not read as |
| 1877 | * two implementers. */ |
| 1878 | if (!first) { |
| 1879 | first = cand->qualified_name; |
| 1880 | distinct = 1; |
no test coverage detected