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
| 1886 | * resolve_method_call so both the f-found and f-absent interface paths share |
| 1887 | * identical semantics. */ |
| 1888 | static const char *java_find_sole_impl(JavaLSPContext *ctx, const char *iface_qn, const char *mname, |
| 1889 | int *out_count) { |
| 1890 | const char *first = NULL; /* first distinct impl QN seen */ |
| 1891 | int distinct = 0; /* distinct impl classes (capped at 2) */ |
| 1892 | const char *iface_dot = strrchr(iface_qn, '.'); |
| 1893 | const char *iface_bare = iface_dot ? iface_dot + 1 : iface_qn; |
| 1894 | for (int ti = 0; ti < ctx->registry->type_count && distinct < 2; ti++) { |
| 1895 | const CBMRegisteredType *cand = &ctx->registry->types[ti]; |
| 1896 | if (cand->is_interface || !cand->qualified_name || cand->alias_of) |
| 1897 | continue; |
| 1898 | /* Does cand declare `mname`? The method-name array is often empty for |
| 1899 | * fixture classes; the method REGISTRY is the authoritative source the |
| 1900 | * dispatch path already uses, so consult it first and fall back to the |
| 1901 | * name array. */ |
| 1902 | bool has = cbm_registry_lookup_method(ctx->registry, cand->qualified_name, mname) != NULL; |
| 1903 | if (!has && cand->method_names) { |
| 1904 | for (int mi = 0; cand->method_names[mi]; mi++) { |
| 1905 | if (strcmp(cand->method_names[mi], mname) == 0) { |
| 1906 | has = true; |
| 1907 | break; |
| 1908 | } |
| 1909 | } |
| 1910 | } |
| 1911 | if (!has) |
| 1912 | continue; |
| 1913 | /* Subtype check: walk cand's supertype chain, matching iface by FULL |
| 1914 | * QN or BARE name. The registry holds duplicate type entries whose |
| 1915 | * `embedded_types` list a supertype sometimes by short name ("Shape") |
| 1916 | * and sometimes by full QN ("proj.Shape"); a full-QN-only comparison |
| 1917 | * silently misses the short-name form, so compare both. */ |
| 1918 | const char *cur = cand->qualified_name; |
| 1919 | bool subtype = false; |
| 1920 | for (int hops = 0; hops < JAVA_LSP_MAX_INHERIT_HOPS && cur && !subtype; hops++) { |
| 1921 | const CBMRegisteredType *ct = cbm_registry_lookup_type(ctx->registry, cur); |
| 1922 | if (!ct || !ct->embedded_types) |
| 1923 | break; |
| 1924 | const char *next = NULL; |
| 1925 | for (int pi = 0; ct->embedded_types[pi]; pi++) { |
| 1926 | const char *e = ct->embedded_types[pi]; |
| 1927 | const char *edot = strrchr(e, '.'); |
| 1928 | const char *ebare = edot ? edot + 1 : e; |
| 1929 | if (strcmp(e, iface_qn) == 0 || strcmp(ebare, iface_bare) == 0) { |
| 1930 | subtype = true; |
| 1931 | break; |
| 1932 | } |
| 1933 | if (!next) |
| 1934 | next = e; /* first supertype → continue the walk upward */ |
| 1935 | } |
| 1936 | cur = next; |
| 1937 | } |
| 1938 | if (!subtype) |
| 1939 | continue; |
| 1940 | /* Count DISTINCT impl classes: the registry duplicates entries per |
| 1941 | * class, so dedup by QN — two entries of one class must not read as |
| 1942 | * two implementers. */ |
| 1943 | if (!first) { |
| 1944 | first = cand->qualified_name; |
| 1945 | distinct = 1; |
no test coverage detected